aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornobody <nobody@localhost>2004-10-13 09:42:10 +0000
committernobody <nobody@localhost>2004-10-13 09:42:10 +0000
commit8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373 (patch)
tree1826706cd4fd009fcd14f4f8021005ec8ec0fa59
downloadbusybox-w32-8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373.tar.gz
busybox-w32-8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373.tar.bz2
busybox-w32-8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373.zip
This commit was manufactured by cvs2svn to create tag 'busybox_1_00'.
-rw-r--r--busybox/.cvsignore7
-rw-r--r--busybox/.indent.pro33
-rw-r--r--busybox/AUTHORS133
-rw-r--r--busybox/Changelog1381
-rw-r--r--busybox/INSTALL20
-rw-r--r--busybox/LICENSE340
-rw-r--r--busybox/Makefile315
-rw-r--r--busybox/README127
-rw-r--r--busybox/Rules.mak196
-rw-r--r--busybox/TODO11
-rw-r--r--busybox/applets/Makefile32
-rw-r--r--busybox/applets/Makefile.in37
-rw-r--r--busybox/applets/applets.c512
-rw-r--r--busybox/applets/busybox.c193
-rwxr-xr-xbusybox/applets/busybox.mkll24
-rwxr-xr-xbusybox/applets/install.sh52
-rw-r--r--busybox/archival/Config.in258
-rw-r--r--busybox/archival/Makefile32
-rw-r--r--busybox/archival/Makefile.in48
-rw-r--r--busybox/archival/ar.c110
-rw-r--r--busybox/archival/bunzip2.c91
-rw-r--r--busybox/archival/cpio.c99
-rw-r--r--busybox/archival/dpkg.c1833
-rw-r--r--busybox/archival/dpkg_deb.c112
-rw-r--r--busybox/archival/gunzip.c198
-rw-r--r--busybox/archival/gzip.c2548
-rw-r--r--busybox/archival/libunarchive/Makefile32
-rw-r--r--busybox/archival/libunarchive/Makefile.in84
-rw-r--r--busybox/archival/libunarchive/archive_xread_all.c32
-rw-r--r--busybox/archival/libunarchive/archive_xread_all_eof.c32
-rw-r--r--busybox/archival/libunarchive/check_header_gzip.c57
-rw-r--r--busybox/archival/libunarchive/data_align.c33
-rw-r--r--busybox/archival/libunarchive/data_extract_all.c124
-rw-r--r--busybox/archival/libunarchive/data_extract_to_buffer.c28
-rw-r--r--busybox/archival/libunarchive/data_extract_to_stdout.c23
-rw-r--r--busybox/archival/libunarchive/data_skip.c27
-rw-r--r--busybox/archival/libunarchive/decompress_bunzip2.c611
-rw-r--r--busybox/archival/libunarchive/decompress_uncompress.c293
-rw-r--r--busybox/archival/libunarchive/decompress_unzip.c982
-rw-r--r--busybox/archival/libunarchive/filter_accept_all.c32
-rw-r--r--busybox/archival/libunarchive/filter_accept_list.c34
-rw-r--r--busybox/archival/libunarchive/filter_accept_list_reassign.c55
-rw-r--r--busybox/archival/libunarchive/filter_accept_reject_list.c45
-rw-r--r--busybox/archival/libunarchive/find_list_entry.c30
-rw-r--r--busybox/archival/libunarchive/get_header_ar.c126
-rw-r--r--busybox/archival/libunarchive/get_header_cpio.c161
-rw-r--r--busybox/archival/libunarchive/get_header_tar.c215
-rw-r--r--busybox/archival/libunarchive/get_header_tar_bz2.c38
-rw-r--r--busybox/archival/libunarchive/get_header_tar_gz.c42
-rw-r--r--busybox/archival/libunarchive/header_list.c7
-rw-r--r--busybox/archival/libunarchive/header_skip.c7
-rw-r--r--busybox/archival/libunarchive/header_verbose_list.c29
-rw-r--r--busybox/archival/libunarchive/init_handle.c36
-rw-r--r--busybox/archival/libunarchive/open_transformer.c51
-rw-r--r--busybox/archival/libunarchive/seek_by_char.c32
-rw-r--r--busybox/archival/libunarchive/seek_by_jump.c35
-rw-r--r--busybox/archival/libunarchive/unpack_ar_archive.c34
-rw-r--r--busybox/archival/rpm.c349
-rw-r--r--busybox/archival/rpm2cpio.c106
-rw-r--r--busybox/archival/tar.c891
-rw-r--r--busybox/archival/uncompress.c115
-rw-r--r--busybox/archival/unzip.c247
-rw-r--r--busybox/console-tools/Config.in68
-rw-r--r--busybox/console-tools/Makefile32
-rw-r--r--busybox/console-tools/Makefile.in44
-rw-r--r--busybox/console-tools/chvt.c61
-rw-r--r--busybox/console-tools/clear.c34
-rw-r--r--busybox/console-tools/deallocvt.c56
-rw-r--r--busybox/console-tools/dumpkmap.c91
-rw-r--r--busybox/console-tools/loadfont.c207
-rw-r--r--busybox/console-tools/loadkmap.c82
-rw-r--r--busybox/console-tools/openvt.c84
-rw-r--r--busybox/console-tools/reset.c45
-rw-r--r--busybox/console-tools/setkeycodes.c72
-rw-r--r--busybox/coreutils/Config.in613
-rw-r--r--busybox/coreutils/Makefile32
-rw-r--r--busybox/coreutils/Makefile.in98
-rw-r--r--busybox/coreutils/basename.c62
-rw-r--r--busybox/coreutils/cal.c379
-rw-r--r--busybox/coreutils/cat.c67
-rw-r--r--busybox/coreutils/chgrp.c81
-rw-r--r--busybox/coreutils/chmod.c112
-rw-r--r--busybox/coreutils/chown.c105
-rw-r--r--busybox/coreutils/chroot.c53
-rw-r--r--busybox/coreutils/cmp.c152
-rw-r--r--busybox/coreutils/cp.c119
-rw-r--r--busybox/coreutils/cut.c344
-rw-r--r--busybox/coreutils/date.c292
-rw-r--r--busybox/coreutils/dd.c203
-rw-r--r--busybox/coreutils/df.c170
-rw-r--r--busybox/coreutils/dirname.c39
-rw-r--r--busybox/coreutils/dos2unix.c198
-rw-r--r--busybox/coreutils/du.c269
-rw-r--r--busybox/coreutils/echo.c165
-rw-r--r--busybox/coreutils/env.c144
-rw-r--r--busybox/coreutils/expr.c528
-rw-r--r--busybox/coreutils/false.c32
-rw-r--r--busybox/coreutils/fold.c194
-rw-r--r--busybox/coreutils/head.c138
-rw-r--r--busybox/coreutils/hostid.c38
-rw-r--r--busybox/coreutils/id.c135
-rw-r--r--busybox/coreutils/install.c151
-rw-r--r--busybox/coreutils/length.c19
-rw-r--r--busybox/coreutils/libcoreutils/Makefile33
-rw-r--r--busybox/coreutils/libcoreutils/Makefile.in37
-rw-r--r--busybox/coreutils/libcoreutils/coreutils.h12
-rw-r--r--busybox/coreutils/libcoreutils/cp_mv_stat.c45
-rw-r--r--busybox/coreutils/libcoreutils/getopt_mk_fifo_nod.c45
-rw-r--r--busybox/coreutils/libcoreutils/xgetoptfile_sort_uniq.c38
-rw-r--r--busybox/coreutils/ln.c102
-rw-r--r--busybox/coreutils/logname.c55
-rw-r--r--busybox/coreutils/ls.c1134
-rw-r--r--busybox/coreutils/md5_sha1_sum.c204
-rw-r--r--busybox/coreutils/mkdir.c75
-rw-r--r--busybox/coreutils/mkfifo.c51
-rw-r--r--busybox/coreutils/mknod.c63
-rw-r--r--busybox/coreutils/mv.c139
-rw-r--r--busybox/coreutils/od.c231
-rw-r--r--busybox/coreutils/printf.c316
-rw-r--r--busybox/coreutils/pwd.c37
-rw-r--r--busybox/coreutils/realpath.c54
-rw-r--r--busybox/coreutils/rm.c66
-rw-r--r--busybox/coreutils/rmdir.c73
-rw-r--r--busybox/coreutils/seq.c51
-rw-r--r--busybox/coreutils/sleep.c86
-rw-r--r--busybox/coreutils/sort.c100
-rw-r--r--busybox/coreutils/stty.c1314
-rw-r--r--busybox/coreutils/sync.c36
-rw-r--r--busybox/coreutils/tail.c330
-rw-r--r--busybox/coreutils/tee.c120
-rw-r--r--busybox/coreutils/test.c559
-rw-r--r--busybox/coreutils/touch.c76
-rw-r--r--busybox/coreutils/tr.c248
-rw-r--r--busybox/coreutils/true.c32
-rw-r--r--busybox/coreutils/tty.c58
-rw-r--r--busybox/coreutils/uname.c118
-rw-r--r--busybox/coreutils/uniq.c112
-rw-r--r--busybox/coreutils/usleep.c41
-rw-r--r--busybox/coreutils/uudecode.c201
-rw-r--r--busybox/coreutils/uuencode.c149
-rw-r--r--busybox/coreutils/watch.c110
-rw-r--r--busybox/coreutils/wc.c227
-rw-r--r--busybox/coreutils/who.c83
-rw-r--r--busybox/coreutils/whoami.c38
-rw-r--r--busybox/coreutils/yes.c56
-rw-r--r--busybox/debian/busybox-cvs-doc.docs1
-rw-r--r--busybox/debian/busybox-cvs-static.dirs2
-rw-r--r--busybox/debian/busybox-cvs-static.manpages1
-rw-r--r--busybox/debian/busybox-cvs-static.override1
-rw-r--r--busybox/debian/busybox-cvs.dirs1
-rw-r--r--busybox/debian/busybox-cvs.manpages1
-rw-r--r--busybox/debian/changelog479
-rw-r--r--busybox/debian/compat1
-rw-r--r--busybox/debian/config-deb379
-rw-r--r--busybox/debian/config-floppy-udeb-linux359
-rw-r--r--busybox/debian/config-static503
-rw-r--r--busybox/debian/config-udeb410
-rw-r--r--busybox/debian/config-udeb-linux419
-rw-r--r--busybox/debian/control88
-rw-r--r--busybox/debian/control-extract2
-rw-r--r--busybox/debian/copyright24
-rwxr-xr-xbusybox/debian/rules196
-rw-r--r--busybox/debianutils/Config.in58
-rw-r--r--busybox/debianutils/Makefile32
-rw-r--r--busybox/debianutils/Makefile.in41
-rw-r--r--busybox/debianutils/mktemp.c63
-rw-r--r--busybox/debianutils/pipe_progress.c55
-rw-r--r--busybox/debianutils/readlink.c46
-rw-r--r--busybox/debianutils/run_parts.c114
-rw-r--r--busybox/debianutils/start_stop_daemon.c296
-rw-r--r--busybox/debianutils/which.c96
-rw-r--r--busybox/docs/.cvsignore8
-rwxr-xr-xbusybox/docs/autodocifier.pl274
-rw-r--r--busybox/docs/busybox.net/.cvsignore2
-rw-r--r--busybox/docs/busybox.net/FAQ.html324
-rw-r--r--busybox/docs/busybox.net/about.html63
-rw-r--r--busybox/docs/busybox.net/busybox-growth.ps404
-rw-r--r--busybox/docs/busybox.net/copyright.txt29
-rw-r--r--busybox/docs/busybox.net/cvs_anon.html57
-rw-r--r--busybox/docs/busybox.net/cvs_howto.html44
-rw-r--r--busybox/docs/busybox.net/cvs_write.html32
-rw-r--r--busybox/docs/busybox.net/docs.html27
-rw-r--r--busybox/docs/busybox.net/download.html38
-rw-r--r--busybox/docs/busybox.net/footer.html20
-rw-r--r--busybox/docs/busybox.net/header.html81
-rw-r--r--busybox/docs/busybox.net/images/back.pngbin0 -> 322 bytes
-rw-r--r--busybox/docs/busybox.net/images/busybox.jpegbin0 -> 9023 bytes
-rw-r--r--busybox/docs/busybox.net/images/busybox.pngbin0 -> 34014 bytes
-rw-r--r--busybox/docs/busybox.net/images/busybox1.pngbin0 -> 10913 bytes
-rw-r--r--busybox/docs/busybox.net/images/busybox2.jpgbin0 -> 8204 bytes
-rw-r--r--busybox/docs/busybox.net/images/busybox3.jpgbin0 -> 3292 bytes
-rw-r--r--busybox/docs/busybox.net/images/dir.pngbin0 -> 309 bytes
-rw-r--r--busybox/docs/busybox.net/images/donate.pngbin0 -> 807 bytes
-rw-r--r--busybox/docs/busybox.net/images/fm.mini.pngbin0 -> 7708 bytes
-rw-r--r--busybox/docs/busybox.net/images/gfx_by_gimp.pngbin0 -> 3955 bytes
-rw-r--r--busybox/docs/busybox.net/images/ltbutton2.pngbin0 -> 6798 bytes
-rw-r--r--busybox/docs/busybox.net/images/sdsmall.pngbin0 -> 1593 bytes
-rw-r--r--busybox/docs/busybox.net/images/text.pngbin0 -> 307 bytes
-rw-r--r--busybox/docs/busybox.net/images/vh40.gifbin0 -> 906 bytes
-rw-r--r--busybox/docs/busybox.net/images/written.in.vi.pngbin0 -> 4394 bytes
-rw-r--r--busybox/docs/busybox.net/index.html1
-rw-r--r--busybox/docs/busybox.net/license.html135
-rw-r--r--busybox/docs/busybox.net/lists.html45
-rw-r--r--busybox/docs/busybox.net/news.html52
-rw-r--r--busybox/docs/busybox.net/oldnews.html1060
-rw-r--r--busybox/docs/busybox.net/products.html166
-rw-r--r--busybox/docs/busybox.net/screenshot.html57
-rw-r--r--busybox/docs/busybox.net/shame.html77
-rw-r--r--busybox/docs/busybox_footer.pod258
-rw-r--r--busybox/docs/busybox_header.pod111
-rw-r--r--busybox/docs/contributing.txt449
-rw-r--r--busybox/docs/new-applet-HOWTO.txt163
-rw-r--r--busybox/docs/style-guide.txt680
-rw-r--r--busybox/editors/Config.in123
-rw-r--r--busybox/editors/Makefile32
-rw-r--r--busybox/editors/Makefile.in48
-rw-r--r--busybox/editors/awk.c2764
-rw-r--r--busybox/editors/patch.c290
-rw-r--r--busybox/editors/sed.c1220
-rw-r--r--busybox/editors/vi.c3983
-rw-r--r--busybox/examples/bootfloppy/bootfloppy.txt180
-rw-r--r--busybox/examples/bootfloppy/display.txt4
-rw-r--r--busybox/examples/bootfloppy/etc/fstab2
-rwxr-xr-xbusybox/examples/bootfloppy/etc/init.d/rcS3
-rw-r--r--busybox/examples/bootfloppy/etc/inittab5
-rw-r--r--busybox/examples/bootfloppy/etc/profile8
-rwxr-xr-xbusybox/examples/bootfloppy/mkdevs.sh62
-rwxr-xr-xbusybox/examples/bootfloppy/mkrootfs.sh105
-rwxr-xr-xbusybox/examples/bootfloppy/mksyslinux.sh48
-rw-r--r--busybox/examples/bootfloppy/quickstart.txt15
-rw-r--r--busybox/examples/bootfloppy/syslinux.cfg7
-rw-r--r--busybox/examples/busybox.spec44
-rwxr-xr-xbusybox/examples/depmod.pl237
-rw-r--r--busybox/examples/devfsd.conf133
-rw-r--r--busybox/examples/inetd.conf73
-rw-r--r--busybox/examples/inittab90
-rwxr-xr-xbusybox/examples/mk2knr.pl84
-rwxr-xr-xbusybox/examples/udhcp/sample.bound31
-rwxr-xr-xbusybox/examples/udhcp/sample.deconfig4
-rwxr-xr-xbusybox/examples/udhcp/sample.nak4
-rwxr-xr-xbusybox/examples/udhcp/sample.renew31
-rw-r--r--busybox/examples/udhcp/sample.script7
-rw-r--r--busybox/examples/udhcp/simple.script40
-rw-r--r--busybox/examples/udhcp/udhcpd.conf123
-rw-r--r--busybox/examples/undeb53
-rw-r--r--busybox/examples/unrpm48
-rw-r--r--busybox/findutils/Config.in133
-rw-r--r--busybox/findutils/Makefile32
-rw-r--r--busybox/findutils/Makefile.in38
-rw-r--r--busybox/findutils/find.c280
-rw-r--r--busybox/findutils/grep.c397
-rw-r--r--busybox/findutils/xargs.c586
-rw-r--r--busybox/include/.cvsignore2
-rw-r--r--busybox/include/applets.h678
-rw-r--r--busybox/include/busybox.h121
-rw-r--r--busybox/include/dump.h49
-rw-r--r--busybox/include/grp_.h116
-rw-r--r--busybox/include/inet_common.h33
-rw-r--r--busybox/include/libbb.h487
-rw-r--r--busybox/include/pwd_.h106
-rw-r--r--busybox/include/shadow_.h98
-rw-r--r--busybox/include/unarchive.h107
-rw-r--r--busybox/include/usage.h2885
-rw-r--r--busybox/init/Config.in72
-rw-r--r--busybox/init/Makefile32
-rw-r--r--busybox/init/Makefile.in62
-rw-r--r--busybox/init/halt.c48
-rw-r--r--busybox/init/init.c1214
-rw-r--r--busybox/init/init_shared.c95
-rw-r--r--busybox/init/init_shared.h3
-rw-r--r--busybox/init/mesg.c58
-rw-r--r--busybox/init/poweroff.c56
-rw-r--r--busybox/init/reboot.c56
-rw-r--r--busybox/libbb/.cvsignore1
-rw-r--r--busybox/libbb/Makefile32
-rw-r--r--busybox/libbb/Makefile.in107
-rw-r--r--busybox/libbb/README11
-rw-r--r--busybox/libbb/ask_confirmation.c49
-rw-r--r--busybox/libbb/bb_askpass.c87
-rw-r--r--busybox/libbb/bb_asprintf.c22
-rw-r--r--busybox/libbb/change_identity.c62
-rw-r--r--busybox/libbb/chomp.c45
-rw-r--r--busybox/libbb/compare_string_array.c31
-rw-r--r--busybox/libbb/concat_path_file.c44
-rw-r--r--busybox/libbb/concat_subpath_file.c36
-rw-r--r--busybox/libbb/copy_file.c268
-rw-r--r--busybox/libbb/copyfd.c90
-rw-r--r--busybox/libbb/correct_password.c77
-rw-r--r--busybox/libbb/create_icmp6_socket.c39
-rw-r--r--busybox/libbb/create_icmp_socket.c37
-rw-r--r--busybox/libbb/default_error_retval.c32
-rw-r--r--busybox/libbb/device_open.c53
-rw-r--r--busybox/libbb/dump.c819
-rw-r--r--busybox/libbb/error_msg.c46
-rw-r--r--busybox/libbb/error_msg_and_die.c47
-rw-r--r--busybox/libbb/fclose_nonstdin.c37
-rw-r--r--busybox/libbb/fflush_stdout_and_exit.c37
-rw-r--r--busybox/libbb/fgets_str.c67
-rw-r--r--busybox/libbb/find_mount_point.c75
-rw-r--r--busybox/libbb/find_pid_by_name.c70
-rw-r--r--busybox/libbb/find_root_device.c89
-rw-r--r--busybox/libbb/full_read.c63
-rw-r--r--busybox/libbb/full_write.c60
-rw-r--r--busybox/libbb/get_console.c99
-rw-r--r--busybox/libbb/get_last_path_component.c56
-rw-r--r--busybox/libbb/get_line_from_file.c82
-rw-r--r--busybox/libbb/get_terminal_width_height.c66
-rw-r--r--busybox/libbb/get_ug_id.c30
-rw-r--r--busybox/libbb/getopt_ulflags.c171
-rw-r--r--busybox/libbb/hash_fd.c859
-rw-r--r--busybox/libbb/herror_msg.c44
-rw-r--r--busybox/libbb/herror_msg_and_die.c45
-rw-r--r--busybox/libbb/human_readable.c87
-rw-r--r--busybox/libbb/inet_common.c249
-rw-r--r--busybox/libbb/inode_hash.c111
-rw-r--r--busybox/libbb/interface.c2083
-rw-r--r--busybox/libbb/isdirectory.c60
-rw-r--r--busybox/libbb/kernel_version.c60
-rw-r--r--busybox/libbb/last_char_is.c38
-rw-r--r--busybox/libbb/llist_add_to.c15
-rw-r--r--busybox/libbb/login.c128
-rw-r--r--busybox/libbb/loop.c157
-rw-r--r--busybox/libbb/make_directory.c117
-rw-r--r--busybox/libbb/messages.c96
-rw-r--r--busybox/libbb/mode_string.c139
-rw-r--r--busybox/libbb/module_syscalls.c116
-rw-r--r--busybox/libbb/mtab.c116
-rw-r--r--busybox/libbb/mtab_file.c42
-rw-r--r--busybox/libbb/my_getgrgid.c57
-rw-r--r--busybox/libbb/my_getgrnam.c49
-rw-r--r--busybox/libbb/my_getpwnam.c49
-rw-r--r--busybox/libbb/my_getpwuid.c56
-rw-r--r--busybox/libbb/my_getug.c64
-rw-r--r--busybox/libbb/obscure.c251
-rw-r--r--busybox/libbb/parse_mode.c177
-rw-r--r--busybox/libbb/parse_number.c64
-rw-r--r--busybox/libbb/perror_msg.c45
-rw-r--r--busybox/libbb/perror_msg_and_die.c46
-rw-r--r--busybox/libbb/perror_nomsg.c30
-rw-r--r--busybox/libbb/perror_nomsg_and_die.c30
-rw-r--r--busybox/libbb/print_file.c76
-rw-r--r--busybox/libbb/printf.c181
-rw-r--r--busybox/libbb/process_escape_sequence.c112
-rw-r--r--busybox/libbb/procps.c158
-rw-r--r--busybox/libbb/pw_encrypt.c45
-rw-r--r--busybox/libbb/pwd2spwd.c74
-rw-r--r--busybox/libbb/qmodule.c29
-rw-r--r--busybox/libbb/read_package_field.c114
-rw-r--r--busybox/libbb/recursive_action.c141
-rw-r--r--busybox/libbb/remove_file.c124
-rw-r--r--busybox/libbb/restricted_shell.c57
-rw-r--r--busybox/libbb/run_parts.c126
-rw-r--r--busybox/libbb/run_shell.c87
-rw-r--r--busybox/libbb/safe_read.c48
-rw-r--r--busybox/libbb/safe_strncpy.c42
-rw-r--r--busybox/libbb/safe_strtol.c92
-rw-r--r--busybox/libbb/safe_write.c48
-rw-r--r--busybox/libbb/setup_environment.c93
-rw-r--r--busybox/libbb/simplify_path.c64
-rw-r--r--busybox/libbb/skip_whitespace.c33
-rw-r--r--busybox/libbb/speed_table.c130
-rw-r--r--busybox/libbb/syscalls.c105
-rw-r--r--busybox/libbb/syslog_msg_with_name.c0
-rw-r--r--busybox/libbb/trim.c49
-rw-r--r--busybox/libbb/u_signal_names.c189
-rw-r--r--busybox/libbb/vdprintf.c47
-rw-r--r--busybox/libbb/verror_msg.c43
-rw-r--r--busybox/libbb/vfork_daemon_rexec.c78
-rw-r--r--busybox/libbb/vherror_msg.c37
-rw-r--r--busybox/libbb/vperror_msg.c45
-rw-r--r--busybox/libbb/warn_ignoring_args.c30
-rw-r--r--busybox/libbb/wfopen.c44
-rw-r--r--busybox/libbb/wfopen_input.c54
-rw-r--r--busybox/libbb/xconnect.c71
-rw-r--r--busybox/libbb/xfuncs.c197
-rw-r--r--busybox/libbb/xgetcwd.c48
-rw-r--r--busybox/libbb/xgethostbyname.c35
-rw-r--r--busybox/libbb/xgethostbyname2.c37
-rw-r--r--busybox/libbb/xgetlarg.c35
-rw-r--r--busybox/libbb/xgetularg.c160
-rw-r--r--busybox/libbb/xreadlink.c37
-rw-r--r--busybox/libbb/xregcomp.c49
-rw-r--r--busybox/libpwdgrp/Makefile32
-rw-r--r--busybox/libpwdgrp/Makefile.in53
-rw-r--r--busybox/libpwdgrp/pwd_grp.c1116
-rw-r--r--busybox/loginutils/Config.in161
-rw-r--r--busybox/loginutils/Makefile32
-rw-r--r--busybox/loginutils/Makefile.in57
-rw-r--r--busybox/loginutils/addgroup.c171
-rw-r--r--busybox/loginutils/adduser.c314
-rw-r--r--busybox/loginutils/delgroup.c62
-rw-r--r--busybox/loginutils/delline.c113
-rw-r--r--busybox/loginutils/deluser.c68
-rw-r--r--busybox/loginutils/getty.c1020
-rw-r--r--busybox/loginutils/login.c486
-rw-r--r--busybox/loginutils/passwd.c400
-rw-r--r--busybox/loginutils/su.c157
-rw-r--r--busybox/loginutils/sulogin.c158
-rw-r--r--busybox/loginutils/vlock.c231
-rw-r--r--busybox/miscutils/Config.in201
-rw-r--r--busybox/miscutils/Makefile32
-rw-r--r--busybox/miscutils/Makefile.in55
-rw-r--r--busybox/miscutils/adjtimex.c167
-rw-r--r--busybox/miscutils/crond.c1055
-rw-r--r--busybox/miscutils/crontab.c368
-rw-r--r--busybox/miscutils/dc.c228
-rw-r--r--busybox/miscutils/devfsd.c2183
-rw-r--r--busybox/miscutils/hdparm.c2872
-rw-r--r--busybox/miscutils/last.c107
-rw-r--r--busybox/miscutils/makedevs.c93
-rw-r--r--busybox/miscutils/mt.c121
-rw-r--r--busybox/miscutils/rx.c344
-rw-r--r--busybox/miscutils/strings.c156
-rw-r--r--busybox/miscutils/time.c502
-rw-r--r--busybox/miscutils/watchdog.c81
-rw-r--r--busybox/modutils/Config.in113
-rw-r--r--busybox/modutils/Makefile32
-rw-r--r--busybox/modutils/Makefile.in39
-rw-r--r--busybox/modutils/insmod.c4039
-rw-r--r--busybox/modutils/lsmod.c174
-rw-r--r--busybox/modutils/modprobe.c654
-rw-r--r--busybox/modutils/rmmod.c124
-rw-r--r--busybox/networking/Config.in634
-rw-r--r--busybox/networking/Makefile32
-rw-r--r--busybox/networking/Makefile.in68
-rw-r--r--busybox/networking/arping.c499
-rw-r--r--busybox/networking/ftpgetput.c378
-rw-r--r--busybox/networking/hostname.c130
-rw-r--r--busybox/networking/httpd.c2091
-rw-r--r--busybox/networking/ifconfig.c605
-rw-r--r--busybox/networking/ifupdown.c1463
-rw-r--r--busybox/networking/inetd.c1221
-rw-r--r--busybox/networking/ip.c115
-rw-r--r--busybox/networking/ipaddr.c27
-rw-r--r--busybox/networking/ipcalc.c224
-rw-r--r--busybox/networking/iplink.c27
-rw-r--r--busybox/networking/iproute.c27
-rw-r--r--busybox/networking/iptunnel.c27
-rw-r--r--busybox/networking/libiproute/Makefile32
-rw-r--r--busybox/networking/libiproute/Makefile.in84
-rw-r--r--busybox/networking/libiproute/ip_common.h18
-rw-r--r--busybox/networking/libiproute/ip_parse_common_args.c76
-rw-r--r--busybox/networking/libiproute/ipaddress.c825
-rw-r--r--busybox/networking/libiproute/iplink.c367
-rw-r--r--busybox/networking/libiproute/iproute.c852
-rw-r--r--busybox/networking/libiproute/iptunnel.c548
-rw-r--r--busybox/networking/libiproute/libnetlink.c524
-rw-r--r--busybox/networking/libiproute/libnetlink.h46
-rw-r--r--busybox/networking/libiproute/linux/pkt_sched.h413
-rw-r--r--busybox/networking/libiproute/ll_addr.c81
-rw-r--r--busybox/networking/libiproute/ll_map.c164
-rw-r--r--busybox/networking/libiproute/ll_map.h12
-rw-r--r--busybox/networking/libiproute/ll_proto.c120
-rw-r--r--busybox/networking/libiproute/ll_types.c115
-rw-r--r--busybox/networking/libiproute/rt_names.c385
-rw-r--r--busybox/networking/libiproute/rt_names.h30
-rw-r--r--busybox/networking/libiproute/rtm_map.c110
-rw-r--r--busybox/networking/libiproute/rtm_map.h10
-rw-r--r--busybox/networking/libiproute/utils.c359
-rw-r--r--busybox/networking/libiproute/utils.h101
-rw-r--r--busybox/networking/nameif.c222
-rw-r--r--busybox/networking/nc.c177
-rw-r--r--busybox/networking/netstat.c661
-rw-r--r--busybox/networking/nslookup.c206
-rw-r--r--busybox/networking/ping.c472
-rw-r--r--busybox/networking/ping6.c515
-rw-r--r--busybox/networking/route.c714
-rw-r--r--busybox/networking/telnet.c769
-rw-r--r--busybox/networking/telnetd.c660
-rw-r--r--busybox/networking/tftp.c584
-rw-r--r--busybox/networking/traceroute.c548
-rw-r--r--busybox/networking/udhcp/AUTHORS13
-rw-r--r--busybox/networking/udhcp/COPYING339
-rw-r--r--busybox/networking/udhcp/ChangeLog260
-rw-r--r--busybox/networking/udhcp/Config.in62
-rw-r--r--busybox/networking/udhcp/Makefile32
-rw-r--r--busybox/networking/udhcp/Makefile.in54
-rw-r--r--busybox/networking/udhcp/README53
-rw-r--r--busybox/networking/udhcp/README.dumpleases17
-rw-r--r--busybox/networking/udhcp/README.udhcpc141
-rw-r--r--busybox/networking/udhcp/README.udhcpd59
-rw-r--r--busybox/networking/udhcp/TODO16
-rw-r--r--busybox/networking/udhcp/arpping.c106
-rw-r--r--busybox/networking/udhcp/arpping.h35
-rw-r--r--busybox/networking/udhcp/clientpacket.c248
-rw-r--r--busybox/networking/udhcp/clientpacket.h14
-rw-r--r--busybox/networking/udhcp/clientsocket.c62
-rw-r--r--busybox/networking/udhcp/clientsocket.h7
-rw-r--r--busybox/networking/udhcp/common.c162
-rw-r--r--busybox/networking/udhcp/common.h56
-rw-r--r--busybox/networking/udhcp/dhcpc.c517
-rw-r--r--busybox/networking/udhcp/dhcpc.h37
-rw-r--r--busybox/networking/udhcp/dhcpd.c273
-rw-r--r--busybox/networking/udhcp/dhcpd.h140
-rw-r--r--busybox/networking/udhcp/dumpleases.c110
-rw-r--r--busybox/networking/udhcp/files.c346
-rw-r--r--busybox/networking/udhcp/files.h17
-rw-r--r--busybox/networking/udhcp/frontend.c16
-rw-r--r--busybox/networking/udhcp/leases.c158
-rw-r--r--busybox/networking/udhcp/leases.h23
-rw-r--r--busybox/networking/udhcp/libbb_udhcp.h54
-rw-r--r--busybox/networking/udhcp/options.c228
-rw-r--r--busybox/networking/udhcp/options.h40
-rw-r--r--busybox/networking/udhcp/packet.c202
-rw-r--r--busybox/networking/udhcp/packet.h41
-rw-r--r--busybox/networking/udhcp/pidfile.c75
-rw-r--r--busybox/networking/udhcp/pidfile.h25
-rw-r--r--busybox/networking/udhcp/script.c233
-rw-r--r--busybox/networking/udhcp/script.h6
-rw-r--r--busybox/networking/udhcp/serverpacket.c275
-rw-r--r--busybox/networking/udhcp/serverpacket.h12
-rw-r--r--busybox/networking/udhcp/signalpipe.c78
-rw-r--r--busybox/networking/udhcp/signalpipe.h22
-rw-r--r--busybox/networking/udhcp/socket.c132
-rw-r--r--busybox/networking/udhcp/socket.h8
-rw-r--r--busybox/networking/udhcp/static_leases.c119
-rw-r--r--busybox/networking/udhcp/static_leases.h25
-rw-r--r--busybox/networking/udhcp/version.h6
-rw-r--r--busybox/networking/vconfig.c184
-rw-r--r--busybox/networking/wget.c868
-rw-r--r--busybox/patches/cmp_n.diff377
-rw-r--r--busybox/patches/dd_ibs_and_obs.diff252
-rw-r--r--busybox/patches/eject.diff164
-rw-r--r--busybox/patches/makdevs_table.diff294
-rw-r--r--busybox/patches/rpm2cpio_bzip2.patch63
-rw-r--r--busybox/patches/tftp_timeout_multicast.diff1053
-rw-r--r--busybox/patches/top_system_cpu.diff51
-rw-r--r--busybox/patches/udhcp_additional_items.diff126
-rw-r--r--busybox/patches/udhcp_config_paths.diff333
-rw-r--r--busybox/patches/udhcpd_foreground.diff33
-rw-r--r--busybox/procps/Config.in82
-rw-r--r--busybox/procps/Makefile32
-rw-r--r--busybox/procps/Makefile.in43
-rw-r--r--busybox/procps/free.c84
-rw-r--r--busybox/procps/kill.c156
-rw-r--r--busybox/procps/pidof.c71
-rw-r--r--busybox/procps/ps.c109
-rw-r--r--busybox/procps/renice.c54
-rw-r--r--busybox/procps/sysctl.c352
-rw-r--r--busybox/procps/top.c592
-rw-r--r--busybox/procps/uptime.c75
-rw-r--r--busybox/scripts/.cvsignore2
-rw-r--r--busybox/scripts/config/.cvsignore8
-rw-r--r--busybox/scripts/config/Kconfig-language.txt255
-rw-r--r--busybox/scripts/config/Makefile111
-rw-r--r--busybox/scripts/config/checklist.c372
-rw-r--r--busybox/scripts/config/colors.h161
-rw-r--r--busybox/scripts/config/conf.c583
-rw-r--r--busybox/scripts/config/confdata.c447
-rw-r--r--busybox/scripts/config/dialog.h196
-rw-r--r--busybox/scripts/config/expr.c1089
-rw-r--r--busybox/scripts/config/expr.h193
-rw-r--r--busybox/scripts/config/inputbox.c240
-rw-r--r--busybox/scripts/config/lex.zconf.c_shipped3688
-rw-r--r--busybox/scripts/config/lkc.h113
-rw-r--r--busybox/scripts/config/lkc_proto.h39
-rw-r--r--busybox/scripts/config/mconf.c713
-rw-r--r--busybox/scripts/config/menu.c431
-rw-r--r--busybox/scripts/config/menubox.c436
-rw-r--r--busybox/scripts/config/msgbox.c85
-rw-r--r--busybox/scripts/config/symbol.c771
-rw-r--r--busybox/scripts/config/textbox.c556
-rw-r--r--busybox/scripts/config/util.c375
-rw-r--r--busybox/scripts/config/yesno.c118
-rw-r--r--busybox/scripts/config/zconf.l366
-rw-r--r--busybox/scripts/config/zconf.tab.c_shipped2127
-rw-r--r--busybox/scripts/config/zconf.tab.h_shipped125
-rw-r--r--busybox/scripts/config/zconf.y687
-rw-r--r--busybox/scripts/mkdep.c628
-rw-r--r--busybox/scripts/split-include.c226
-rw-r--r--busybox/shell/Config.in229
-rw-r--r--busybox/shell/Makefile32
-rw-r--r--busybox/shell/Makefile.in40
-rw-r--r--busybox/shell/ash.c13586
-rw-r--r--busybox/shell/cmdedit.c1582
-rw-r--r--busybox/shell/cmdedit.h15
-rw-r--r--busybox/shell/hush.c2963
-rw-r--r--busybox/shell/lash.c1696
-rw-r--r--busybox/shell/msh.c5487
-rw-r--r--busybox/sysdeps/linux/Config.in294
-rw-r--r--busybox/sysdeps/linux/defconfig421
-rw-r--r--busybox/sysklogd/Config.in109
-rw-r--r--busybox/sysklogd/Makefile32
-rw-r--r--busybox/sysklogd/Makefile.in39
-rw-r--r--busybox/sysklogd/klogd.c165
-rw-r--r--busybox/sysklogd/logger.c204
-rw-r--r--busybox/sysklogd/logread.c186
-rw-r--r--busybox/sysklogd/syslogd.c712
-rw-r--r--busybox/testsuite/README31
-rw-r--r--busybox/testsuite/TODO18
-rw-r--r--busybox/testsuite/basename/basename-does-not-remove-identical-extension1
-rw-r--r--busybox/testsuite/basename/basename-works2
-rw-r--r--busybox/testsuite/bunzip2/bunzip2-reads-from-standard-input2
-rw-r--r--busybox/testsuite/bunzip2/bunzip2-removes-compressed-file3
-rw-r--r--busybox/testsuite/bunzip2/bzcat-does-not-remove-compressed-file3
-rw-r--r--busybox/testsuite/cat/cat-prints-a-file3
-rw-r--r--busybox/testsuite/cat/cat-prints-a-file-and-standard-input7
-rw-r--r--busybox/testsuite/cmp/cmp-detects-difference9
-rw-r--r--busybox/testsuite/cp/cp-a-files-to-dir14
-rw-r--r--busybox/testsuite/cp/cp-a-preserves-links5
-rw-r--r--busybox/testsuite/cp/cp-copies-empty-file3
-rw-r--r--busybox/testsuite/cp/cp-copies-large-file3
-rw-r--r--busybox/testsuite/cp/cp-copies-small-file3
-rw-r--r--busybox/testsuite/cp/cp-d-files-to-dir11
-rw-r--r--busybox/testsuite/cp/cp-dir-create-dir4
-rw-r--r--busybox/testsuite/cp/cp-dir-existing-dir5
-rw-r--r--busybox/testsuite/cp/cp-does-not-copy-unreadable-file6
-rw-r--r--busybox/testsuite/cp/cp-files-to-dir11
-rw-r--r--busybox/testsuite/cp/cp-follows-links4
-rw-r--r--busybox/testsuite/cp/cp-preserves-hard-links6
-rw-r--r--busybox/testsuite/cp/cp-preserves-links5
-rw-r--r--busybox/testsuite/cp/cp-preserves-source-file3
-rw-r--r--busybox/testsuite/cut/cut-cuts-a-character1
-rw-r--r--busybox/testsuite/cut/cut-cuts-a-closed-range1
-rw-r--r--busybox/testsuite/cut/cut-cuts-a-field1
-rw-r--r--busybox/testsuite/cut/cut-cuts-an-open-range1
-rw-r--r--busybox/testsuite/cut/cut-cuts-an-unclosed-range1
-rw-r--r--busybox/testsuite/date/date-R-works2
-rw-r--r--busybox/testsuite/date/date-format-works1
-rw-r--r--busybox/testsuite/date/date-u-works2
-rw-r--r--busybox/testsuite/date/date-works2
-rw-r--r--busybox/testsuite/dd/dd-accepts-if2
-rw-r--r--busybox/testsuite/dd/dd-accepts-of2
-rw-r--r--busybox/testsuite/dd/dd-copies-from-standard-input-to-standard-output1
-rw-r--r--busybox/testsuite/dd/dd-prints-count-to-standard-error2
-rw-r--r--busybox/testsuite/dirname/dirname-handles-absolute-path1
-rw-r--r--busybox/testsuite/dirname/dirname-handles-empty-path1
-rw-r--r--busybox/testsuite/dirname/dirname-handles-multiple-slashes1
-rw-r--r--busybox/testsuite/dirname/dirname-handles-relative-path1
-rw-r--r--busybox/testsuite/dirname/dirname-handles-root1
-rw-r--r--busybox/testsuite/dirname/dirname-handles-single-component1
-rw-r--r--busybox/testsuite/dirname/dirname-works2
-rw-r--r--busybox/testsuite/du/du-h-works4
-rw-r--r--busybox/testsuite/du/du-k-works4
-rw-r--r--busybox/testsuite/du/du-l-works4
-rw-r--r--busybox/testsuite/du/du-m-works4
-rw-r--r--busybox/testsuite/du/du-s-works4
-rw-r--r--busybox/testsuite/du/du-works4
-rw-r--r--busybox/testsuite/echo/echo-does-not-print-newline1
-rw-r--r--busybox/testsuite/echo/echo-prints-argument1
-rw-r--r--busybox/testsuite/echo/echo-prints-arguments1
-rw-r--r--busybox/testsuite/echo/echo-prints-newline1
-rw-r--r--busybox/testsuite/expr/expr-works59
-rw-r--r--busybox/testsuite/false/false-is-silent1
-rw-r--r--busybox/testsuite/false/false-returns-failure1
-rw-r--r--busybox/testsuite/find/find-supports-minus-xdev1
-rw-r--r--busybox/testsuite/grep/egrep-is-not-case-insensitive2
-rw-r--r--busybox/testsuite/grep/egrep-supports-extended-regexps2
-rw-r--r--busybox/testsuite/grep/grep-handles-binary-files1
-rw-r--r--busybox/testsuite/grep/grep-handles-multiple-regexps1
-rw-r--r--busybox/testsuite/grep/grep-is-also-egrep2
-rw-r--r--busybox/testsuite/grep/grep-matches-NUL8
-rw-r--r--busybox/testsuite/gunzip/gunzip-reads-from-standard-input2
-rw-r--r--busybox/testsuite/gzip/gzip-accepts-multiple-files3
-rw-r--r--busybox/testsuite/gzip/gzip-accepts-single-minus1
-rw-r--r--busybox/testsuite/gzip/gzip-removes-original-file3
-rw-r--r--busybox/testsuite/head/head-n-works4
-rw-r--r--busybox/testsuite/head/head-works4
-rw-r--r--busybox/testsuite/hostid/hostid-works2
-rw-r--r--busybox/testsuite/hostname/hostname-d-works2
-rw-r--r--busybox/testsuite/hostname/hostname-i-works2
-rw-r--r--busybox/testsuite/hostname/hostname-s-works1
-rw-r--r--busybox/testsuite/hostname/hostname-works1
-rw-r--r--busybox/testsuite/id/id-g-works1
-rw-r--r--busybox/testsuite/id/id-u-works1
-rw-r--r--busybox/testsuite/id/id-un-works1
-rw-r--r--busybox/testsuite/id/id-ur-works1
-rw-r--r--busybox/testsuite/ln/ln-creates-hard-links4
-rw-r--r--busybox/testsuite/ln/ln-creates-soft-links4
-rw-r--r--busybox/testsuite/ln/ln-force-creates-hard-links5
-rw-r--r--busybox/testsuite/ln/ln-force-creates-soft-links5
-rw-r--r--busybox/testsuite/ln/ln-preserves-hard-links8
-rw-r--r--busybox/testsuite/ln/ln-preserves-soft-links9
-rw-r--r--busybox/testsuite/ls/ls-1-works4
-rw-r--r--busybox/testsuite/ls/ls-h-works4
-rw-r--r--busybox/testsuite/ls/ls-l-works4
-rw-r--r--busybox/testsuite/ls/ls-s-works4
-rw-r--r--busybox/testsuite/md5sum/md5sum-verifies-non-binary-file3
-rw-r--r--busybox/testsuite/mkdir/mkdir-makes-a-directory2
-rw-r--r--busybox/testsuite/mkdir/mkdir-makes-parent-directories2
-rw-r--r--busybox/testsuite/msh/msh-supports-underscores-in-variable-names1
-rw-r--r--busybox/testsuite/mv/mv-files-to-dir16
-rw-r--r--busybox/testsuite/mv/mv-follows-links4
-rw-r--r--busybox/testsuite/mv/mv-moves-empty-file4
-rw-r--r--busybox/testsuite/mv/mv-moves-file3
-rw-r--r--busybox/testsuite/mv/mv-moves-hardlinks4
-rw-r--r--busybox/testsuite/mv/mv-moves-large-file4
-rw-r--r--busybox/testsuite/mv/mv-moves-small-file4
-rw-r--r--busybox/testsuite/mv/mv-moves-symlinks6
-rw-r--r--busybox/testsuite/mv/mv-moves-unreadable-files5
-rw-r--r--busybox/testsuite/mv/mv-preserves-hard-links6
-rw-r--r--busybox/testsuite/mv/mv-preserves-links5
-rw-r--r--busybox/testsuite/mv/mv-refuses-mv-dir-to-subdir23
-rw-r--r--busybox/testsuite/mv/mv-removes-source-file4
-rw-r--r--busybox/testsuite/pwd/pwd-prints-working-directory1
-rw-r--r--busybox/testsuite/rm/rm-removes-file3
-rw-r--r--busybox/testsuite/rmdir/rmdir-removes-parent-directories3
-rwxr-xr-xbusybox/testsuite/runtest102
-rw-r--r--busybox/testsuite/sed/sed-accepts-blanks-before-command1
-rw-r--r--busybox/testsuite/sed/sed-aic-commands134
-rw-r--r--busybox/testsuite/sed/sed-append-hold-space-to-pattern-space13
-rw-r--r--busybox/testsuite/sed/sed-append-next-line19
-rw-r--r--busybox/testsuite/sed/sed-branch1
-rw-r--r--busybox/testsuite/sed/sed-branch-conditional15
-rw-r--r--busybox/testsuite/sed/sed-branch-conditional211
-rw-r--r--busybox/testsuite/sed/sed-branch-no-label1
-rw-r--r--busybox/testsuite/sed/sed-chains-substs1
-rw-r--r--busybox/testsuite/sed/sed-chains-substs21
-rw-r--r--busybox/testsuite/sed/sed-does-not-substitute-in-deleted-line2
-rw-r--r--busybox/testsuite/sed/sed-handles-embedded-slashes1
-rw-r--r--busybox/testsuite/sed/sed-handles-empty-lines1
-rw-r--r--busybox/testsuite/sed/sed-handles-unsatisfied-backrefs6
-rw-r--r--busybox/testsuite/sed/sed-next-line12
-rw-r--r--busybox/testsuite/sed/sed-prints-line-once-for-multiple-substs4
-rw-r--r--busybox/testsuite/sed/sed-recurses-properly1
-rw-r--r--busybox/testsuite/sed/sed-regex-match-newline10
-rw-r--r--busybox/testsuite/sed/sed-splits-edit-commands-on-command-line9
-rw-r--r--busybox/testsuite/sed/sed-subst-subprint9
-rw-r--r--busybox/testsuite/sed/sed-write-to-stdout10
-rw-r--r--busybox/testsuite/sort/sort-n-works4
-rw-r--r--busybox/testsuite/sort/sort-r-works4
-rw-r--r--busybox/testsuite/sort/sort-works4
-rw-r--r--busybox/testsuite/strings/strings-works-like-GNU9
-rw-r--r--busybox/testsuite/tail/tail-n-works4
-rw-r--r--busybox/testsuite/tail/tail-works4
-rw-r--r--busybox/testsuite/tar/tar-archives-multiple-files6
-rw-r--r--busybox/testsuite/tar/tar-complains-about-missing-file3
-rw-r--r--busybox/testsuite/tar/tar-demands-at-least-one-ctx1
-rw-r--r--busybox/testsuite/tar/tar-demands-at-most-one-ctx1
-rw-r--r--busybox/testsuite/tar/tar-extracts-file5
-rw-r--r--busybox/testsuite/tar/tar-extracts-from-standard-input5
-rw-r--r--busybox/testsuite/tar/tar-extracts-multiple-files6
-rw-r--r--busybox/testsuite/tar/tar-extracts-to-standard-output3
-rw-r--r--busybox/testsuite/tar/tar-handles-cz-options5
-rw-r--r--busybox/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list6
-rw-r--r--busybox/testsuite/tar/tar-handles-exclude-and-extract-lists8
-rw-r--r--busybox/testsuite/tar/tar-handles-multiple-X-options10
-rw-r--r--busybox/testsuite/tar/tar-handles-nested-exclude9
-rw-r--r--busybox/testsuite/tee/tee-appends-input5
-rw-r--r--busybox/testsuite/tee/tee-tees-input3
-rw-r--r--busybox/testsuite/touch/touch-creates-file2
-rw-r--r--busybox/testsuite/touch/touch-does-not-create-file2
-rw-r--r--busybox/testsuite/touch/touch-touches-files-after-non-existent-file3
-rw-r--r--busybox/testsuite/tr/tr-d-works4
-rw-r--r--busybox/testsuite/tr/tr-non-gnu1
-rw-r--r--busybox/testsuite/tr/tr-works9
-rw-r--r--busybox/testsuite/true/true-is-silent1
-rw-r--r--busybox/testsuite/true/true-returns-success1
-rw-r--r--busybox/testsuite/uptime/uptime-works2
-rw-r--r--busybox/testsuite/uuencode/uuencode-sets-standard-input-mode-correctly4
-rw-r--r--busybox/testsuite/wc/wc-counts-all1
-rw-r--r--busybox/testsuite/wc/wc-counts-characters1
-rw-r--r--busybox/testsuite/wc/wc-counts-lines1
-rw-r--r--busybox/testsuite/wc/wc-counts-words1
-rw-r--r--busybox/testsuite/wc/wc-prints-longest-line-length1
-rw-r--r--busybox/testsuite/wget/wget--O-overrides--P3
-rw-r--r--busybox/testsuite/wget/wget-handles-empty-path1
-rw-r--r--busybox/testsuite/wget/wget-retrieves-google-index2
-rw-r--r--busybox/testsuite/wget/wget-supports--P3
-rw-r--r--busybox/testsuite/which/which-uses-default-path4
-rw-r--r--busybox/testsuite/xargs/xargs-works4
-rw-r--r--busybox/util-linux/Config.in357
-rw-r--r--busybox/util-linux/Makefile32
-rw-r--r--busybox/util-linux/Makefile.in66
-rw-r--r--busybox/util-linux/dmesg.c99
-rw-r--r--busybox/util-linux/fbset.c427
-rw-r--r--busybox/util-linux/fdflush.c54
-rw-r--r--busybox/util-linux/fdformat.c161
-rw-r--r--busybox/util-linux/fdisk.c5880
-rw-r--r--busybox/util-linux/freeramdisk.c69
-rw-r--r--busybox/util-linux/fsck_minix.c1476
-rw-r--r--busybox/util-linux/getopt.c391
-rw-r--r--busybox/util-linux/hexdump.c142
-rw-r--r--busybox/util-linux/hwclock.c230
-rw-r--r--busybox/util-linux/losetup.c59
-rw-r--r--busybox/util-linux/mkfs_minix.c852
-rw-r--r--busybox/util-linux/mkswap.c418
-rw-r--r--busybox/util-linux/more.c211
-rw-r--r--busybox/util-linux/mount.c497
-rw-r--r--busybox/util-linux/nfsmount.c1026
-rw-r--r--busybox/util-linux/nfsmount.h242
-rw-r--r--busybox/util-linux/pivot_root.c35
-rw-r--r--busybox/util-linux/rdate.c121
-rw-r--r--busybox/util-linux/swaponoff.c120
-rw-r--r--busybox/util-linux/umount.c298
785 files changed, 168766 insertions, 0 deletions
diff --git a/busybox/.cvsignore b/busybox/.cvsignore
new file mode 100644
index 000000000..4e4f5863e
--- /dev/null
+++ b/busybox/.cvsignore
@@ -0,0 +1,7 @@
1busybox
2busybox.links
3_install
4.config
5.menuconfig.log
6.config.cmd
7.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 @@
1--blank-lines-after-declarations
2--blank-lines-after-procedures
3--break-before-boolean-operator
4--no-blank-lines-after-commas
5--braces-on-if-line
6--braces-on-struct-decl-line
7--comment-indentation25
8--declaration-comment-column25
9--no-comment-delimiters-on-blank-lines
10--cuddle-else
11--continuation-indentation4
12--case-indentation0
13--else-endif-column33
14--space-after-cast
15--line-comments-indentation0
16--declaration-indentation1
17--dont-format-first-column-comments
18--dont-format-comments
19--honour-newlines
20--indent-level4
21/* changed from 0 to 4 */
22--parameter-indentation4
23--line-length78 /* changed from 75 */
24--continue-at-parentheses
25--no-space-after-function-call-names
26--dont-break-procedure-type
27--dont-star-comments
28--leave-optional-blank-lines
29--dont-space-special-semicolon
30--tab-size4
31/* additions by Mark */
32--case-brace-indentation0
33--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 @@
1List of the authors of code contained in BusyBox.
2
3If you have code in BusyBox, you should be listed here. If you should be
4listed, or the description of what you have done needs more detail, or is
5incorect, _please_ let me know.
6
7 -Erik
8
9-----------
10
11Emanuele Aina <emanuele.aina@tiscali.it>
12 run-parts
13
14Erik Andersen <andersen@codepoet.org>
15 Tons of new stuff, major rewrite of most of the
16 core apps, tons of new apps as noted in header files.
17 Lots of tedious effort writing these boring docs that
18 nobody is going to actually read.
19
20Laurence Anderson <l.d.anderson@warwick.ac.uk>
21 rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm
22
23Jeff Angielski <jeff@theptrgroup.com>
24 ftpput, ftpget
25
26Edward Betts <edward@debian.org>
27 expr, hostid, logname, whoami
28
29John Beppu <beppu@codepoet.org>
30 du, nslookup, sort
31
32Brian Candler <B.Candler@pobox.com>
33 tiny-ls(ls)
34
35Randolph Chung <tausq@debian.org>
36 fbset, ping, hostname
37
38Dave Cinege <dcinege@psychosis.com>
39 more(v2), makedevs, dutmp, modularization, auto links file,
40 various fixes, Linux Router Project maintenance
41
42Jordan Crouse <jordan@cosmicpenguin.net>
43 ipcalc
44
45Magnus Damm <damm@opensource.se>
46 tftp client
47 insmod powerpc support
48
49Larry Doolittle <ldoolitt@recycle.lbl.gov>
50 pristine source directory compilation, lots of patches and fixes.
51
52Glenn Engel <glenne@engel.org>
53 httpd
54
55Gennady Feldman <gfeldman@gena01.com>
56 Sysklogd (single threaded syslogd, IPC Circular buffer support,
57 logread), various fixes.
58
59Robert Griebl <sandman@handhelds.org>
60 modprobe, hwclock, suid/sgid handling, tinylogin integration
61 many bugfixes and enhancements
62
63Karl M. Hegbloom <karlheg@debian.org>
64 cp_mv.c, the test suite, various fixes to utility.c, &c.
65
66Daniel Jacobowitz <dan@debian.org>
67 mktemp.c
68
69Matt Kraai <kraai@alumni.cmu.edu>
70 documentation, bugfixes, test suite
71
72Stephan Linz <linz@li-pro.net>
73 ipcalc, Red Hat equivalence
74
75John Lombardo <john@deltanet.com>
76 tr
77
78Glenn McGrath <bug1@iinet.net.au>
79 Common unarchving code and unarchiving applets, ifupdown, ftpgetput,
80 nameif, sed, patch, fold, install, uudecode.
81 Various bugfixes, review and apply numerous patches.
82
83Manuel Novoa III <mjn3@codepoet.org>
84 cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes,
85 mesg, vconfig, make_directory, parse_mode, dirname, mode_string,
86 get_last_path_component, simplify_path, and a number trivial libbb routines
87
88 also bug fixes, partial rewrites, and size optimizations in
89 ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir,
90 mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable,
91 interface, dutmp, ifconfig, route
92
93Vladimir Oleynik <dzo@simtreas.ru>
94 cmdedit; xargs(current), httpd(current);
95 ports: ash, crond, fdisk, inetd, stty, traceroute, top;
96 locale, various fixes
97 and irreconcilable critic of everything not perfect.
98
99Bruce Perens <bruce@pixar.com>
100 Original author of BusyBox in 1995, 1996. Some of his code can
101 still be found hiding here and there...
102
103Tim Riker <Tim@Rikers.org>
104 bug fixes, member of fan club
105
106Kent Robotti <robotti@metconnect.com>
107 reset, tons and tons of bug reports and patches.
108
109Chip Rosenthal <chip@unicom.com>, <crosenth@covad.com>
110 wget - Contributed by permission of Covad Communications
111
112Pavel Roskin <proski@gnu.org>
113 Lots of bugs fixes and patches.
114
115Gyepi Sam <gyepi@praxis-sw.com>
116 Remote logging feature for syslogd
117
118Linus Torvalds <torvalds@transmeta.com>
119 mkswap, fsck.minix, mkfs.minix
120
121Mark Whitley <markw@codepoet.org>
122 grep, sed, cut, xargs(previous),
123 style-guide, new-applet-HOWTO, bug fixes, etc.
124
125Charles P. Wright <cpwright@villagenet.com>
126 gzip, mini-netcat(nc)
127
128Enrique Zanardi <ezanardi@ull.es>
129 tarcat (since removed), loadkmap, various fixes, Debian maintenance
130
131Tito Ragusa <farmatito@tiscali.it>
132 devfsd and size optimizations in strings, openvt, chvt, deallocvt, hdparm and fdformat.
133
diff --git a/busybox/Changelog b/busybox/Changelog
new file mode 100644
index 000000000..721fc8270
--- /dev/null
+++ b/busybox/Changelog
@@ -0,0 +1,1381 @@
1---------------------
2PatchSet 4347
3Date: 2004/08/16 10:29:28
4Author: andersen
5Branch: HEAD
6Tag: busybox_1_00_rc3
7Log:
8Prepare for release
9
10Members:
11 Changelog:1.294->1.295
12 docs/busybox_header.pod:1.17->1.18
13 docs/busybox.net/news.html:1.21->1.22
14 docs/busybox.net/screenshot.html:1.11->1.12
15
16---------------------
17PatchSet 4348
18Date: 2004/08/18 17:57:16
19Author: andersen
20Branch: HEAD
21Tag: (none)
22Log:
23Fixup 'dc' usage
24
25Members:
26 include/usage.h:1.218->1.219
27
28---------------------
29PatchSet 4349
30Date: 2004/08/19 18:22:13
31Author: andersen
32Branch: HEAD
33Tag: (none)
34Log:
35Patch from Vladimir N. Oleynik:
36
37On Wed Aug 18, 2004 at 06:52:57PM +0800, Matt Johnston wrote:
38> I've come across some strange-seeming behaviour when running programs
39> under Busybox (1.0.0-rc3) ash. If the child process sets stdin to be
40> non-blocking and then exits, the parent ash will also exit. A quick strace
41> shows that a subsequent read() from stdin returns EAGAIN (as would be
42> expected):
43
44Thanks!
45Patch attached.
46
47
48--w
49vodz
50
51Members:
52 shell/ash.c:1.104->1.105
53 shell/cmdedit.c:1.92->1.93
54
55---------------------
56PatchSet 4350
57Date: 2004/08/19 18:25:02
58Author: andersen
59Branch: HEAD
60Tag: (none)
61Log:
62Patch from Tito documenting the '-q' option
63
64Members:
65 include/usage.h:1.219->1.220
66
67---------------------
68PatchSet 4351
69Date: 2004/08/19 18:26:26
70Author: andersen
71Branch: HEAD
72Tag: (none)
73Log:
74Patch from Tito adding support for '-q'
75
76Members:
77 procps/kill.c:1.52->1.53
78
79---------------------
80PatchSet 4352
81Date: 2004/08/19 18:30:31
82Author: andersen
83Branch: HEAD
84Tag: (none)
85Log:
86Patch from Mike Castle to cleanup some modutils issues, in
87particular making alias support work better.
88
89Members:
90 modutils/modprobe.c:1.39->1.40
91
92---------------------
93PatchSet 4353
94Date: 2004/08/19 19:15:06
95Author: andersen
96Branch: HEAD
97Tag: (none)
98Log:
99regularly update the status line display
100 -Erik
101
102Members:
103 editors/vi.c:1.37->1.38
104
105---------------------
106PatchSet 4354
107Date: 2004/08/19 19:17:30
108Author: andersen
109Branch: HEAD
110Tag: (none)
111Log:
112Patch from Rodney Radford adding x86_64 support.
113
114Members:
115 modutils/insmod.c:1.122->1.123
116
117---------------------
118PatchSet 4355
119Date: 2004/08/25 02:02:19
120Author: bug1
121Branch: HEAD
122Tag: (none)
123Log:
124Patch from Manousaridis Angelos to cleanup stale file descriptors, it was preventing unmounting an initial filesystem.
125
126Members:
127 loginutils/getty.c:1.13->1.14
128 loginutils/login.c:1.19->1.20
129
130---------------------
131PatchSet 4356
132Date: 2004/08/26 21:45:21
133Author: andersen
134Branch: HEAD
135Tag: (none)
136Log:
137Felipe Kellermann writes:
138
139Unfortunatelly I've not followed the last two or three weeks commits (new
140semester started and so now I rarely have time to fix my personal bridge)
141but tonight I synched my tree and immediately noticed a rather nasty bug!
142
143[Using libbb/interface.c:1.24]
144# grep eth0 /proc/net/dev | xargs
145eth0:311708397 237346 1670 0 1789 1670 0 0 22580308 120297 0 0 0 102 0 0
146
147# ifconfig eth0
148eth0 Link encap:Ethernet HWaddr 00:20:AF:7C:EA:B7
149 inet addr:10.0.0.1 Bcast:10.0.0.127 Mask:255.255.255.128
150 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
151 RX packets:0 errors:0 dropped:0 overruns:0 frame:0
152 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
153 collisions:0 txqueuelen:1000
154 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
155 Interrupt:5 Base address:0x320
156
157
158All values `ifconfig' is showing are `zeroed' -- I quickly looked at the
159last commits I missed and noticed that there were a commit relating to
160ifconfig, libbb/interface.c:1.23->1.24 (PatchSet 4338).
161
162I've reversed the patch and now everything is working again. I compared
163the get_name's return values from the 1.23 and 1.24 and quickly noticed
164that the new revision is leaving `p' right on the sep while the rev 1.23
165was leaving it right on the starting of the values...
166
1671-line, 1/3-minute patch attached :-)
168
169Members:
170 libbb/interface.c:1.24->1.25
171
172---------------------
173PatchSet 4357
174Date: 2004/08/26 22:18:56
175Author: andersen
176Branch: HEAD
177Tag: (none)
178Log:
179Tito writes:
180
181Hi,
182I've spent the half night staring at the devilish my_getpwuid and my_getgrgid functions
183trying to find out a way to avoid actual and future potential buffer overflow problems
184without breaking existing code.
185Finally I've found a not intrusive way to do this that surely doesn't break existing code
186and fixes a couple of problems too.
187The attached patch:
1881) changes the behaviour of my_getpwuid and my_getgrgid to avoid potetntial buffer overflows
1892) fixes all occurences of this function calls in tar.c , id.c , ls.c, whoami.c, logger.c, libbb.h.
1903) The behaviour of tar, ls and logger is unchanged.
1914) The behavior of ps with somewhat longer usernames messing up output is fixed.
1925) The only bigger change was the increasing of size of the buffers in id.c to avoid
193 false negatives (unknown user: xxxxxx) with usernames longer than 8 chars.
194 The value i used ( 32 chars ) was taken from the tar header ( see gname and uname).
195 Maybe this buffers can be reduced a bit ( to 16 or whatever ), this is up to you.
1966) The increase of size of the binary is not so dramatic:
197 size busybox
198 text data bss dec hex filename
199 239568 2300 36816 278684 4409c busybox
200 size busybox_fixed
201 text data bss dec hex filename
202 239616 2300 36816 278732 440cc busybox
2037) The behaviour of whoami changed:
204 actually it prints out an username cut down to the size of the buffer.
205 This could be fixed by increasing the size of the buffer as in id.c or
206 avoid the use of my_getpwuid and use getpwuid directly instead.
207 Maybe this colud be also remain unchanged......
208
209Please apply if you think it is ok to do so.
210The diff applies on today's cvs tarball (2004-08-25).
211Thanks in advance,
212Ciao,
213Tito
214
215Members:
216 archival/tar.c:1.194->1.195
217 coreutils/id.c:1.24->1.25
218 coreutils/ls.c:1.110->1.111
219 coreutils/whoami.c:1.21->1.22
220 include/libbb.h:1.133->1.134
221 libbb/my_getgrgid.c:1.7->1.8
222 libbb/my_getpwuid.c:1.7->1.8
223 libbb/procps.c:1.13->1.14
224 sysklogd/logger.c:1.39->1.40
225
226---------------------
227PatchSet 4358
228Date: 2004/08/26 22:22:50
229Author: andersen
230Branch: HEAD
231Tag: (none)
232Log:
233Vladimir N. Oleynik writes:
234
235Ming-Ching,
236
237>>No. Here there are no mistakes.
238>>You using POST metod.
239>>For get data you should read from stdin CONTENT_LENGTH bytes.
240
241>Hower as I posted a little while ago, there is indeed a bug
242>in POST method if the CONTENT_LENGTH is bigger
243>than sizeof(wbuf[128]). So if your CGI script is expecting to
244>read the full CONTENT_LENGTH, it might block forever,
245>because it will only transfer sizeof(wbuf) to the CGI.
246
247Ok, Ok. I should find time to understand with a problem.
248Try attached patch.
249
250
251--w
252vodz
253
254Members:
255 networking/httpd.c:1.26->1.27
256
257---------------------
258PatchSet 4359
259Date: 2004/08/26 22:26:26
260Author: andersen
261Branch: HEAD
262Tag: (none)
263Log:
264Save a line or two
265
266Members:
267 loginutils/getty.c:1.14->1.15
268 loginutils/login.c:1.20->1.21
269
270---------------------
271PatchSet 4360
272Date: 2004/08/26 22:36:02
273Author: andersen
274Branch: HEAD
275Tag: (none)
276Log:
277Tito writes:
278
279Hi,
280I've fixed also the issue of whoami cutting down usernames.
281This time I cannot send a diff because i don't know if my previous patches will be applied
282or not, so I send in the whole file.
283The changes I've made don't affect size but ensure that usernames of whatever lenght
284are correctly displayed.
285root@localhost:/dev/pts/3:/root/Desktop/busybox/coreutils# size whoami_orig.o
286 text data bss dec hex filename
287 102 0 0 102 66 whoami_orig.o
288root@localhost:/dev/pts/3:/root/Desktop/busybox/coreutils# size whoami.o
289 text data bss dec hex filename
290 93 0 0 93 5d whoami.o
291
292This should be applied even if the other patches aren't as this matches the behaviour of the GNU whoami.
293
294Thanks in advance,
295Ciao,
296Tito
297
298Members:
299 coreutils/whoami.c:1.22->1.23
300
301---------------------
302PatchSet 4361
303Date: 2004/08/26 23:01:34
304Author: andersen
305Branch: HEAD
306Tag: (none)
307Log:
308The login applet should always be setuid root
309
310Members:
311 include/applets.h:1.114->1.115
312
313---------------------
314PatchSet 4362
315Date: 2004/08/26 23:13:00
316Author: andersen
317Branch: HEAD
318Tag: (none)
319Log:
320Improve the setuid situation a bit, and make it more apparent
321when people really ought to make busybox setuid root.
322 -Erik
323
324Members:
325 Makefile:1.296->1.297
326 loginutils/Config.in:1.8->1.9
327 miscutils/Config.in:1.18->1.19
328
329---------------------
330PatchSet 4363
331Date: 2004/08/26 23:15:29
332Author: andersen
333Branch: HEAD
334Tag: (none)
335Log:
336Togg writes:
337
338Syslogd wont start if remote-logging is enabled and the connection to the
339remote-log server is not possible on syslogd startup.
340
341I found a patch somewhere which works like a charm. It uses sendto() which
342seems more reliable for this issue.
343
344Please see attached patch. Many people will be more happy with this included
345I think.
346
347Regards,
348Togg
349
350Members:
351 sysklogd/syslogd.c:1.113->1.114
352
353---------------------
354PatchSet 4364
355Date: 2004/08/27 19:55:28
356Author: andersen
357Branch: HEAD
358Tag: (none)
359Log:
360Quiet a few warnings
361
362Members:
363 init/mesg.c:1.2->1.3
364 shell/msh.c:1.20->1.21
365
366---------------------
367PatchSet 4365
368Date: 2004/08/28 00:43:05
369Author: andersen
370Branch: HEAD
371Tag: (none)
372Log:
373Fixup some warnings
374
375Members:
376 archival/bunzip2.c:1.19->1.20
377 archival/libunarchive/decompress_bunzip2.c:1.13->1.14
378 coreutils/uniq.c:1.21->1.22
379 modutils/insmod.c:1.123->1.124
380 networking/ipcalc.c:1.10->1.11
381 util-linux/mkfs_minix.c:1.42->1.43
382
383---------------------
384PatchSet 4366
385Date: 2004/09/02 22:21:39
386Author: andersen
387Branch: HEAD
388Tag: (none)
389Log:
390Tito writes:
391
392Hi Erik,
393Hi to all,
394This is part five of the my_get*id story.
395I've tweaked a bit this two functions to make them more flexible,
396but this changes will not affect existing code.
397Now they work so:
3981) my_getpwuid( char *user, uid_t uid, int bufsize)
399
400 if bufsize is > 0 char *user cannot be set to NULL
401 on success username is written on static allocated buffer
402 on failure uid as string is written to buffer and NULL is returned
403 if bufsize is = 0 char *user can be set to NULL
404 on success username is returned
405 on failure NULL is returned
406 if bufsize is < 0 char *user can be set to NULL
407 on success username is returned
408 on failure an error message is printed and the program exits
409
410 2) 1) my_getgrgid( char *group, uid_t uid, int bufsize)
411
412 if bufsize is > 0 char *group cannot be set to NULL
413 on success groupname is written on static allocated buffer
414 on failure gid as string is written to buffer and NULL is returned
415 if bufsize is = 0 char *group can be set to NULL
416 on success groupname is returned
417 on failure NULL is returned
418 if bufsize is < 0 char *group can be set to nULL
419 on success groupname is returned
420 on failure an error message is printed and the program exits
421
422This changes were needed mainly for my new id applet.
423It is somewhat bigger then the previous but matches the behaviour of GNU id
424and is capable to handle usernames of whatever length.
425BTW: at a first look it seems to me that it will integrate well (with just a few changes)
426with the pending patch in patches/id_groups_alias.patch.
427The increase in size is balanced by the removal of my_getpwnamegid.c
428from libbb as this was used only in previous id applet and by size optimizations
429made possible in whoami.c and in passwd.c.
430I know that we are in feature freeze but I think that i've tested it enough
431(at least I hope so.......).
432
433Members:
434 coreutils/id.c:1.25->1.26
435 coreutils/whoami.c:1.23->1.24
436 include/libbb.h:1.134->1.135
437 libbb/Makefile.in:1.36->1.37
438 libbb/my_getgrgid.c:1.8->1.9
439 libbb/my_getpwuid.c:1.8->1.9
440 loginutils/passwd.c:1.7->1.8
441
442---------------------
443PatchSet 4367
444Date: 2004/09/02 22:22:16
445Author: andersen
446Branch: HEAD
447Tag: (none)
448Log:
449Tito writes:
450
451The second patch contains:
4521) a size optimization for adduser.c
4532) removes a warning about an unused variable in syslogd.c if CONFIG_FEATURE_REMOTE_LOG is not set
4543)cosmetic fixes for addgroup_full_usage and adduser_full_usage
455
456Ciao,
457Tito
458
459Members:
460 include/usage.h:1.220->1.221
461 loginutils/adduser.c:1.10->1.11
462 sysklogd/syslogd.c:1.114->1.115
463
464---------------------
465PatchSet 4368
466Date: 2004/09/02 23:03:24
467Author: andersen
468Branch: HEAD
469Tag: (none)
470Log:
471Based on patches from Mike Frysinger, add insmod support for
472sparc and ia64 (itanium).
473
474Also, reorganize the insmod architecture support code to be
475alphasorted and less messy.
476
477Update the readme to list current insmod arch support.
478
479Members:
480 README:1.35->1.36
481 modutils/insmod.c:1.124->1.125
482
483---------------------
484PatchSet 4369
485Date: 2004/09/02 23:11:52
486Author: andersen
487Branch: HEAD
488Tag: (none)
489Log:
490No longer needed
491
492Members:
493 libbb/my_getpwnamegid.c:1.7->1.8(DEAD)
494 patches/id_groups_alias.patch:1.1->1.2(DEAD)
495
496---------------------
497PatchSet 4370
498Date: 2004/09/02 23:13:10
499Author: andersen
500Branch: HEAD
501Tag: (none)
502Log:
503Jonas Holmberg from axis dot com writes:
504
505This patch makes msh handle variable expansion within backticks more
506correctly.
507
508Current behaviour (wrong):
509--------------------------
510
511BusyBox v1.00-rc3 (2004.08.26-11:51+0000) Built-in shell (msh)
512Enter 'help' for a list of built-in commands.
513
514$ A='`echo hello`'
515$ echo $A
516`echo hello`
517$ echo `echo $A`
518hello
519$
520
521
522New behaviour (correct):
523------------------------
524
525BusyBox v1.00-rc3 (2004.08.26-11:51+0000) Built-in shell (msh)
526Enter 'help' for a list of built-in commands.
527
528$ A='`echo hello`'
529$ echo $A
530`echo hello`
531$ echo `echo $A`
532`echo hello`
533$
534
535The current behaviour (wrong according to standards) was actually my
536fault. msh handles backticks by executing a subshell (which makes it
537work on MMU-less systems). Executing a subshell makes it hard to only
538expand variables once in the parent. Therefore I export all variables
539that will be expanded within the backticks and let the subshell handle
540the expansion instead.
541
542The bug was found while searching for security leaks in CGI-scripts.
543Current behaviour of msh makes it easy to expand backticks by mistake
544in $QUERY_STRING. I recommend appling the patch before release of bb
5451.00.
546
547/Jonas
548
549Members:
550 shell/msh.c:1.21->1.22
551
552---------------------
553PatchSet 4371
554Date: 2004/09/08 10:01:07
555Author: andersen
556Branch: HEAD
557Tag: (none)
558Log:
559Patrick Huesmann noticed BusyBox would not link when
560CONFIG_FEATURE_COMMAND_EDITING was defined *and*
561CONFIG_FEATURE_COMMAND_TAB_COMPLETION was undefined.
562
563Vladimir N. Oleynik writes:
564
565Its declare always, also if CONFIG_FEATURE_COMMAND_TAB_COMPLETION
566undefined.
567Patch to CVS version attached.
568
569--w
570vodz
571
572Members:
573 shell/ash.c:1.105->1.106
574
575---------------------
576PatchSet 4372
577Date: 2004/09/08 10:56:06
578Author: andersen
579Branch: HEAD
580Tag: (none)
581Log:
582Felipe Kellermann writes:
583
584The Togg's sysklogd patch to use sendto() on remote logging is formatting
585strangely (using `<' and '>' surrounding the `msg' string message). This
586is OK, but this is not the standard way of formatting this message.
587
588So this patch does the following:
589
590o Fix the formatting to the standard way.
591o Uses `MAXLINE' when needed;
592o Don't loop sending messages without a "sleeping time",
593 I'm now doing `now = 1', `now <<= 1';
594o Don't die on `init_RemoteLog' when starting up (feature!)
595 We're now trying to connect every time we have an invalid fd;
596o Removes one static uneeded variable.
597o Removes two automatic uneeded variables.
598
599Members:
600 sysklogd/syslogd.c:1.115->1.116
601
602---------------------
603PatchSet 4373
604Date: 2004/09/08 20:13:05
605Author: andersen
606Branch: HEAD
607Tag: (none)
608Log:
609Fixup URL
610
611Members:
612 docs/busybox.net/cvs_write.html:1.9->1.10
613
614---------------------
615PatchSet 4374
616Date: 2004/09/14 13:59:44
617Author: bug1
618Branch: HEAD
619Tag: (none)
620Log:
621I have to assume both Avaks and LSILogic are deliberatly ignoring me.
622
623Members:
624 docs/busybox.net/shame.html:1.18->1.19
625
626---------------------
627PatchSet 4375
628Date: 2004/09/14 16:08:02
629Author: bug1
630Branch: HEAD
631Tag: (none)
632Log:
633Patch from tito to add argument checking.
634
635Members:
636 loginutils/addgroup.c:1.12->1.13
637
638---------------------
639PatchSet 4376
640Date: 2004/09/14 16:23:56
641Author: bug1
642Branch: HEAD
643Tag: (none)
644Log:
645Patch from Felipe Kellermann, adds missing applet usage options, removes usage
646for options that are currently not implemented and fixes typos.
647
648Members:
649 include/usage.h:1.221->1.222
650
651---------------------
652PatchSet 4377
653Date: 2004/09/14 17:24:58
654Author: bug1
655Branch: HEAD
656Tag: (none)
657Log:
658Patch from Felipe Kellermann, remove some unnecessary dups, i declared a few extra const's also.
659
660Members:
661 networking/ifupdown.c:1.50->1.51
662 networking/telnet.c:1.43->1.44
663 networking/telnetd.c:1.12->1.13
664 networking/tftp.c:1.28->1.29
665 util-linux/getopt.c:1.13->1.14
666
667---------------------
668PatchSet 4378
669Date: 2004/09/14 18:12:13
670Author: bug1
671Branch: HEAD
672Tag: (none)
673Log:
674Patch by Felipe Kellermann, fix a bug introduced in the last patch by adding a condition around the remote logging, also adds some comments.
675
676Members:
677 sysklogd/syslogd.c:1.116->1.117
678
679---------------------
680PatchSet 4379
681Date: 2004/09/14 18:56:52
682Author: bug1
683Branch: HEAD
684Tag: (none)
685Log:
686Apply patch from Felipe Kellermann to simlify logic of sort functions.
687I reversed the result of the sort functions to make the big numbers go to the top.
688
689Members:
690 procps/top.c:1.12->1.13
691
692---------------------
693PatchSet 4380
694Date: 2004/09/14 19:14:00
695Author: bug1
696Branch: HEAD
697Tag: (none)
698Log:
699remove a cut/paste mistake, i better get some sleep.
700
701Members:
702 procps/top.c:1.13->1.14
703
704---------------------
705PatchSet 4381
706Date: 2004/09/15 02:05:23
707Author: bug1
708Branch: HEAD
709Tag: (none)
710Log:
711Patch by Felipe Kellermann, use the common escape handling function and remove some unused code.
712
713Members:
714 coreutils/printf.c:1.22->1.23
715
716---------------------
717PatchSet 4382
718Date: 2004/09/15 02:39:09
719Author: bug1
720Branch: HEAD
721Tag: (none)
722Log:
723Only write to shadow file is shadow passwords are enabled. Patch by magicfox modified by myself to retain check for shadow file access.
724
725Members:
726 loginutils/passwd.c:1.8->1.9
727
728---------------------
729PatchSet 4383
730Date: 2004/09/15 03:04:07
731Author: bug1
732Branch: HEAD
733Tag: (none)
734Log:
735Tito writes,
736"This patch fixes all the bugs in id previously spotted by vodz and me.
737The binary size increased a bit, but now it should work as expected."
738
739Members:
740 coreutils/id.c:1.26->1.27
741 include/libbb.h:1.135->1.136
742 libbb/Makefile.in:1.37->1.38
743 libbb/my_getgrgid.c:1.9->1.10
744 libbb/my_getpwuid.c:1.9->1.10
745 libbb/my_getug.c:INITIAL->1.1
746
747---------------------
748PatchSet 4384
749Date: 2004/09/15 03:24:32
750Author: bug1
751Branch: HEAD
752Tag: (none)
753Log:
754Add a missing brace, patch by Hideki IWAMOTO
755
756Members:
757 coreutils/stty.c:1.9->1.10
758
759---------------------
760PatchSet 4385
761Date: 2004/09/23 20:08:46
762Author: andersen
763Branch: HEAD
764Tag: (none)
765Log:
766Add some notes on how to make telnetd actually work
767
768Members:
769 networking/Config.in:1.28->1.29
770
771---------------------
772PatchSet 4386
773Date: 2004/09/24 01:25:39
774Author: andersen
775Branch: HEAD
776Tag: (none)
777Log:
778A bit of extra explanation regarding STANDALONE
779
780Members:
781 shell/Config.in:1.16->1.17
782
783---------------------
784PatchSet 4387
785Date: 2004/09/24 02:04:13
786Author: bug1
787Branch: HEAD
788Tag: (none)
789Log:
790Patch from David Daney to make the -i option work with -l.
791
792Members:
793 coreutils/ls.c:1.111->1.112
794
795---------------------
796PatchSet 4388
797Date: 2004/09/24 02:36:44
798Author: bug1
799Branch: HEAD
800Tag: (none)
801Log:
802Remove this error message at Vodz request, it was misleading.
803
804Members:
805 libbb/correct_password.c:1.4->1.5
806
807---------------------
808PatchSet 4389
809Date: 2004/09/24 09:09:44
810Author: bug1
811Branch: HEAD
812Tag: (none)
813Log:
814Fix a typo
815
816Members:
817 shell/Config.in:1.17->1.18
818
819---------------------
820PatchSet 4390
821Date: 2004/09/24 09:18:55
822Author: bug1
823Branch: HEAD
824Tag: (none)
825Log:
826Patch from Egor Duda
827Attached patch prevents modprobe from trying to call 'insmod (null)'
828whenever nonexistent module is either passed to modprobe via command
829line or mentioned in modules.dep
830
831this replaces cryptic error
832sh: Syntax error: word unexpected (expecting ")")
833with
834modprobe: module some-module not found.
835
836egor.
837
838Members:
839 modutils/modprobe.c:1.40->1.41
840
841---------------------
842PatchSet 4391
843Date: 2004/09/24 09:24:27
844Author: bug1
845Branch: HEAD
846Tag: (none)
847Log:
848Patch from Dmitry Zakharov to fix a bug triggered by freeswan's scripts.
849
850Members:
851 editors/awk.c:1.10->1.11
852
853---------------------
854PatchSet 4392
855Date: 2004/09/30 00:24:21
856Author: bug1
857Branch: HEAD
858Tag: (none)
859Log:
860Patch from William Barsse to fix a segfault when multiple files are specified.
861
862Members:
863 coreutils/tail.c:1.47->1.48
864
865---------------------
866PatchSet 4393
867Date: 2004/10/07 00:35:59
868Author: andersen
869Branch: HEAD
870Tag: (none)
871Log:
872Make it more apparent that archive creation is not supported
873
874Members:
875 archival/ar.c:1.49->1.50
876
877---------------------
878PatchSet 4394
879Date: 2004/10/08 07:21:58
880Author: andersen
881Branch: HEAD
882Tag: (none)
883Log:
884Patch from Michael Tokarev:
885
886Scenario:
887
888 touch x -- creates plain file name `x'
889 mkdir x -- exits successefully
890
891libbb/make_directory.c, bb_make_directory(), contains
892the following code:
893
894 if (mkdir(path, 0777) < 0) {
895 /* If we failed for any other reason than the directory
896 * already exists, output a diagnostic and return -1.*/
897 if (errno != EEXIST) {
898 fail_msg = "create";
899 umask(mask);
900 break;
901 }
902 /* Since the directory exists, don't attempt to change
903 * permissions if it was the full target. Note that
904 * this is not an error conditon. */
905 if (!c) {
906 umask(mask);
907 return 0;
908 }
909 }
910
911The assumption that EEXIST error is due to that the *directory*
912already exists is wrong: any file type with that name will cause
913this error to be returned. Proper way IMHO will be is to stat()
914the path and check whenever this is really a directory. Below
915(attached) is a patch to fix this issue.
916
917Members:
918 libbb/make_directory.c:1.15->1.16
919
920---------------------
921PatchSet 4395
922Date: 2004/10/08 07:45:08
923Author: andersen
924Branch: HEAD
925Tag: (none)
926Log:
927egor duda writes:
928
929Hi!
930
931I've created a patch to busybox' build system to allow building it in
932separate tree in a manner similar to kbuild from kernel version 2.6.
933
934That is, one runs command like
935'make O=/build/some/where/for/specific/target/and/options'
936and everything is built in this exact directory, provided that it exists.
937
938I understand that applyingc such invasive changes during 'release
939candidates' stage of development is at best unwise. So, i'm currently
940asking for comments about this patch, starting from whether such thing
941is needed at all to whether it coded properly.
942
943'make check' should work now, and one make creates Makefile in build
944directory, so one can run 'make' in build directory after that.
945
946One possible caveat is that if we build in some directory other than
947source one, the source directory should be 'distclean'ed first.
948
949egor
950
951Members:
952 Makefile:1.297->1.298
953 Rules.mak:1.37->1.38
954 applets/Makefile:1.5->1.6
955 applets/Makefile.in:1.5->1.6
956 archival/Makefile:1.7->1.8
957 archival/Makefile.in:1.5->1.6
958 archival/libunarchive/Makefile:1.5->1.6
959 archival/libunarchive/Makefile.in:1.23->1.24
960 console-tools/Makefile:1.4->1.5
961 console-tools/Makefile.in:1.5->1.6
962 coreutils/Makefile:1.3->1.4
963 coreutils/Makefile.in:1.9->1.10
964 coreutils/libcoreutils/Makefile:1.3->1.4
965 coreutils/libcoreutils/Makefile.in:1.3->1.4
966 debianutils/Makefile:1.3->1.4
967 debianutils/Makefile.in:1.5->1.6
968 editors/Makefile:1.4->1.5
969 editors/Makefile.in:1.5->1.6
970 findutils/Makefile:1.4->1.5
971 findutils/Makefile.in:1.4->1.5
972 init/Makefile:1.5->1.6
973 init/Makefile.in:1.9->1.10
974 libbb/Makefile:1.10->1.11
975 libbb/Makefile.in:1.38->1.39
976 libpwdgrp/Makefile:1.3->1.4
977 libpwdgrp/Makefile.in:1.4->1.5
978 loginutils/Makefile:1.3->1.4
979 loginutils/Makefile.in:1.8->1.9
980 miscutils/Makefile:1.7->1.8
981 miscutils/Makefile.in:1.12->1.13
982 modutils/Makefile:1.4->1.5
983 modutils/Makefile.in:1.3->1.4
984 networking/Makefile:1.7->1.8
985 networking/Makefile.in:1.19->1.20
986 networking/libiproute/Makefile:1.3->1.4
987 networking/libiproute/Makefile.in:1.6->1.7
988 networking/udhcp/Makefile:1.3->1.4
989 networking/udhcp/Makefile.in:1.10->1.11
990 procps/Makefile:1.4->1.5
991 procps/Makefile.in:1.6->1.7
992 scripts/config/Makefile:1.4->1.5
993 shell/Makefile:1.4->1.5
994 shell/Makefile.in:1.3->1.4
995 sysklogd/Makefile:1.5->1.6
996 sysklogd/Makefile.in:1.3->1.4
997 testsuite/runtest:1.8->1.9
998 testsuite/du/du-h-works:1.1->1.2
999 testsuite/du/du-k-works:1.1->1.2
1000 testsuite/du/du-l-works:1.1->1.2
1001 testsuite/du/du-m-works:1.1->1.2
1002 testsuite/du/du-s-works:1.1->1.2
1003 testsuite/du/du-works:1.1->1.2
1004 testsuite/head/head-n-works:1.1->1.2
1005 testsuite/head/head-works:1.1->1.2
1006 testsuite/ls/ls-1-works:1.1->1.2
1007 testsuite/ls/ls-h-works:1.1->1.2
1008 testsuite/ls/ls-l-works:1.1->1.2
1009 testsuite/ls/ls-s-works:1.1->1.2
1010 testsuite/sort/sort-n-works:1.1->1.2
1011 testsuite/sort/sort-r-works:1.1->1.2
1012 testsuite/sort/sort-works:1.1->1.2
1013 testsuite/tail/tail-n-works:1.1->1.2
1014 testsuite/tail/tail-works:1.1->1.2
1015 testsuite/xargs/xargs-works:1.1->1.2
1016 util-linux/Makefile:1.6->1.7
1017 util-linux/Makefile.in:1.8->1.9
1018
1019---------------------
1020PatchSet 4396
1021Date: 2004/10/08 07:58:30
1022Author: andersen
1023Branch: HEAD
1024Tag: (none)
1025Log:
1026As noticed by egor duda, current_menu is declared as 'extern struct menu
1027*current_menu;' in scripts/config/lkc.h line 63, and this conflicts with
1028static definition in mconf.c.
1029
1030Members:
1031 scripts/config/mconf.c:1.5->1.6
1032
1033---------------------
1034PatchSet 4397
1035Date: 2004/10/08 08:03:29
1036Author: andersen
1037Branch: HEAD
1038Tag: (none)
1039Log:
1040last_patch139.gz from Vladimir N. Oleynik:
1041
1042>I also don't mean to disagree about leaving 30x status codes until after
1043>1.0. In fact, although redirecting http://host/dir to http://host/dir/
1044>with a 301 is common practice (e.g. Apache, IIS), AFAIK it isn't
1045>actually required (or mentioned) by the HTTP specs.
1046
1047Ok.
1048Attached patch have 302 and 408 implemented features.
1049
1050
1051--w
1052vodz
1053
1054Members:
1055 networking/httpd.c:1.27->1.28
1056
1057---------------------
1058PatchSet 4398
1059Date: 2004/10/08 08:07:40
1060Author: andersen
1061Branch: HEAD
1062Tag: (none)
1063Log:
1064Tito writes:
1065
1066Hi to all,
1067This patch contains just some fixes for some misleading
1068comments in my_getpwuid.c and my_getug.c.
1069The code is untouched so this patch will not
1070cause troubles.
1071
1072Please apply.
1073
1074Thanks in advance and Ciao,
1075Tito
1076
1077Members:
1078 libbb/my_getpwuid.c:1.10->1.11
1079 libbb/my_getug.c:1.1->1.2
1080
1081---------------------
1082PatchSet 4399
1083Date: 2004/10/08 08:10:57
1084Author: andersen
1085Branch: HEAD
1086Tag: (none)
1087Log:
1088Hiroshi Ito writes:
1089
1090 Hello
1091
1092 I'm using busy box on mipsel machine.
1093
1094 "grep -f file" will cause segmentation fault.
1095
1096Vladimir N. Oleynik writes:
1097
1098Hiroshi,
1099
1100Thank for bug report, but your patch is full broken.
1101Worked patch attached.
1102(really changes is zero initialize, and indent correcting).
1103
1104
1105--w
1106vodz
1107
1108Members:
1109 findutils/grep.c:1.85->1.86
1110
1111---------------------
1112PatchSet 4400
1113Date: 2004/10/08 08:14:58
1114Author: andersen
1115Branch: HEAD
1116Tag: (none)
1117Log:
1118Hiroshi Ito writes:
1119
1120ash
1121 "unset OLDPWD; cd -" causes segmentation fault.
1122 ( OLDPWD is not set when sh is invoked from getty. )
1123
1124patch against current CVS is attached.
1125
1126Members:
1127 shell/ash.c:1.106->1.107
1128
1129---------------------
1130PatchSet 4401
1131Date: 2004/10/08 08:17:39
1132Author: andersen
1133Branch: HEAD
1134Tag: (none)
1135Log:
1136Hiroshi Ito writes:
1137
1138"kill -HUP 1" reloads inittab, and when I append one line to inittab
1139and send HUP signal two times, It will starts 2 process.
1140
1141patch against current CVS is attached.
1142
1143Members:
1144 init/init.c:1.204->1.205
1145
1146---------------------
1147PatchSet 4402
1148Date: 2004/10/08 08:21:54
1149Author: andersen
1150Branch: HEAD
1151Tag: (none)
1152Log:
1153Hiroshi Ito writes:
1154
1155Hello, all.
1156
1157Busybox init does not handle removed inittab entry correctly.
1158
1159# I'm sorry about my poor english, but you can find
1160# what I would like to say from patch, isn't it?
1161
1162even if you apply this path,
1163when yoy try to change a command line option in inittab,
1164you have to do following steps.
11651. remove old line from initrd
11662. send HUP signal to init
11673. kill old proces which is invoked from init.
11684. append new line to inittab
11695. send HUP signal to init, again
1170
1171patch is against current CVS + last patch witch I send it last.
1172
1173Members:
1174 init/init.c:1.205->1.206
1175
1176---------------------
1177PatchSet 4403
1178Date: 2004/10/08 08:27:40
1179Author: andersen
1180Branch: HEAD
1181Tag: (none)
1182Log:
1183Patch from Denis Vlasenko to fix a problem where
1184wget http://1.2.3.4/abc/ loses last '/'
1185
1186Members:
1187 networking/wget.c:1.74->1.75
1188
1189---------------------
1190PatchSet 4404
1191Date: 2004/10/08 08:49:25
1192Author: andersen
1193Branch: HEAD
1194Tag: (none)
1195Log:
1196Wade Berrier writes:
1197
1198Hello,
1199
1200Here's a patch for a first attempt at static leases for udhcpd.
1201Included in the tarball are 2 files (static_leases.c, static_leases.h)
1202and a patch against the latest cvs.
1203
1204In the config file you can configure static leases with the following
1205format:
1206
1207static_lease 00:60:08:11:CE:4E 192.168.0.54
1208static_lease 00:60:08:11:CE:3E 192.168.0.44
1209
1210Comments/suggestions/improvements are welcome.
1211
1212
1213Wade
1214
1215Members:
1216 examples/udhcp/udhcpd.conf:1.3->1.4
1217 networking/udhcp/Makefile.in:1.11->1.12
1218 networking/udhcp/dhcpd.c:1.6->1.7
1219 networking/udhcp/dhcpd.h:1.6->1.7
1220 networking/udhcp/files.c:1.14->1.15
1221 networking/udhcp/leases.c:1.6->1.7
1222 networking/udhcp/serverpacket.c:1.6->1.7
1223 networking/udhcp/static_leases.c:INITIAL->1.1
1224 networking/udhcp/static_leases.h:INITIAL->1.1
1225
1226---------------------
1227PatchSet 4405
1228Date: 2004/10/08 08:57:35
1229Author: andersen
1230Branch: HEAD
1231Tag: (none)
1232Log:
1233Patch from Claus Klein to increase, and make more apparent
1234the hard coded limit on the number of mounts
1235
1236Members:
1237 libbb/mtab.c:1.5->1.6
1238
1239---------------------
1240PatchSet 4406
1241Date: 2004/10/08 09:43:34
1242Author: andersen
1243Branch: HEAD
1244Tag: (none)
1245Log:
1246Fix CONFIG_ASH_MATH_SUPPORT_64 so it actually works
1247
1248Members:
1249 shell/ash.c:1.107->1.108
1250
1251---------------------
1252PatchSet 4407
1253Date: 2004/10/08 10:50:08
1254Author: andersen
1255Branch: HEAD
1256Tag: (none)
1257Log:
1258Add an initial FAQ
1259
1260Members:
1261 docs/busybox.net/FAQ.html:INITIAL->1.1
1262 docs/busybox.net/header.html:1.8->1.9
1263
1264---------------------
1265PatchSet 4408
1266Date: 2004/10/08 10:52:08
1267Author: andersen
1268Branch: HEAD
1269Tag: (none)
1270Log:
1271Fix the supported architectures section
1272
1273Members:
1274 README:1.36->1.37
1275
1276---------------------
1277PatchSet 4409
1278Date: 2004/10/08 10:52:33
1279Author: andersen
1280Branch: HEAD
1281Tag: (none)
1282Log:
1283Bump version
1284
1285Members:
1286 Rules.mak:1.38->1.39
1287
1288---------------------
1289PatchSet 4410
1290Date: 2004/10/08 10:54:20
1291Author: andersen
1292Branch: HEAD
1293Tag: (none)
1294Log:
1295unmerged fix
1296
1297Members:
1298 docs/busybox.net/news.html:1.22->1.23
1299
1300---------------------
1301PatchSet 4411
1302Date: 2004/10/08 11:11:02
1303Author: andersen
1304Branch: HEAD
1305Tag: (none)
1306Log:
1307oops
1308
1309Members:
1310 docs/busybox.net/FAQ.html:1.1->1.2
1311
1312---------------------
1313PatchSet 4412
1314Date: 2004/10/11 20:52:16
1315Author: andersen
1316Branch: HEAD
1317Tag: (none)
1318Log:
1319Patch from David Daney:
1320
1321It seems that date -s MMDDHHMMYYYY.ss
1322
1323will ignore the .ss part. This patch tries to fix the problem.
1324
1325David Daney.
1326
1327Members:
1328 coreutils/date.c:1.47->1.48
1329
1330---------------------
1331PatchSet 4413
1332Date: 2004/10/13 06:25:51
1333Author: andersen
1334Branch: HEAD
1335Tag: (none)
1336Log:
1337Make certain clients of bb_make_directory default to honoring
1338the user's umask
1339
1340Members:
1341 archival/libunarchive/data_extract_all.c:1.20->1.21
1342 libbb/make_directory.c:1.16->1.17
1343 miscutils/devfsd.c:1.9->1.10
1344
1345---------------------
1346PatchSet 4414
1347Date: 2004/10/13 07:18:05
1348Author: andersen
1349Branch: HEAD
1350Tag: (none)
1351Log:
1352Simon Poole writes:
1353
1354Erik,
1355
1356Attached is a patch for the udhcpc sample scripts, to correct the order in
1357which routers are applied if the DHCP server provides more than one (as per
1358section 3.5 of RFC2132).
1359
1360Apologies for not being on the mailing list and thanks for your continued
1361efforts.
1362
1363Simon.
1364
1365Members:
1366 examples/udhcp/sample.bound:1.1->1.2
1367 examples/udhcp/sample.renew:1.1->1.2
1368 examples/udhcp/simple.script:1.1->1.2
1369
1370---------------------
1371PatchSet 4415
1372Date: 2004/10/13 07:25:01
1373Author: andersen
1374Branch: HEAD
1375Tag: (none)
1376Log:
1377return failure when nslookup fails
1378
1379Members:
1380 networking/nslookup.c:1.32->1.33
1381
diff --git a/busybox/INSTALL b/busybox/INSTALL
new file mode 100644
index 000000000..c9cdf8e26
--- /dev/null
+++ b/busybox/INSTALL
@@ -0,0 +1,20 @@
11) Run 'make config' or 'make menuconfig' and select the
2 functionality that you wish to enable.
3
42) Run 'make dep'
5
63) Check the Makefile for any Makefile setting you wish
7 to adjust for your system (things like like setting
8 your cross compiler, adjusting optimizations, etc)
9
104) Run 'make'
11
125) Go get a drink of water, drink a soda, visit the bathroom,
13 or whatever while it compiles. It doesn't take very
14 long to compile, so you don't really need to waste too
15 much time waiting...
16
176) Run 'make install' or 'make PREFIX=/target install' to
18 install busybox and all the needed links. Some people
19 will prefer to install using hardlinks and will instead
20 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 @@
1 GNU GENERAL PUBLIC LICENSE
2 Version 2, June 1991
3
4 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6 Everyone is permitted to copy and distribute verbatim copies
7 of this license document, but changing it is not allowed.
8
9 Preamble
10
11 The licenses for most software are designed to take away your
12freedom to share and change it. By contrast, the GNU General Public
13License is intended to guarantee your freedom to share and change free
14software--to make sure the software is free for all its users. This
15General Public License applies to most of the Free Software
16Foundation's software and to any other program whose authors commit to
17using it. (Some other Free Software Foundation software is covered by
18the GNU Library General Public License instead.) You can apply it to
19your programs, too.
20
21 When we speak of free software, we are referring to freedom, not
22price. Our General Public Licenses are designed to make sure that you
23have the freedom to distribute copies of free software (and charge for
24this service if you wish), that you receive source code or can get it
25if you want it, that you can change the software or use pieces of it
26in new free programs; and that you know you can do these things.
27
28 To protect your rights, we need to make restrictions that forbid
29anyone to deny you these rights or to ask you to surrender the rights.
30These restrictions translate to certain responsibilities for you if you
31distribute copies of the software, or if you modify it.
32
33 For example, if you distribute copies of such a program, whether
34gratis or for a fee, you must give the recipients all the rights that
35you have. You must make sure that they, too, receive or can get the
36source code. And you must show them these terms so they know their
37rights.
38
39 We protect your rights with two steps: (1) copyright the software, and
40(2) offer you this license which gives you legal permission to copy,
41distribute and/or modify the software.
42
43 Also, for each author's protection and ours, we want to make certain
44that everyone understands that there is no warranty for this free
45software. If the software is modified by someone else and passed on, we
46want its recipients to know that what they have is not the original, so
47that any problems introduced by others will not reflect on the original
48authors' reputations.
49
50 Finally, any free program is threatened constantly by software
51patents. We wish to avoid the danger that redistributors of a free
52program will individually obtain patent licenses, in effect making the
53program proprietary. To prevent this, we have made it clear that any
54patent must be licensed for everyone's free use or not licensed at all.
55
56 The precise terms and conditions for copying, distribution and
57modification follow.
58
59 GNU GENERAL PUBLIC LICENSE
60 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
62 0. This License applies to any program or other work which contains
63a notice placed by the copyright holder saying it may be distributed
64under the terms of this General Public License. The "Program", below,
65refers to any such program or work, and a "work based on the Program"
66means either the Program or any derivative work under copyright law:
67that is to say, a work containing the Program or a portion of it,
68either verbatim or with modifications and/or translated into another
69language. (Hereinafter, translation is included without limitation in
70the term "modification".) Each licensee is addressed as "you".
71
72Activities other than copying, distribution and modification are not
73covered by this License; they are outside its scope. The act of
74running the Program is not restricted, and the output from the Program
75is covered only if its contents constitute a work based on the
76Program (independent of having been made by running the Program).
77Whether that is true depends on what the Program does.
78
79 1. You may copy and distribute verbatim copies of the Program's
80source code as you receive it, in any medium, provided that you
81conspicuously and appropriately publish on each copy an appropriate
82copyright notice and disclaimer of warranty; keep intact all the
83notices that refer to this License and to the absence of any warranty;
84and give any other recipients of the Program a copy of this License
85along with the Program.
86
87You may charge a fee for the physical act of transferring a copy, and
88you may at your option offer warranty protection in exchange for a fee.
89
90 2. You may modify your copy or copies of the Program or any portion
91of it, thus forming a work based on the Program, and copy and
92distribute such modifications or work under the terms of Section 1
93above, provided that you also meet all of these conditions:
94
95 a) You must cause the modified files to carry prominent notices
96 stating that you changed the files and the date of any change.
97
98 b) You must cause any work that you distribute or publish, that in
99 whole or in part contains or is derived from the Program or any
100 part thereof, to be licensed as a whole at no charge to all third
101 parties under the terms of this License.
102
103 c) If the modified program normally reads commands interactively
104 when run, you must cause it, when started running for such
105 interactive use in the most ordinary way, to print or display an
106 announcement including an appropriate copyright notice and a
107 notice that there is no warranty (or else, saying that you provide
108 a warranty) and that users may redistribute the program under
109 these conditions, and telling the user how to view a copy of this
110 License. (Exception: if the Program itself is interactive but
111 does not normally print such an announcement, your work based on
112 the Program is not required to print an announcement.)
113
114These requirements apply to the modified work as a whole. If
115identifiable sections of that work are not derived from the Program,
116and can be reasonably considered independent and separate works in
117themselves, then this License, and its terms, do not apply to those
118sections when you distribute them as separate works. But when you
119distribute the same sections as part of a whole which is a work based
120on the Program, the distribution of the whole must be on the terms of
121this License, whose permissions for other licensees extend to the
122entire whole, and thus to each and every part regardless of who wrote it.
123
124Thus, it is not the intent of this section to claim rights or contest
125your rights to work written entirely by you; rather, the intent is to
126exercise the right to control the distribution of derivative or
127collective works based on the Program.
128
129In addition, mere aggregation of another work not based on the Program
130with the Program (or with a work based on the Program) on a volume of
131a storage or distribution medium does not bring the other work under
132the scope of this License.
133
134 3. You may copy and distribute the Program (or a work based on it,
135under Section 2) in object code or executable form under the terms of
136Sections 1 and 2 above provided that you also do one of the following:
137
138 a) Accompany it with the complete corresponding machine-readable
139 source code, which must be distributed under the terms of Sections
140 1 and 2 above on a medium customarily used for software interchange; or,
141
142 b) Accompany it with a written offer, valid for at least three
143 years, to give any third party, for a charge no more than your
144 cost of physically performing source distribution, a complete
145 machine-readable copy of the corresponding source code, to be
146 distributed under the terms of Sections 1 and 2 above on a medium
147 customarily used for software interchange; or,
148
149 c) Accompany it with the information you received as to the offer
150 to distribute corresponding source code. (This alternative is
151 allowed only for noncommercial distribution and only if you
152 received the program in object code or executable form with such
153 an offer, in accord with Subsection b above.)
154
155The source code for a work means the preferred form of the work for
156making modifications to it. For an executable work, complete source
157code means all the source code for all modules it contains, plus any
158associated interface definition files, plus the scripts used to
159control compilation and installation of the executable. However, as a
160special exception, the source code distributed need not include
161anything that is normally distributed (in either source or binary
162form) with the major components (compiler, kernel, and so on) of the
163operating system on which the executable runs, unless that component
164itself accompanies the executable.
165
166If distribution of executable or object code is made by offering
167access to copy from a designated place, then offering equivalent
168access to copy the source code from the same place counts as
169distribution of the source code, even though third parties are not
170compelled to copy the source along with the object code.
171
172 4. You may not copy, modify, sublicense, or distribute the Program
173except as expressly provided under this License. Any attempt
174otherwise to copy, modify, sublicense or distribute the Program is
175void, and will automatically terminate your rights under this License.
176However, parties who have received copies, or rights, from you under
177this License will not have their licenses terminated so long as such
178parties remain in full compliance.
179
180 5. You are not required to accept this License, since you have not
181signed it. However, nothing else grants you permission to modify or
182distribute the Program or its derivative works. These actions are
183prohibited by law if you do not accept this License. Therefore, by
184modifying or distributing the Program (or any work based on the
185Program), you indicate your acceptance of this License to do so, and
186all its terms and conditions for copying, distributing or modifying
187the Program or works based on it.
188
189 6. Each time you redistribute the Program (or any work based on the
190Program), the recipient automatically receives a license from the
191original licensor to copy, distribute or modify the Program subject to
192these terms and conditions. You may not impose any further
193restrictions on the recipients' exercise of the rights granted herein.
194You are not responsible for enforcing compliance by third parties to
195this License.
196
197 7. If, as a consequence of a court judgment or allegation of patent
198infringement or for any other reason (not limited to patent issues),
199conditions are imposed on you (whether by court order, agreement or
200otherwise) that contradict the conditions of this License, they do not
201excuse you from the conditions of this License. If you cannot
202distribute so as to satisfy simultaneously your obligations under this
203License and any other pertinent obligations, then as a consequence you
204may not distribute the Program at all. For example, if a patent
205license would not permit royalty-free redistribution of the Program by
206all those who receive copies directly or indirectly through you, then
207the only way you could satisfy both it and this License would be to
208refrain entirely from distribution of the Program.
209
210If any portion of this section is held invalid or unenforceable under
211any particular circumstance, the balance of the section is intended to
212apply and the section as a whole is intended to apply in other
213circumstances.
214
215It is not the purpose of this section to induce you to infringe any
216patents or other property right claims or to contest validity of any
217such claims; this section has the sole purpose of protecting the
218integrity of the free software distribution system, which is
219implemented by public license practices. Many people have made
220generous contributions to the wide range of software distributed
221through that system in reliance on consistent application of that
222system; it is up to the author/donor to decide if he or she is willing
223to distribute software through any other system and a licensee cannot
224impose that choice.
225
226This section is intended to make thoroughly clear what is believed to
227be a consequence of the rest of this License.
228
229 8. If the distribution and/or use of the Program is restricted in
230certain countries either by patents or by copyrighted interfaces, the
231original copyright holder who places the Program under this License
232may add an explicit geographical distribution limitation excluding
233those countries, so that distribution is permitted only in or among
234countries not thus excluded. In such case, this License incorporates
235the limitation as if written in the body of this License.
236
237 9. The Free Software Foundation may publish revised and/or new versions
238of the General Public License from time to time. Such new versions will
239be similar in spirit to the present version, but may differ in detail to
240address new problems or concerns.
241
242Each version is given a distinguishing version number. If the Program
243specifies a version number of this License which applies to it and "any
244later version", you have the option of following the terms and conditions
245either of that version or of any later version published by the Free
246Software Foundation. If the Program does not specify a version number of
247this License, you may choose any version ever published by the Free Software
248Foundation.
249
250 10. If you wish to incorporate parts of the Program into other free
251programs whose distribution conditions are different, write to the author
252to ask for permission. For software which is copyrighted by the Free
253Software Foundation, write to the Free Software Foundation; we sometimes
254make exceptions for this. Our decision will be guided by the two goals
255of preserving the free status of all derivatives of our free software and
256of promoting the sharing and reuse of software generally.
257
258 NO WARRANTY
259
260 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268REPAIR OR CORRECTION.
269
270 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278POSSIBILITY OF SUCH DAMAGES.
279
280 END OF TERMS AND CONDITIONS
281
282 How to Apply These Terms to Your New Programs
283
284 If you develop a new program, and you want it to be of the greatest
285possible use to the public, the best way to achieve this is to make it
286free software which everyone can redistribute and change under these terms.
287
288 To do so, attach the following notices to the program. It is safest
289to attach them to the start of each source file to most effectively
290convey the exclusion of warranty; and each file should have at least
291the "copyright" line and a pointer to where the full notice is found.
292
293 <one line to give the program's name and a brief idea of what it does.>
294 Copyright (C) <year> <name of author>
295
296 This program is free software; you can redistribute it and/or modify
297 it under the terms of the GNU General Public License as published by
298 the Free Software Foundation; either version 2 of the License, or
299 (at your option) any later version.
300
301 This program is distributed in the hope that it will be useful,
302 but WITHOUT ANY WARRANTY; without even the implied warranty of
303 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 GNU General Public License for more details.
305
306 You should have received a copy of the GNU General Public License
307 along with this program; if not, write to the Free Software
308 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
309
310
311Also add information on how to contact you by electronic and paper mail.
312
313If the program is interactive, make it output a short notice like this
314when it starts in an interactive mode:
315
316 Gnomovision version 69, Copyright (C) year name of author
317 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
318 This is free software, and you are welcome to redistribute it
319 under certain conditions; type `show c' for details.
320
321The hypothetical commands `show w' and `show c' should show the appropriate
322parts of the General Public License. Of course, the commands you use may
323be called something other than `show w' and `show c'; they could even be
324mouse-clicks or menu items--whatever suits your program.
325
326You should also get your employer (if you work as a programmer) or your
327school, if any, to sign a "copyright disclaimer" for the program, if
328necessary. Here is a sample; alter the names:
329
330 Yoyodyne, Inc., hereby disclaims all copyright interest in the program
331 `Gnomovision' (which makes passes at compilers) written by James Hacker.
332
333 <signature of Ty Coon>, 1 April 1989
334 Ty Coon, President of Vice
335
336This General Public License does not permit incorporating your program into
337proprietary programs. If your program is a subroutine library, you may
338consider it more useful to permit linking proprietary applications with the
339library. If this is what you want to do, use the GNU Library General
340Public 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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20#--------------------------------------------------------------
21# You shouldn't need to mess with anything beyond this point...
22#--------------------------------------------------------------
23noconfig_targets := menuconfig config oldconfig randconfig \
24 defconfig allyesconfig allnoconfig clean distclean \
25 release tags
26
27ifndef TOPDIR
28TOPDIR=$(CURDIR)/
29endif
30ifndef top_srcdir
31top_srcdir=$(CURDIR)
32endif
33ifndef top_builddir
34top_builddir=$(CURDIR)
35endif
36
37srctree=$(top_srcdir)
38vpath %/Config.in $(srctree)
39
40include $(top_builddir)/Rules.mak
41
42DIRS:=applets archival archival/libunarchive coreutils console-tools \
43 debianutils editors findutils init miscutils modutils networking \
44 networking/libiproute networking/udhcp procps loginutils shell \
45 sysklogd util-linux libpwdgrp coreutils/libcoreutils libbb
46
47SRC_DIRS:=$(patsubst %,$(top_srcdir)/%,$(DIRS))
48
49ifeq ($(strip $(CONFIG_SELINUX)),y)
50CFLAGS += -I/usr/include/selinux
51LIBRARIES += -lsecure
52endif
53
54CONFIG_CONFIG_IN = $(top_srcdir)/sysdeps/$(TARGET_OS)/Config.in
55CONFIG_DEFCONFIG = $(top_srcdir)/sysdeps/$(TARGET_OS)/defconfig
56
57ALL_DIRS:= $(DIRS) scripts/config
58ALL_MAKEFILES:=$(patsubst %,%/Makefile,$(ALL_DIRS))
59
60ifeq ($(KBUILD_SRC),)
61
62ifdef O
63 ifeq ("$(origin O)", "command line")
64 KBUILD_OUTPUT := $(O)
65 endif
66endif
67
68# That's our default target when none is given on the command line
69.PHONY: _all
70_all:
71
72ifneq ($(KBUILD_OUTPUT),)
73# Invoke a second make in the output directory, passing relevant variables
74# check that the output directory actually exists
75saved-output := $(KBUILD_OUTPUT)
76KBUILD_OUTPUT := $(shell cd $(KBUILD_OUTPUT) && /bin/pwd)
77$(if $(wildcard $(KBUILD_OUTPUT)),, \
78 $(error output directory "$(saved-output)" does not exist))
79
80.PHONY: $(MAKECMDGOALS)
81
82$(filter-out _all,$(MAKECMDGOALS)) _all: $(KBUILD_OUTPUT)/Rules.mak $(KBUILD_OUTPUT)/Makefile
83 $(MAKE) -C $(KBUILD_OUTPUT) \
84 top_srcdir=$(CURDIR) \
85 top_builddir=$(KBUILD_OUTPUT) \
86 TOPDIR=$(KBUILD_OUTPUT) \
87 KBUILD_SRC=$(CURDIR) \
88 -f $(CURDIR)/Makefile $@
89
90$(KBUILD_OUTPUT)/Rules.mak:
91 @echo > $@
92 @echo top_srcdir=$(CURDIR) >> $@
93 @echo top_builddir=$(KBUILD_OUTPUT) >> $@
94 @echo include $(top_srcdir)/Rules.mak >> $@
95
96$(KBUILD_OUTPUT)/Makefile:
97 @echo > $@
98 @echo top_srcdir=$(CURDIR) >> $@
99 @echo top_builddir=$(KBUILD_OUTPUT) >> $@
100 @echo KBUILD_SRC='$$(top_srcdir)' >> $@
101 @echo include '$$(KBUILD_SRC)'/Makefile >> $@
102
103# Leave processing to above invocation of make
104skip-makefile := 1
105endif # ifneq ($(KBUILD_OUTPUT),)
106endif # ifeq ($(KBUILD_SRC),)
107
108ifeq ($(skip-makefile),)
109
110_all: all
111
112ifeq ($(strip $(HAVE_DOT_CONFIG)),y)
113
114all: busybox busybox.links doc
115
116all_tree: $(ALL_MAKEFILES)
117
118$(ALL_MAKEFILES): %/Makefile: $(top_srcdir)/%/Makefile
119 d=`dirname $@`; [ -d "$$d" ] || mkdir -p "$$d"; cp $< $@
120
121# In this section, we need .config
122-include $(top_builddir)/.config.cmd
123include $(patsubst %,%/Makefile.in, $(SRC_DIRS))
124-include $(top_builddir)/.depend
125
126busybox: $(ALL_MAKEFILES) .depend include/config.h $(libraries-y)
127 $(CC) $(LDFLAGS) -o $@ -Wl,--start-group $(libraries-y) $(LIBRARIES) -Wl,--end-group
128 $(STRIPCMD) $@
129
130busybox.links: $(top_srcdir)/applets/busybox.mkll include/config.h $(top_srcdir)/include/applets.h
131 - $(SHELL) $^ >$@
132
133install: applets/install.sh busybox busybox.links
134 $(SHELL) $< $(PREFIX)
135ifeq ($(strip $(CONFIG_FEATURE_SUID)),y)
136 @echo
137 @echo
138 @echo --------------------------------------------------
139 @echo You will probably need to make your busybox binary
140 @echo setuid root to ensure all configured applets will
141 @echo work properly.
142 @echo --------------------------------------------------
143 @echo
144endif
145
146uninstall: busybox.links
147 rm -f $(PREFIX)/bin/busybox
148 for i in `cat busybox.links` ; do rm -f $(PREFIX)$$i; done
149
150install-hardlinks: applets/install.sh busybox busybox.links
151 $(SHELL) $< $(PREFIX) --hardlinks
152
153check: busybox
154 bindir=$(top_builddir) srcdir=$(top_srcdir)/testsuite \
155 $(top_srcdir)/testsuite/runtest
156
157# Documentation Targets
158doc: docs/busybox.pod docs/BusyBox.txt docs/BusyBox.1 docs/BusyBox.html
159
160docs/busybox.pod : $(top_srcdir)/docs/busybox_header.pod $(top_srcdir)/include/usage.h $(top_srcdir)/docs/busybox_footer.pod
161 -mkdir -p docs
162 - ( cat $(top_srcdir)/docs/busybox_header.pod; \
163 $(top_srcdir)/docs/autodocifier.pl $(top_srcdir)/include/usage.h; \
164 cat $(top_srcdir)/docs/busybox_footer.pod ) > docs/busybox.pod
165
166docs/BusyBox.txt: docs/busybox.pod
167 @echo
168 @echo BusyBox Documentation
169 @echo
170 -mkdir -p docs
171 -pod2text $< > $@
172
173docs/BusyBox.1: docs/busybox.pod
174 - mkdir -p docs
175 - pod2man --center=BusyBox --release="version $(VERSION)" \
176 $< > $@
177
178docs/BusyBox.html: docs/busybox.net/BusyBox.html
179 - mkdir -p docs
180 -@ rm -f docs/BusyBox.html
181 -@ cp docs/busybox.net/BusyBox.html docs/BusyBox.html
182
183docs/busybox.net/BusyBox.html: docs/busybox.pod
184 -@ mkdir -p docs/busybox.net
185 - pod2html --noindex $< > \
186 docs/busybox.net/BusyBox.html
187 -@ rm -f pod2htm*
188
189# The nifty new buildsystem stuff
190scripts/mkdep: $(top_srcdir)/scripts/mkdep.c
191 $(HOSTCC) $(HOSTCFLAGS) -o $@ $<
192
193scripts/split-include: $(top_srcdir)/scripts/split-include.c
194 $(HOSTCC) $(HOSTCFLAGS) -o $@ $<
195
196.depend: scripts/mkdep
197 rm -f .depend .hdepend;
198 mkdir -p include/config;
199 scripts/mkdep -I include -- \
200 `find $(top_srcdir) -name \*.c -print | sed -e "s,^./,,"` >> .depend;
201 scripts/mkdep -I include -- \
202 `find $(top_srcdir) -name \*.h -print | sed -e "s,^./,,"` >> .hdepend;
203
204depend dep: include/config.h .depend
205
206include/config/MARKER: depend scripts/split-include
207 scripts/split-include include/config.h include/config
208 @ touch include/config/MARKER
209
210include/config.h: .config
211 @if [ ! -x $(top_builddir)/scripts/config/conf ] ; then \
212 $(MAKE) -C scripts/config conf; \
213 fi;
214 @$(top_builddir)/scripts/config/conf -o $(CONFIG_CONFIG_IN)
215
216finished2:
217 @echo
218 @echo Finished installing...
219 @echo
220
221else # ifeq ($(strip $(HAVE_DOT_CONFIG)),y)
222
223all: menuconfig
224
225# configuration
226# ---------------------------------------------------------------------------
227
228$(ALL_MAKEFILES): %/Makefile: $(top_srcdir)/%/Makefile
229 d=`dirname $@`; [ -d "$$d" ] || mkdir -p "$$d"; cp $< $@
230
231scripts/config/conf: scripts/config/Makefile Rules.mak
232 $(MAKE) -C scripts/config conf
233 -@if [ ! -f .config ] ; then \
234 cp $(CONFIG_DEFCONFIG) .config; \
235 fi
236
237scripts/config/mconf: scripts/config/Makefile Rules.mak
238 $(MAKE) -C scripts/config ncurses conf mconf
239 -@if [ ! -f .config ] ; then \
240 cp $(CONFIG_DEFCONFIG) .config; \
241 fi
242
243menuconfig: scripts/config/mconf
244 @./scripts/config/mconf $(CONFIG_CONFIG_IN)
245
246config: scripts/config/conf
247 @./scripts/config/conf $(CONFIG_CONFIG_IN)
248
249oldconfig: scripts/config/conf
250 @./scripts/config/conf -o $(CONFIG_CONFIG_IN)
251
252randconfig: scripts/config/conf
253 @./scripts/config/conf -r $(CONFIG_CONFIG_IN)
254
255allyesconfig: scripts/config/conf
256 @./scripts/config/conf -y $(CONFIG_CONFIG_IN)
257 sed -i -e "s/^CONFIG_DEBUG.*/# CONFIG_DEBUG is not set/" .config
258 sed -i -e "s/^USING_CROSS_COMPILER.*/# USING_CROSS_COMPILER is not set/" .config
259 sed -i -e "s/^CONFIG_STATIC.*/# CONFIG_STATIC is not set/" .config
260 sed -i -e "s/^CONFIG_SELINUX.*/# CONFIG_SELINUX is not set/" .config
261 @./scripts/config/conf -o $(CONFIG_CONFIG_IN)
262
263allnoconfig: scripts/config/conf
264 @./scripts/config/conf -n $(CONFIG_CONFIG_IN)
265
266defconfig: scripts/config/conf
267 @./scripts/config/conf -d $(CONFIG_CONFIG_IN)
268
269clean:
270 - rm -f docs/busybox.dvi docs/busybox.ps \
271 docs/busybox.pod docs/busybox.net/busybox.html \
272 docs/busybox pod2htm* *.gdb *.elf *~ core .*config.log \
273 docs/BusyBox.txt docs/BusyBox.1 docs/BusyBox.html \
274 docs/busybox.net/BusyBox.html busybox.links libbb/loop.h \
275 .config.old .hdepend busybox
276 - rm -rf _install
277 - find . -name .\*.flags -exec rm -f {} \;
278 - find . -name \*.o -exec rm -f {} \;
279 - find . -name \*.a -exec rm -f {} \;
280
281distclean: clean
282 - rm -f scripts/split-include scripts/mkdep
283 - rm -rf include/config include/config.h
284 - find . -name .depend -exec rm -f {} \;
285 rm -f .config .config.old .config.cmd
286 - $(MAKE) -C scripts/config clean
287
288release: distclean #doc
289 cd ..; \
290 rm -rf $(PROG)-$(VERSION); \
291 cp -a busybox $(PROG)-$(VERSION); \
292 \
293 find $(PROG)-$(VERSION)/ -type d \
294 -name CVS \
295 -print \
296 -exec rm -rf {} \; ; \
297 \
298 find $(PROG)-$(VERSION)/ -type f \
299 -name .\#* \
300 -print \
301 -exec rm -f {} \; ; \
302 \
303 tar -cvzf $(PROG)-$(VERSION).tar.gz $(PROG)-$(VERSION)/;
304
305tags:
306 ctags -R .
307
308
309endif # ifeq ($(strip $(HAVE_DOT_CONFIG)),y)
310
311endif # ifeq ($(skip-makefile),)
312
313.PHONY: dummy subdirs release distclean clean config oldconfig \
314 menuconfig tags check test depend buildtree
315
diff --git a/busybox/README b/busybox/README
new file mode 100644
index 000000000..bf2ae6f3f
--- /dev/null
+++ b/busybox/README
@@ -0,0 +1,127 @@
1Please see the LICENSE file for details on copying and usage.
2
3BusyBox combines tiny versions of many common UNIX utilities into a single
4small executable. It provides minimalist replacements for most of the utilities
5you usually find in GNU coreutils, util-linux, etc. The utilities in BusyBox
6generally have fewer options than their full-featured GNU cousins; however, the
7options that are included provide the expected functionality and behave very
8much like their GNU counterparts.
9
10BusyBox has been written with size-optimization and limited resources in mind.
11It is also extremely modular so you can easily include or exclude commands (or
12features) at compile time. This makes it easy to customize your embedded
13systems. To create a working system, just add /dev, /etc, and a Linux kernel.
14BusyBox provides a fairly complete POSIX environment for any small or embedded
15system.
16
17BusyBox is extremely configurable. This allows you to include only the
18components you need, thereby reducing binary size. Run 'make config' or
19'make menuconfig' to select the functionality that you wish to enable.
20
21After the build is complete, a busybox.links file is generated. This is
22used by 'make install' to create symlinks to the BusyBox binary for all
23compiled in functions. By default, 'make install' will place the symlink
24forest into `pwd`/_install unless you have defined the PREFIX environment
25variable (i.e., 'make PREFIX=/tmp/foo install')
26
27If you wish to install hard links, rather than symlinks, you can use
28'make PREFIX=/tmp/foo install-hardlinks' instead.
29
30----------------
31
32Supported architectures:
33
34 BusyBox in general will build on any architecture supported by gcc.
35 Kernel module loading for 2.2 and 2.4 Linux kernels is currently
36 limited to ARM, CRIS, H8/300, x86, ia64, x86_64, m68k, MIPS, PowerPC,
37 S390, SH3/4/5, Sparc, v850e, and x86_64 for 2.4.x kernels. For 2.6.x
38 kernels, kernel module loading support should work on all architectures.
39
40
41Supported C Libraries:
42
43 uClibc and glibc are supported. People have been looking at newlib and
44 dietlibc, but they are currently considered unsupported, untested, or
45 worse. Linux-libc5 is no longer supported -- you should probably use uClibc
46 instead if you want a small C library.
47
48Supported kernels:
49
50 Full functionality requires Linux 2.2.x or better. A large fraction of the
51 code should run on just about anything. While the current code is fairly
52 Linux specific, it should be fairly easy to port the majority of the code
53 to support, say, FreeBSD or Solaris, or Mac OS X, or even Windows (if you
54 are into that sort of thing).
55
56----------------
57
58Getting help:
59
60When you find you need help, you can check out the BusyBox mailing list
61archives at http://busybox.net/lists/busybox/ or even join
62the mailing list if you are interested.
63
64----------------
65
66Bugs:
67
68If you find bugs, please submit a detailed bug report to the BusyBox mailing
69list at busybox@mail.busybox.net. A well-written bug report should include a
70transcript of a shell session that demonstrates the bad behavior and enables
71anyone else to duplicate the bug on their own machine. The following is such
72an example:
73
74 To: busybox@mail.busybox.net
75 From: diligent@testing.linux.org
76 Subject: /bin/date doesn't work
77
78 Package: BusyBox
79 Version: 1.00
80
81 When I execute BusyBox 'date' it produces unexpected results.
82 With GNU date I get the following output:
83
84 $ date
85 Fri Oct 8 14:19:41 MDT 2004
86
87 But when I use BusyBox date I get this instead:
88
89 $ date
90 illegal instruction
91
92 I am using Debian unstable, kernel version 2.4.25-vrs2 on a Netwinder,
93 and the latest uClibc from CVS. Thanks for the wonderful program!
94
95 -Diligent
96
97Note the careful description and use of examples showing not only what BusyBox
98does, but also a counter example showing what an equivalent GNU app does. Bug
99reports lacking such detail may never be fixed... Thanks for understanding.
100
101----------------
102
103Downloads:
104
105Source for the latest released version, as well as daily snapshots, can always
106be downloaded from
107 http://busybox.net/downloads/
108
109----------------
110
111CVS:
112
113BusyBox now has its own publicly browsable CVS tree at:
114 http://busybox.net/cgi-bin/cvsweb/busybox/
115
116Anonymous CVS access is available. For instructions, check out:
117 http://busybox.net/cvs_anon.html
118
119For those that are actively contributing there is even CVS write access:
120 http://busybox.net/cvs_write.html
121
122----------------
123
124Please feed suggestions, bug reports, insults, and bribes back to:
125 Erik Andersen
126 <andersen@codepoet.org>
127
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 @@
1# Rules.make for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20#--------------------------------------------------------
21PROG := busybox
22VERSION := 1.00
23BUILDTIME := $(shell TZ=UTC date -u "+%Y.%m.%d-%H:%M%z")
24
25
26#--------------------------------------------------------
27# With a modern GNU make(1) (highly recommended, that's what all the
28# developers use), all of the following configuration values can be
29# overridden at the command line. For example:
30# make CROSS=powerpc-linux- BB_SRC_DIR=$HOME/busybox PREFIX=/mnt/app
31#--------------------------------------------------------
32
33# If you are running a cross compiler, you will want to set 'CROSS'
34# to something more interesting... Target architecture is determined
35# by asking the CC compiler what arch it compiles things for, so unless
36# your compiler is broken, you should not need to specify TARGET_ARCH
37CROSS =$(subst ",, $(strip $(CROSS_COMPILER_PREFIX)))
38CC = $(CROSS)gcc
39AR = $(CROSS)ar
40AS = $(CROSS)as
41LD = $(CROSS)ld
42NM = $(CROSS)nm
43STRIP = $(CROSS)strip
44CPP = $(CC) -E
45# MAKEFILES = $(top_builddir)/.config
46
47# What OS are you compiling busybox for? This allows you to include
48# OS specific things, syscall overrides, etc.
49TARGET_OS=linux
50
51# Select the compiler needed to build binaries for your development system
52HOSTCC = gcc
53HOSTCFLAGS= -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
54
55# Ensure consistent sort order, 'gcc -print-search-dirs' behavior, etc.
56LC_ALL:= C
57
58# If you want to add some simple compiler switches (like -march=i686),
59# especially from the command line, use this instead of CFLAGS directly.
60# For optimization overrides, it's better still to set OPTIMIZATION.
61CFLAGS_EXTRA=$(subst ",, $(strip $(EXTRA_CFLAGS_OPTIONS)))
62
63# If you have a "pristine" source directory, point BB_SRC_DIR to it.
64# Experimental and incomplete; tell the mailing list
65# <busybox@busybox.net> if you do or don't like it so far.
66BB_SRC_DIR=
67
68# To compile vs some other alternative libc, you may need to use/adjust
69# the following lines to meet your needs...
70#
71# If you are using Red Hat 6.x with the compatible RPMs (for developing under
72# Red Hat 5.x and glibc 2.0) uncomment the following. Be sure to read about
73# using the compatible RPMs (compat-*) at http://www.redhat.com !
74#LIBCDIR:=/usr/i386-glibc20-linux
75#
76# For other libraries, you are on your own. But these may (or may not) help...
77#LDFLAGS+=-nostdlib
78#LIBRARIES:=$(LIBCDIR)/lib/libc.a -lgcc
79#CROSS_CFLAGS+=-nostdinc -I$(LIBCDIR)/include -I$(GCCINCDIR)
80#GCCINCDIR:=$(shell gcc -print-search-dirs | sed -ne "s/install: \(.*\)/\1include/gp")
81
82WARNINGS=-Wall -Wstrict-prototypes -Wshadow
83CFLAGS=-I$(top_builddir)/include -I$(top_srcdir)/include -I$(srcdir)
84ARFLAGS=-r
85
86#--------------------------------------------------------
87export VERSION BUILDTIME TOPDIR HOSTCC HOSTCFLAGS CROSS CC AR AS LD NM STRIP CPP
88ifeq ($(strip $(TARGET_ARCH)),)
89TARGET_ARCH=$(shell $(CC) -dumpmachine | sed -e s'/-.*//' \
90 -e 's/i.86/i386/' \
91 -e 's/sparc.*/sparc/' \
92 -e 's/arm.*/arm/g' \
93 -e 's/m68k.*/m68k/' \
94 -e 's/ppc/powerpc/g' \
95 -e 's/v850.*/v850/g' \
96 -e 's/sh[234]/sh/' \
97 -e 's/mips-.*/mips/' \
98 -e 's/mipsel-.*/mipsel/' \
99 -e 's/cris.*/cris/' \
100 )
101endif
102
103# Pull in the user's busybox configuration
104ifeq ($(filter $(noconfig_targets),$(MAKECMDGOALS)),)
105-include $(top_builddir)/.config
106endif
107
108# A nifty macro to make testing gcc features easier
109check_gcc=$(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1; \
110 then echo "$(1)"; else echo "$(2)"; fi)
111
112#--------------------------------------------------------
113# Arch specific compiler optimization stuff should go here.
114# Unless you want to override the defaults, do not set anything
115# for OPTIMIZATION...
116
117# use '-Os' optimization if available, else use -O2
118OPTIMIZATION=
119OPTIMIZATION=${call check_gcc,-Os,-O2}
120
121# Some nice architecture specific optimizations
122ifeq ($(strip $(TARGET_ARCH)),arm)
123 OPTIMIZATION+=-fstrict-aliasing
124endif
125ifeq ($(strip $(TARGET_ARCH)),i386)
126 OPTIMIZATION+=$(call check_gcc,-march=i386,)
127 OPTIMIZATION+=$(call check_gcc,-mpreferred-stack-boundary=2,)
128 OPTIMIZATION+=$(call check_gcc,-falign-functions=0 -falign-jumps=0 -falign-loops=0,\
129 -malign-functions=0 -malign-jumps=0 -malign-loops=0)
130endif
131OPTIMIZATIONS=$(OPTIMIZATION) -fomit-frame-pointer
132
133#
134#--------------------------------------------------------
135# If you're going to do a lot of builds with a non-vanilla configuration,
136# it makes sense to adjust parameters above, so you can type "make"
137# by itself, instead of following it by the same half-dozen overrides
138# every time. The stuff below, on the other hand, is probably less
139# prone to casual user adjustment.
140#
141
142ifeq ($(strip $(CONFIG_LFS)),y)
143 # For large file summit support
144 CFLAGS+=-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
145endif
146ifeq ($(strip $(CONFIG_DMALLOC)),y)
147 # For testing mem leaks with dmalloc
148 CFLAGS+=-DDMALLOC
149 LIBRARIES:=-ldmalloc
150else
151 ifeq ($(strip $(CONFIG_EFENCE)),y)
152 LIBRARIES:=-lefence
153 endif
154endif
155ifeq ($(strip $(CONFIG_DEBUG)),y)
156 CFLAGS +=$(WARNINGS) -g -D_GNU_SOURCE
157 LDFLAGS +=-Wl,-warn-common
158 STRIPCMD:=/bin/true -Not_stripping_since_we_are_debugging
159else
160 CFLAGS+=$(WARNINGS) $(OPTIMIZATIONS) -D_GNU_SOURCE -DNDEBUG
161 LDFLAGS += -s -Wl,-warn-common
162 STRIPCMD:=$(STRIP) --remove-section=.note --remove-section=.comment
163endif
164ifeq ($(strip $(CONFIG_STATIC)),y)
165 LDFLAGS += --static
166endif
167
168ifeq ($(strip $(PREFIX)),)
169 PREFIX:=`pwd`/_install
170endif
171
172# Additional complications due to support for pristine source dir.
173# Include files in the build directory should take precedence over
174# the copy in BB_SRC_DIR, both during the compilation phase and the
175# shell script that finds the list of object files.
176# Work in progress by <ldoolitt@recycle.lbl.gov>.
177#
178ifneq ($(strip $(BB_SRC_DIR)),)
179 VPATH:=$(BB_SRC_DIR)
180endif
181
182OBJECTS:=$(APPLET_SOURCES:.c=.o) busybox.o usage.o applets.o
183CFLAGS += $(CROSS_CFLAGS)
184ifdef BB_INIT_SCRIPT
185 CFLAGS += -DINIT_SCRIPT='"$(BB_INIT_SCRIPT)"'
186endif
187
188# Put user-supplied flags at the end, where they
189# have a chance of winning.
190CFLAGS += $(CFLAGS_EXTRA)
191
192.PHONY: dummy
193
194
195.EXPORT_ALL_VARIABLES:
196
diff --git a/busybox/TODO b/busybox/TODO
new file mode 100644
index 000000000..ffffd4f53
--- /dev/null
+++ b/busybox/TODO
@@ -0,0 +1,11 @@
1Busybox TODO
2
3Stuff that needs to be done
4
5----
6tr - missing SuS3 features in busybox 1.0pre10
7
8tr doesnt support [:blank:], [:digit:] or other predefined classes, [=equiv=]
9support is also missing.
10----
11
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/applets
23APPLETS_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include $(srcdir)/Makefile.in
27all: $(libraries-y)
28-include $(top_builddir).depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20APPLETS_AR:=applets.a
21ifndef $(APPLETS_DIR)
22APPLETS_DIR:=$(top_builddir)/applets/
23endif
24srcdir=$(top_srcdir)/applets
25
26APPLET_SRC:=applets.c busybox.c
27APPLET_OBJ:= $(patsubst %.c,$(APPLETS_DIR)%.o, $(APPLET_SRC))
28
29libraries-y+=$(APPLETS_DIR)$(APPLETS_AR)
30
31$(APPLETS_DIR)$(APPLETS_AR): $(APPLET_OBJ)
32 $(AR) -ro $@ $(APPLET_OBJ)
33
34$(APPLET_OBJ): $(top_builddir)/.config
35$(APPLET_OBJ): $(APPLETS_DIR)%.o: $(srcdir)/%.c
36 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
37
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) tons of folks. Tracking down who wrote what
6 * isn't something I'm going to worry about... If you wrote something
7 * here, please feel free to acknowledge your work.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
24 * Permission has been granted to redistribute this code under the GPL.
25 *
26 */
27
28#include <unistd.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <assert.h>
33#include "busybox.h"
34
35const char usage_messages[] =
36
37#define MAKE_USAGE
38#include "usage.h"
39
40#include "applets.h"
41
42;
43
44#undef MAKE_USAGE
45#undef APPLET
46#undef APPLET_NOUSAGE
47#undef PROTOTYPES
48#include "applets.h"
49
50
51static struct BB_applet *applet_using;
52
53/* The -1 arises because of the {0,NULL,0,-1} entry above. */
54const size_t NUM_APPLETS = (sizeof (applets) / sizeof (struct BB_applet) - 1);
55
56
57#ifdef CONFIG_FEATURE_SUID
58
59static void check_suid (struct BB_applet *app);
60
61#ifdef CONFIG_FEATURE_SUID_CONFIG
62
63#include <sys/stat.h>
64#include <ctype.h>
65#include "pwd_.h"
66#include "grp_.h"
67
68static void parse_config_file (void);
69
70#define CONFIG_FILE "/etc/busybox.conf"
71
72/* applets [] is const, so we have to define this "override" structure */
73struct BB_suid_config
74{
75 struct BB_applet *m_applet;
76
77 uid_t m_uid;
78 gid_t m_gid;
79 mode_t m_mode;
80
81 struct BB_suid_config *m_next;
82};
83
84static struct BB_suid_config *suid_config;
85static int suid_cfg_readable;
86
87#endif /* CONFIG_FEATURE_SUID_CONFIG */
88
89#endif /* CONFIG_FEATURE_SUID */
90
91
92
93extern void
94bb_show_usage (void)
95{
96 const char *format_string;
97 const char *usage_string = usage_messages;
98 int i;
99
100 for (i = applet_using - applets; i > 0;) {
101 if (!*usage_string++) {
102 --i;
103 }
104 }
105
106 format_string = "%s\n\nUsage: %s %s\n\n";
107 if (*usage_string == '\b')
108 format_string = "%s\n\nNo help available.\n\n";
109 fprintf (stderr, format_string, bb_msg_full_version, applet_using->name,
110 usage_string);
111
112 exit (EXIT_FAILURE);
113}
114
115static int
116applet_name_compare (const void *x, const void *y)
117{
118 const char *name = x;
119 const struct BB_applet *applet = y;
120
121 return strcmp (name, applet->name);
122}
123
124extern const size_t NUM_APPLETS;
125
126struct BB_applet *
127find_applet_by_name (const char *name)
128{
129 return bsearch (name, applets, NUM_APPLETS, sizeof (struct BB_applet),
130 applet_name_compare);
131}
132
133void
134run_applet_by_name (const char *name, int argc, char **argv)
135{
136 static int recurse_level = 0;
137 extern int been_there_done_that; /* From busybox.c */
138
139#ifdef CONFIG_FEATURE_SUID_CONFIG
140 if (recurse_level == 0)
141 parse_config_file ();
142#endif
143
144 recurse_level++;
145 /* Do a binary search to find the applet entry given the name. */
146 if ((applet_using = find_applet_by_name (name)) != NULL) {
147 bb_applet_name = applet_using->name;
148 if (argv[1] && strcmp (argv[1], "--help") == 0) {
149 if (strcmp (applet_using->name, "busybox") == 0) {
150 if (argv[2])
151 applet_using = find_applet_by_name (argv[2]);
152 else
153 applet_using = NULL;
154 }
155 if (applet_using)
156 bb_show_usage ();
157 been_there_done_that = 1;
158 busybox_main (0, NULL);
159 }
160#ifdef CONFIG_FEATURE_SUID
161 check_suid (applet_using);
162#endif
163
164 exit ((*(applet_using->main)) (argc, argv));
165 }
166 /* Just in case they have renamed busybox - Check argv[1] */
167 if (recurse_level == 1) {
168 run_applet_by_name ("busybox", argc, argv);
169 }
170 recurse_level--;
171}
172
173
174#ifdef CONFIG_FEATURE_SUID
175
176#ifdef CONFIG_FEATURE_SUID_CONFIG
177
178/* check if u is member of group g */
179static int
180ingroup (uid_t u, gid_t g)
181{
182 struct group *grp = getgrgid (g);
183
184 if (grp) {
185 char **mem;
186
187 for (mem = grp->gr_mem; *mem; mem++) {
188 struct passwd *pwd = getpwnam (*mem);
189
190 if (pwd && (pwd->pw_uid == u))
191 return 1;
192 }
193 }
194 return 0;
195}
196
197#endif
198
199
200void
201check_suid (struct BB_applet *applet)
202{
203 uid_t ruid = getuid (); /* real [ug]id */
204 uid_t rgid = getgid ();
205
206#ifdef CONFIG_FEATURE_SUID_CONFIG
207 if (suid_cfg_readable) {
208 struct BB_suid_config *sct;
209
210 for (sct = suid_config; sct; sct = sct->m_next) {
211 if (sct->m_applet == applet)
212 break;
213 }
214 if (sct) {
215 mode_t m = sct->m_mode;
216
217 if (sct->m_uid == ruid) /* same uid */
218 m >>= 6;
219 else if ((sct->m_gid == rgid) || ingroup (ruid, sct->m_gid)) /* same group / in group */
220 m >>= 3;
221
222 if (!(m & S_IXOTH)) /* is x bit not set ? */
223 bb_error_msg_and_die ("You have no permission to run this applet!");
224
225 if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { /* *both* have to be set for sgid */
226 if (setegid (sct->m_gid))
227 bb_error_msg_and_die
228 ("BusyBox binary has insufficient rights to set proper GID for applet!");
229 } else
230 setgid (rgid); /* no sgid -> drop */
231
232 if (sct->m_mode & S_ISUID) {
233 if (seteuid (sct->m_uid))
234 bb_error_msg_and_die
235 ("BusyBox binary has insufficient rights to set proper UID for applet!");
236 } else
237 setuid (ruid); /* no suid -> drop */
238 } else {
239 /* default: drop all priviledges */
240 setgid (rgid);
241 setuid (ruid);
242 }
243 return;
244 } else {
245#ifndef CONFIG_FEATURE_SUID_CONFIG_QUIET
246 static int onetime = 0;
247
248 if (!onetime) {
249 onetime = 1;
250 fprintf (stderr, "Using fallback suid method\n");
251 }
252#endif
253 }
254#endif
255
256 if (applet->need_suid == _BB_SUID_ALWAYS) {
257 if (geteuid () != 0)
258 bb_error_msg_and_die ("This applet requires root priviledges!");
259 } else if (applet->need_suid == _BB_SUID_NEVER) {
260 setgid (rgid); /* drop all priviledges */
261 setuid (ruid);
262 }
263}
264
265#ifdef CONFIG_FEATURE_SUID_CONFIG
266
267/* This should probably be a libbb routine. In that case,
268 * I'd probably rename it to something like bb_trimmed_slice.
269 */
270static char *get_trimmed_slice(char *s, char *e)
271{
272 /* First, consider the value at e to be nul and back up until we
273 * reach a non-space char. Set the char after that (possibly at
274 * the original e) to nul. */
275 while (e-- > s) {
276 if (!isspace(*e)) {
277 break;
278 }
279 }
280 e[1] = 0;
281
282 /* Next, advance past all leading space and return a ptr to the
283 * first non-space char; possibly the terminating nul. */
284 return (char *) bb_skip_whitespace(s);
285}
286
287
288#define parse_error(x) { err=x; goto pe_label; }
289
290/* Don't depend on the tools to combine strings. */
291static const char config_file[] = CONFIG_FILE;
292
293/* There are 4 chars + 1 nul for each of user/group/other. */
294static const char mode_chars[] = "Ssx-\0Ssx-\0Ttx-";
295
296/* We don't supply a value for the nul, so an index adjustment is
297 * necessary below. Also, we use unsigned short here to save some
298 * space even though these are really mode_t values. */
299static const unsigned short mode_mask[] = {
300 /* SST sst xxx --- */
301 S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */
302 S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */
303 0, S_IXOTH, S_IXOTH, 0 /* other */
304};
305
306static void parse_config_file(void)
307{
308 struct BB_suid_config *sct_head;
309 struct BB_suid_config *sct;
310 struct BB_applet *applet;
311 FILE *f;
312 char *err;
313 char *s;
314 char *e;
315 int i, lc, section;
316 char buffer[256];
317 struct stat st;
318
319 assert(!suid_config); /* Should be set to NULL by bss init. */
320
321 if ((stat(config_file, &st) != 0) /* No config file? */
322 || !S_ISREG(st.st_mode) /* Not a regular file? */
323 || (st.st_uid != 0) /* Not owned by root? */
324 || (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
325 || !(f = fopen(config_file, "r")) /* Can not open? */
326 ) {
327 return;
328 }
329
330 suid_cfg_readable = 1;
331 sct_head = NULL;
332 section = lc = 0;
333
334 do {
335 s = buffer;
336
337 if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
338 if (ferror(f)) { /* Make sure it wasn't a read error. */
339 parse_error("reading");
340 }
341 fclose(f);
342 suid_config = sct_head; /* Success, so set the pointer. */
343 return;
344 }
345
346 lc++; /* Got a (partial) line. */
347
348 /* If a line is too long for our buffer, we consider it an error.
349 * The following test does mistreat one corner case though.
350 * If the final line of the file does not end with a newline and
351 * yet exactly fills the buffer, it will be treated as too long
352 * even though there isn't really a problem. But it isn't really
353 * worth adding code to deal with such an unlikely situation, and
354 * we do err on the side of caution. Besides, the line would be
355 * too long if it did end with a newline. */
356 if (!strchr(s, '\n') && !feof(f)) {
357 parse_error("line too long");
358 }
359
360 /* Trim leading and trailing whitespace, ignoring comments, and
361 * check if the resulting string is empty. */
362 if (!*(s = get_trimmed_slice(s, strchrnul(s, '#')))) {
363 continue;
364 }
365
366 /* Check for a section header. */
367
368 if (*s == '[') {
369 /* Unlike the old code, we ignore leading and trailing
370 * whitespace for the section name. We also require that
371 * there are no stray characters after the closing bracket. */
372 if (!(e = strchr(s, ']')) /* Missing right bracket? */
373 || e[1] /* Trailing characters? */
374 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
375 ) {
376 parse_error("section header");
377 }
378 /* Right now we only have one section so just check it.
379 * If more sections are added in the future, please don't
380 * resort to cascading ifs with multiple strcasecmp calls.
381 * That kind of bloated code is all too common. A loop
382 * and a string table would be a better choice unless the
383 * number of sections is very small. */
384 if (strcasecmp(s, "SUID") == 0) {
385 section = 1;
386 continue;
387 }
388 section = -1; /* Unknown section so set to skip. */
389 continue;
390 }
391
392 /* Process sections. */
393
394 if (section == 1) { /* SUID */
395 /* Since we trimmed leading and trailing space above, we're
396 * now looking for strings of the form
397 * <key>[::space::]*=[::space::]*<value>
398 * where both key and value could contain inner whitespace. */
399
400 /* First get the key (an applet name in our case). */
401 if (!!(e = strchr(s, '='))) {
402 s = get_trimmed_slice(s, e);
403 }
404 if (!e || !*s) { /* Missing '=' or empty key. */
405 parse_error("keyword");
406 }
407
408 /* Ok, we have an applet name. Process the rhs if this
409 * applet is currently built in and ignore it otherwise.
410 * Note: This can hide config file bugs which only pop
411 * up when the busybox configuration is changed. */
412 if ((applet = find_applet_by_name(s))) {
413 /* Note: We currently don't check for duplicates!
414 * The last config line for each applet will be the
415 * one used since we insert at the head of the list.
416 * I suppose this could be considered a feature. */
417 sct = xmalloc(sizeof(struct BB_suid_config));
418 sct->m_applet = applet;
419 sct->m_mode = 0;
420 sct->m_next = sct_head;
421 sct_head = sct;
422
423 /* Get the specified mode. */
424
425 e = (char *) bb_skip_whitespace(e+1);
426
427 for (i=0 ; i < 3 ; i++) {
428 const char *q;
429 if (!*(q = strchrnul(mode_chars + 5*i, *e++))) {
430 parse_error("mode");
431 }
432 /* Adjust by -i to account for nul. */
433 sct->m_mode |= mode_mask[(q - mode_chars) - i];
434 }
435
436 /* Now get the the user/group info. */
437
438 s = (char *) bb_skip_whitespace(e);
439
440 /* Note: We require whitespace between the mode and the
441 * user/group info. */
442 if ((s == e) || !(e = strchr(s, '.'))) {
443 parse_error("<uid>.<gid>");
444 }
445 *e++ = 0;
446
447 /* We can't use get_ug_id here since it would exit()
448 * if a uid or gid was not found. Oh well... */
449 {
450 char *e2;
451
452 sct->m_uid = strtoul(s, &e2, 10);
453 if (*e2 || (s == e2)) {
454 struct passwd *pwd;
455 if (!(pwd = getpwnam(s))) {
456 parse_error("user");
457 }
458 sct->m_uid = pwd->pw_uid;
459 }
460
461 sct->m_gid = strtoul(e, &e2, 10);
462 if (*e2 || (e == e2)) {
463 struct group *grp;
464 if (!(grp = getgrnam(e))) {
465 parse_error("group");
466 }
467 sct->m_gid = grp->gr_gid;
468 }
469 }
470 }
471 continue;
472 }
473
474 /* Unknown sections are ignored. */
475
476 /* Encountering configuration lines prior to seeing a
477 * section header is treated as an error. This is how
478 * the old code worked, but it may not be desirable.
479 * We may want to simply ignore such lines in case they
480 * are used in some future version of busybox. */
481 if (!section) {
482 parse_error("keyword outside section");
483 }
484
485 } while (1);
486
487 pe_label:
488 fprintf(stderr, "Parse error in %s, line %d: %s\n",
489 config_file, lc, err);
490
491 fclose(f);
492 /* Release any allocated memory before returning. */
493 while (sct_head) {
494 sct = sct_head->m_next;
495 free(sct_head);
496 sct_head = sct;
497 }
498 return;
499}
500
501#endif
502
503#endif
504
505/* END CODE */
506/*
507 Local Variables:
508 c-file-style: "linux"
509 c-basic-offset: 4
510 tab-width: 4
511End:
512*/
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 @@
1/* vi: set sw=4 ts=4: */
2#include <stdio.h>
3#include <string.h>
4#include <unistd.h>
5#include <errno.h>
6#include <stdlib.h>
7#include "busybox.h"
8#ifdef CONFIG_LOCALE_SUPPORT
9#include <locale.h>
10#endif
11
12int been_there_done_that = 0; /* Also used in applets.c */
13const char *bb_applet_name;
14
15#ifdef CONFIG_FEATURE_INSTALLER
16/*
17 * directory table
18 * this should be consistent w/ the enum, busybox.h::Location,
19 * or else...
20 */
21static const char usr_bin [] ="/usr/bin";
22static const char usr_sbin[] ="/usr/sbin";
23
24static const char* const install_dir[] = {
25 &usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
26 &usr_bin [4], /* "/bin" */
27 &usr_sbin[4], /* "/sbin" */
28 usr_bin,
29 usr_sbin
30};
31
32/* abstract link() */
33typedef int (*__link_f)(const char *, const char *);
34
35/*
36 * Where in the filesystem is this busybox?
37 * [return]
38 * malloc'd string w/ full pathname of busybox's location
39 * NULL on failure
40 */
41static inline char *busybox_fullpath(void)
42{
43 return xreadlink("/proc/self/exe");
44}
45
46/* create (sym)links for each applet */
47static void install_links(const char *busybox, int use_symbolic_links)
48{
49 __link_f Link = link;
50
51 char *fpc;
52 int i;
53 int rc;
54
55 if (use_symbolic_links)
56 Link = symlink;
57
58 for (i = 0; applets[i].name != NULL; i++) {
59 fpc = concat_path_file(
60 install_dir[applets[i].location], applets[i].name);
61 rc = Link(busybox, fpc);
62 if (rc!=0 && errno!=EEXIST) {
63 bb_perror_msg("%s", fpc);
64 }
65 free(fpc);
66 }
67}
68
69#endif /* CONFIG_FEATURE_INSTALLER */
70
71int main(int argc, char **argv)
72{
73 const char *s;
74
75 bb_applet_name = argv[0];
76
77 if (bb_applet_name[0] == '-')
78 bb_applet_name++;
79
80 for (s = bb_applet_name; *s != '\0';) {
81 if (*s++ == '/')
82 bb_applet_name = s;
83 }
84
85#ifdef CONFIG_LOCALE_SUPPORT
86#ifdef CONFIG_INIT
87 if(getpid()!=1) /* Do not set locale for `init' */
88#endif
89 {
90 setlocale(LC_ALL, "");
91 }
92#endif
93
94 run_applet_by_name(bb_applet_name, argc, argv);
95 bb_error_msg_and_die("applet not found");
96}
97
98
99int busybox_main(int argc, char **argv)
100{
101 int col = 0, len, i;
102
103#ifdef CONFIG_FEATURE_INSTALLER
104 /*
105 * This style of argument parsing doesn't scale well
106 * in the event that busybox starts wanting more --options.
107 * If someone has a cleaner approach, by all means implement it.
108 */
109 if (argc > 1 && (strcmp(argv[1], "--install") == 0)) {
110 int use_symbolic_links = 0;
111 int rc = 0;
112 char *busybox;
113
114 /* to use symlinks, or not to use symlinks... */
115 if (argc > 2) {
116 if ((strcmp(argv[2], "-s") == 0)) {
117 use_symbolic_links = 1;
118 }
119 }
120
121 /* link */
122 busybox = busybox_fullpath();
123 if (busybox) {
124 install_links(busybox, use_symbolic_links);
125 free(busybox);
126 } else {
127 rc = 1;
128 }
129 return rc;
130 }
131#endif /* CONFIG_FEATURE_INSTALLER */
132
133 argc--;
134
135 /* If we've already been here once, exit now */
136 if (been_there_done_that == 1 || argc < 1) {
137 const struct BB_applet *a = applets;
138 int output_width = 60;
139
140#ifdef CONFIG_FEATURE_AUTOWIDTH
141 /* Obtain the terminal width. */
142 get_terminal_width_height(0, &output_width, NULL);
143 /* leading tab and room to wrap */
144 output_width -= 20;
145#endif
146
147 fprintf(stderr, "%s\n\n"
148 "Usage: busybox [function] [arguments]...\n"
149 " or: [function] [arguments]...\n\n"
150 "\tBusyBox is a multi-call binary that combines many common Unix\n"
151 "\tutilities into a single executable. Most people will create a\n"
152 "\tlink to busybox for each function they wish to use, and BusyBox\n"
153 "\twill act like whatever it was invoked as.\n"
154 "\nCurrently defined functions:\n", bb_msg_full_version);
155
156 while (a->name != 0) {
157 col +=
158 fprintf(stderr, "%s%s", ((col == 0) ? "\t" : ", "),
159 (a++)->name);
160 if (col > output_width && a->name != 0) {
161 fprintf(stderr, ",\n");
162 col = 0;
163 }
164 }
165 fprintf(stderr, "\n\n");
166 exit(0);
167 }
168
169 /* Flag that we've been here already */
170 been_there_done_that = 1;
171
172 /* Move the command line down a notch */
173 /* Preserve pointers so setproctitle() works consistently */
174 len = argv[argc] + strlen(argv[argc]) - argv[1];
175 memmove(argv[0], argv[1], len);
176 memset(argv[0] + len, 0, argv[1] - argv[0]);
177
178 /* Fix up the argv pointers */
179 len = argv[1] - argv[0];
180 memmove(argv, argv + 1, sizeof(char *) * (argc + 1));
181 for (i = 0; i < argc; i++)
182 argv[i] -= len;
183
184 return (main(argc, argv));
185}
186
187/*
188Local Variables:
189c-file-style: "linux"
190c-basic-offset: 4
191tab-width: 4
192End:
193*/
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 @@
1#!/bin/sh
2# Make busybox links list file.
3
4# input $1: full path to Config.h
5# input $2: full path to applets.h
6# output (stdout): list of pathnames that should be linked to busybox
7
8# Maintainer: Larry Doolittle <ldoolitt@recycle.lbl.gov>
9
10export LC_ALL=POSIX
11export LC_CTYPE=POSIX
12
13CONFIG_H=${1:-include/config.h}
14APPLETS_H=${2:-include/applets.h}
15gcc -E -DMAKE_LINKS -include $CONFIG_H $APPLETS_H |
16 awk '/^[ \t]*LINK/{
17 dir=substr($2,8)
18 gsub("_","/",dir)
19 if(dir=="/ROOT") dir=""
20 file=$3
21 gsub("\"","",file)
22 if (file=="busybox") next
23 print tolower(dir) "/" file
24 }'
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 @@
1#!/bin/sh
2
3export LC_ALL=POSIX
4export LC_CTYPE=POSIX
5
6prefix=$1
7if [ "$prefix" = "" ]; then
8 echo "No installation directory, aborting."
9 exit 1;
10fi
11if [ "$2" = "--hardlinks" ]; then
12 linkopts="-f"
13else
14 linkopts="-fs"
15fi
16h=`sort busybox.links | uniq`
17
18
19rm -f $prefix/bin/busybox || exit 1
20mkdir -p $prefix/bin || exit 1
21install -m 755 busybox $prefix/bin/busybox || exit 1
22
23for i in $h ; do
24 appdir=`dirname $i`
25 mkdir -p $prefix/$appdir || exit 1
26 if [ "$2" = "--hardlinks" ]; then
27 bb_path="$prefix/bin/busybox"
28 else
29 case "$appdir" in
30 /)
31 bb_path="bin/busybox"
32 ;;
33 /bin)
34 bb_path="busybox"
35 ;;
36 /sbin)
37 bb_path="../bin/busybox"
38 ;;
39 /usr/bin|/usr/sbin)
40 bb_path="../../bin/busybox"
41 ;;
42 *)
43 echo "Unknown installation directory: $appdir"
44 exit 1
45 ;;
46 esac
47 fi
48 echo " $prefix$i -> $bb_path"
49 ln $linkopts $bb_path $prefix$i || exit 1
50done
51
52exit 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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Archival Utilities"
7
8config CONFIG_AR
9 bool "ar"
10 default n
11 help
12 ar is an archival utility program used to create, modify, and
13 extract contents from archives. An archive is a single file holding
14 a collection of other files in a structure that makes it possible to
15 retrieve the original individual files (called archive members).
16 The original files' contents, mode (permissions), timestamp, owner,
17 and group are preserved in the archive, and can be restored on
18 extraction.
19
20 The stored filename is limited to 15 characters. (for more information
21 see long filename support).
22 ar has 60 bytes of overheads for every stored file.
23
24 This implementation of ar can extract archives, it cannot create or
25 modify them.
26 On an x86 system, the ar applet adds about 1K.
27
28 Unless you have a specific application which requires ar, you should
29 probably say N here.
30
31config CONFIG_FEATURE_AR_LONG_FILENAMES
32 bool " Enable support for long filenames (not need for debs)"
33 default n
34 depends on CONFIG_AR
35 help
36 By default the ar format can only store the first 15 characters of the
37 filename, this option removes that limitation.
38 It supports the GNU ar long filename method which moves multiple long
39 filenames into a the data section of a new ar entry.
40
41config CONFIG_BUNZIP2
42 bool "bunzip2"
43 default n
44 help
45 bunzip2 is a compression utility using the Burrows-Wheeler block
46 sorting text compression algorithm, and Huffman coding. Compression
47 is generally considerably better than that achieved by more
48 conventional LZ77/LZ78-based compressors, and approaches the
49 performance of the PPM family of statistical compressors.
50
51 The BusyBox bunzip2 applet is limited to de-compression only.
52 On an x86 system, this applet adds about 11K.
53
54 Unless you have a specific application which requires bunzip2, you
55 should probably say N here.
56
57config CONFIG_CPIO
58 bool "cpio"
59 default n
60 help
61 cpio is an archival utility program used to create, modify, and extract
62 contents from archives.
63 cpio has 110 bytes of overheads for every stored file.
64
65 This implementation of cpio can extract cpio archives created in the
66 "newc" or "crc" format, it cannot create or modify them.
67
68 Unless you have a specific application which requires cpio, you should
69 probably say N here.
70
71config CONFIG_DPKG
72 bool "dpkg"
73 default n
74 help
75 dpkg is a medium-level tool to install, build, remove and manage Debian packages.
76
77 This implementation of dpkg has a number of limitations, you should use the
78 official dpkg if possible.
79
80config CONFIG_DPKG_DEB
81 bool "dpkg_deb"
82 default n
83 help
84 dpkg-deb packs, unpacks and provides information about Debian archives.
85
86 This implementation of dpkg-deb cannot pack archives.
87
88 Unless you have a specific application which requires dpkg-deb, you should
89 probably say N here.
90
91config CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY
92 bool " extract only (-x)"
93 default n
94 depends on CONFIG_DPKG_DEB
95 help
96 This reduces dpkg-deb to the equivalent of "ar -p <deb> data.tar.gz | tar -zx".
97 However it saves space as none of the extra dpkg-deb, ar or tar options are
98 needed, they are linked to internally.
99
100config CONFIG_GUNZIP
101 bool "gunzip"
102 default n
103 help
104 gunzip is used to decompress archives created by gzip.
105 You can use the `-t' option to test the integrity of
106 an archive, without decompressing it.
107
108config CONFIG_FEATURE_GUNZIP_UNCOMPRESS
109 bool " Uncompress support"
110 default n
111 depends on CONFIG_GUNZIP
112 help
113 Enable if you want gunzip to have the ability to decompress
114 archives created by the program compress (not much
115 used anymore).
116
117config CONFIG_GZIP
118 bool "gzip"
119 default n
120 help
121 gzip is used to compress files.
122 It's probably the most widely used UNIX compression program.
123
124config CONFIG_RPM2CPIO
125 bool "rpm2cpio"
126 default n
127 help
128 Converts an RPM file into a CPIO archive.
129
130config CONFIG_RPM
131 bool "rpm"
132 default n
133 help
134 Mini RPM applet - queries and extracts
135
136config CONFIG_TAR
137 bool "tar"
138 default n
139 help
140 tar is an archiving program. It's commonly used with gzip to
141 create compressed archives. It's probably the most widely used
142 UNIX archive program.
143
144config CONFIG_FEATURE_TAR_CREATE
145 bool " Enable archive creation"
146 default y
147 depends on CONFIG_TAR
148 help
149 If you enable this option you'll be able to create
150 tar archives using the `-c' option.
151
152config CONFIG_FEATURE_TAR_BZIP2
153 bool " Enable -j option to handle .tar.bz2 files"
154 default n
155 depends on CONFIG_TAR
156 help
157 If you enable this option you'll be able to extract
158 archives compressed with bzip2.
159
160config CONFIG_FEATURE_TAR_FROM
161 bool " Enable -X (exclude from) and -T (include from) options)"
162 default n
163 depends on CONFIG_TAR
164 help
165 If you enable this option you'll be able to specify
166 a list of files to include or exclude from an archive.
167
168config CONFIG_FEATURE_TAR_GZIP
169 bool " Enable -z option"
170 default y
171 depends on CONFIG_TAR
172 help
173 If you enable this option tar will be able to call gzip,
174 when creating or extracting tar gziped archives.
175
176config CONFIG_FEATURE_TAR_COMPRESS
177 bool " Enable -Z option"
178 default n
179 depends on CONFIG_TAR
180 help
181 If you enable this option tar will be able to call uncompress,
182 when extracting .tar.Z archives.
183
184config CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY
185 bool " Enable support for old tar header format"
186 default N
187 depends on CONFIG_TAR
188 help
189 This option is required to unpack archives created in
190 the old GNU format; help to kill this old format by
191 repacking your ancient archives with the new format.
192
193config CONFIG_FEATURE_TAR_GNU_EXTENSIONS
194 bool " Enable support for some GNU tar extensions"
195 default y
196 depends on CONFIG_TAR
197 help
198 With this option busybox supports GNU long filenames and
199 linknames.
200
201config CONFIG_FEATURE_TAR_LONG_OPTIONS
202 bool " Enable long options"
203 default n
204 depends on CONFIG_TAR
205 help
206 Enable use of long options, increases size by about 400 Bytes
207
208config CONFIG_UNCOMPRESS
209 bool "uncompress"
210 default n
211 help
212 uncompress is used to decompress archives created by compress.
213 Not much used anymore, replaced by gzip/gunzip.
214
215config CONFIG_UNZIP
216 bool "unzip"
217 default n
218 help
219 unzip will list or extract files from a ZIP archive,
220 commonly found on DOS/WIN systems. The default behavior
221 (with no options) is to extract the archive into the
222 current directory. Use the `-d' option to extract to a
223 directory of your choice.
224
225comment "Common options for cpio and tar"
226 depends on CONFIG_CPIO || CONFIG_TAR
227
228config CONFIG_FEATURE_UNARCHIVE_TAPE
229 bool " Enable tape drive support"
230 default n
231 depends on CONFIG_CPIO || CONFIG_TAR
232 help
233 I don't think this is needed anymore.
234
235comment "Common options for dpkg and dpkg_deb"
236 depends on CONFIG_DPKG || CONFIG_DPKG_DEB
237
238config CONFIG_FEATURE_DEB_TAR_GZ
239 bool " gzip debian packages (normal)"
240 default y if CONFIG_DPKG || CONFIG_DPKG_DEB
241 depends on CONFIG_DPKG || CONFIG_DPKG_DEB
242 help
243 This is the default compression method inside the debian ar file.
244
245 If you want compatibility with standard .deb's you should say yes here.
246
247config CONFIG_FEATURE_DEB_TAR_BZ2
248 bool " bzip2 debian packages"
249 default n
250 depends on CONFIG_DPKG || CONFIG_DPKG_DEB
251 help
252 This allows dpkg and dpkg-deb to extract deb's that are compressed internally
253 with bzip2 instead of gzip.
254
255 You only want this if you are creating your own custom debian packages that
256 use an internal control.tar.bz2 or data.tar.bz2.
257
258endmenu
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22ARCHIVAL_DIR:=./
23srcdir=$(top_srcdir)/archival
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include $(srcdir)/Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20ARCHIVAL_AR:=archival.a
21ifndef $(ARCHIVAL_DIR)
22ARCHIVAL_DIR:=$(top_builddir)/archival/
23endif
24srcdir=$(top_srcdir)/archival
25
26ARCHIVAL-y:=
27ARCHIVAL-$(CONFIG_APT_GET) +=
28ARCHIVAL-$(CONFIG_AR) += ar.o
29ARCHIVAL-$(CONFIG_BUNZIP2) += bunzip2.o
30ARCHIVAL-$(CONFIG_CPIO) += cpio.o
31ARCHIVAL-$(CONFIG_DPKG) += dpkg.o
32ARCHIVAL-$(CONFIG_DPKG_DEB) += dpkg_deb.o
33ARCHIVAL-$(CONFIG_GUNZIP) += gunzip.o
34ARCHIVAL-$(CONFIG_GZIP) += gzip.o
35ARCHIVAL-$(CONFIG_RPM2CPIO) += rpm2cpio.o
36ARCHIVAL-$(CONFIG_RPM) += rpm.o
37ARCHIVAL-$(CONFIG_TAR) += tar.o
38ARCHIVAL-$(CONFIG_UNCOMPRESS) += uncompress.o
39ARCHIVAL-$(CONFIG_UNZIP) += unzip.o
40
41libraries-y+=$(ARCHIVAL_DIR)$(ARCHIVAL_AR)
42
43$(ARCHIVAL_DIR)$(ARCHIVAL_AR): $(patsubst %,$(ARCHIVAL_DIR)%, $(ARCHIVAL-y))
44 $(AR) -ro $@ $(patsubst %,$(ARCHIVAL_DIR)%, $(ARCHIVAL-y))
45
46$(ARCHIVAL_DIR)%.o: $(srcdir)/%.c
47 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
48
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini ar implementation for busybox
4 *
5 * Copyright (C) 2000 by Glenn McGrath
6 * Written by Glenn McGrath <bug1@iinet.net.au> 1 June 2000
7 *
8 * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 * There is no single standard to adhere to so ar may not portable
25 * between different systems
26 * http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html
27 */
28
29#include <fcntl.h>
30#include <getopt.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <time.h>
35#include <utime.h>
36#include <unistd.h>
37
38#include "unarchive.h"
39#include "busybox.h"
40
41static void header_verbose_list_ar(const file_header_t *file_header)
42{
43 const char *mode = bb_mode_string(file_header->mode);
44 char *mtime;
45
46 mtime = ctime(&file_header->mtime);
47 mtime[16] = ' ';
48 memmove(&mtime[17], &mtime[20], 4);
49 mtime[21] = '\0';
50 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);
51}
52
53#define AR_CTX_PRINT 0x01
54#define AR_CTX_LIST 0x02
55#define AR_CTX_EXTRACT 0x04
56#define AR_OPT_PRESERVE_DATE 0x08
57#define AR_OPT_VERBOSE 0x10
58#define AR_OPT_CREATE 0x20
59
60extern int ar_main(int argc, char **argv)
61{
62 archive_handle_t *archive_handle;
63 unsigned long opt;
64 char magic[8];
65
66 archive_handle = init_handle();
67
68 bb_opt_complementaly = "p~tx:t~px:x~pt";
69 opt = bb_getopt_ulflags(argc, argv, "ptxovc");
70
71 if ((opt & 0x80000000UL) || (optind == argc)) {
72 bb_show_usage();
73 }
74
75 if (opt & AR_CTX_PRINT) {
76 archive_handle->action_data = data_extract_to_stdout;
77 }
78 if (opt & AR_CTX_LIST) {
79 archive_handle->action_header = header_list;
80 }
81 if (opt & AR_CTX_EXTRACT) {
82 archive_handle->action_data = data_extract_all;
83 }
84 if (opt & AR_OPT_PRESERVE_DATE) {
85 archive_handle->flags |= ARCHIVE_PRESERVE_DATE;
86 }
87 if (opt & AR_OPT_VERBOSE) {
88 archive_handle->action_header = header_verbose_list_ar;
89 }
90 if (opt & AR_OPT_CREATE) {
91 bb_error_msg_and_die("Archive creation not supported. Install binutils 'ar'.");
92 }
93
94 archive_handle->src_fd = bb_xopen(argv[optind++], O_RDONLY);
95
96 while (optind < argc) {
97 archive_handle->filter = filter_accept_list;
98 archive_handle->accept = llist_add_to(archive_handle->accept, argv[optind++]);
99 }
100
101 archive_xread_all(archive_handle, magic, 7);
102 if (strncmp(magic, "!<arch>", 7) != 0) {
103 bb_error_msg_and_die("Invalid ar magic");
104 }
105 archive_handle->offset += 7;
106
107 while (get_header_ar(archive_handle) == EXIT_SUCCESS);
108
109 return EXIT_SUCCESS;
110}
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 @@
1/*
2 * Modified for busybox by Glenn McGrath <bug1@iinet.net.au>
3 * Added support output to stdout by Thomas Lundquist <thomasez@zelow.no>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20#include <fcntl.h>
21#include <getopt.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26
27#include "busybox.h"
28#include "unarchive.h"
29
30#define BUNZIP2_OPT_STDOUT 1
31#define BUNZIP2_OPT_FORCE 2
32
33int bunzip2_main(int argc, char **argv)
34{
35 char *compressed_name;
36 /* Note: Ignore the warning about save_name being used uninitialized.
37 * That is not the case, but gcc has trouble working that out... */
38 char *save_name;
39 unsigned long opt;
40 int status;
41 int src_fd;
42 int dst_fd;
43
44 opt = bb_getopt_ulflags(argc, argv, "cf");
45
46 /* if called as bzcat force the stdout flag */
47 if (bb_applet_name[2] == 'c') {
48 opt |= BUNZIP2_OPT_STDOUT;
49 }
50
51 /* Set input filename and number */
52 compressed_name = argv[optind];
53 if ((compressed_name) && (compressed_name[0] != '-') && (compressed_name[1] != '\0')) {
54 /* Open input file */
55 src_fd = bb_xopen(compressed_name, O_RDONLY);
56 } else {
57 src_fd = STDIN_FILENO;
58 opt |= BUNZIP2_OPT_STDOUT;
59 }
60
61 /* Check that the input is sane. */
62 if (isatty(src_fd) && (opt & BUNZIP2_OPT_FORCE) == 0) {
63 bb_error_msg_and_die("compressed data not read from terminal. Use -f to force it.");
64 }
65
66 if (opt & BUNZIP2_OPT_STDOUT) {
67 dst_fd = STDOUT_FILENO;
68 } else {
69 int len = strlen(compressed_name) - 4;
70 if (strcmp(compressed_name + len, ".bz2") != 0) {
71 bb_error_msg_and_die("Invalid extension");
72 }
73 save_name = bb_xstrndup(compressed_name, len);
74 dst_fd = bb_xopen(save_name, O_WRONLY | O_CREAT);
75 }
76
77 status = uncompressStream(src_fd, dst_fd);
78 if(!(opt & BUNZIP2_OPT_STDOUT)) {
79 char *delete_name;
80 if (status) {
81 delete_name = save_name;
82 } else {
83 delete_name = compressed_name;
84 }
85 if (unlink(delete_name) < 0) {
86 bb_error_msg_and_die("Couldn't remove %s", delete_name);
87 }
88 }
89
90 return status;
91}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini cpio implementation for busybox
4 *
5 * Copyright (C) 2001 by Glenn McGrath
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 * Limitations:
22 * Doesn't check CRC's
23 * Only supports new ASCII and CRC formats
24 *
25 */
26#include <fcntl.h>
27#include <getopt.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31#include "unarchive.h"
32#include "busybox.h"
33
34#define CPIO_OPT_EXTRACT 0x01
35#define CPIO_OPT_TEST 0x02
36#define CPIO_OPT_UNCONDITIONAL 0x04
37#define CPIO_OPT_VERBOSE 0x08
38#define CPIO_OPT_FILE 0x10
39#define CPIO_OPT_CREATE_LEADING_DIR 0x20
40#define CPIO_OPT_PRESERVE_MTIME 0x40
41
42extern int cpio_main(int argc, char **argv)
43{
44 archive_handle_t *archive_handle;
45 char *cpio_filename = NULL;
46 unsigned long opt;
47
48 /* Initialise */
49 archive_handle = init_handle();
50 archive_handle->src_fd = STDIN_FILENO;
51 archive_handle->seek = seek_by_char;
52 archive_handle->flags = ARCHIVE_EXTRACT_NEWER | ARCHIVE_PRESERVE_DATE;
53
54 opt = bb_getopt_ulflags(argc, argv, "ituvF:dm", &cpio_filename);
55
56 /* One of either extract or test options must be given */
57 if ((opt & (CPIO_OPT_TEST | CPIO_OPT_EXTRACT)) == 0) {
58 bb_show_usage();
59 }
60
61 if (opt & CPIO_OPT_TEST) {
62 /* if both extract and test option are given, ignore extract option */
63 if (opt & CPIO_OPT_EXTRACT) {
64 opt &= ~CPIO_OPT_EXTRACT;
65 }
66 archive_handle->action_header = header_list;
67 }
68 if (opt & CPIO_OPT_EXTRACT) {
69 archive_handle->action_data = data_extract_all;
70 }
71 if (opt & CPIO_OPT_UNCONDITIONAL) {
72 archive_handle->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
73 archive_handle->flags &= ~ARCHIVE_EXTRACT_NEWER;
74 }
75 if (opt & CPIO_OPT_VERBOSE) {
76 if (archive_handle->action_header == header_list) {
77 archive_handle->action_header = header_verbose_list;
78 } else {
79 archive_handle->action_header = header_list;
80 }
81 }
82 if (cpio_filename) { /* CPIO_OPT_FILE */
83 archive_handle->src_fd = bb_xopen(cpio_filename, O_RDONLY);
84 archive_handle->seek = seek_by_jump;
85 }
86 if (opt & CPIO_OPT_CREATE_LEADING_DIR) {
87 archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS;
88 }
89
90 while (optind < argc) {
91 archive_handle->filter = filter_accept_list;
92 archive_handle->accept = llist_add_to(archive_handle->accept, argv[optind]);
93 optind++;
94 }
95
96 while (get_header_cpio(archive_handle) == EXIT_SUCCESS);
97
98 return(EXIT_SUCCESS);
99}
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 @@
1/*
2 * Mini dpkg implementation for busybox.
3 * This is not meant as a replacement for dpkg
4 *
5 * Written By Glenn McGrath with the help of others
6 * Copyright (C) 2001 by Glenn McGrath
7 *
8 * Started life as a busybox implementation of udpkg
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 */
24
25/*
26 * Known difference between busybox dpkg and the official dpkg that i don't
27 * consider important, its worth keeping a note of differences anyway, just to
28 * make it easier to maintain.
29 * - The first value for the Confflile: field isnt placed on a new line.
30 * - When installing a package the Status: field is placed at the end of the
31 * section, rather than just after the Package: field.
32 *
33 * Bugs that need to be fixed
34 * - (unknown, please let me know when you find any)
35 *
36 */
37
38#include <fcntl.h>
39#include <getopt.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43#include "unarchive.h"
44#include "busybox.h"
45
46/* NOTE: If you vary HASH_PRIME sizes be aware,
47 * 1) Tweaking these will have a big effect on how much memory this program uses.
48 * 2) For computational efficiency these hash tables should be at least 20%
49 * larger than the maximum number of elements stored in it.
50 * 3) All _HASH_PRIME's must be a prime number or chaos is assured, if your looking
51 * for a prime, try http://www.utm.edu/research/primes/lists/small/10000.txt
52 * 4) If you go bigger than 15 bits you may get into trouble (untested) as its
53 * sometimes cast to an unsigned int, if you go to 16 bit you will overlap
54 * int's and chaos is assured, 16381 is the max prime for 14 bit field
55 */
56
57/* NAME_HASH_PRIME, Stores package names and versions,
58 * I estimate it should be at least 50% bigger than PACKAGE_HASH_PRIME,
59 * as there a lot of duplicate version numbers */
60#define NAME_HASH_PRIME 16381
61char *name_hashtable[NAME_HASH_PRIME + 1];
62
63/* PACKAGE_HASH_PRIME, Maximum number of unique packages,
64 * It must not be smaller than STATUS_HASH_PRIME,
65 * Currently only packages from status_hashtable are stored in here, but in
66 * future this may be used to store packages not only from a status file,
67 * but an available_hashtable, and even multiple packages files.
68 * Package can be stored more than once if they have different versions.
69 * e.g. The same package may have different versions in the status file
70 * and available file */
71#define PACKAGE_HASH_PRIME 10007
72typedef struct edge_s {
73 unsigned int operator:3;
74 unsigned int type:4;
75 unsigned int name:14;
76 unsigned int version:14;
77} edge_t;
78
79typedef struct common_node_s {
80 unsigned int name:14;
81 unsigned int version:14;
82 unsigned int num_of_edges:14;
83 edge_t **edge;
84} common_node_t;
85common_node_t *package_hashtable[PACKAGE_HASH_PRIME + 1];
86
87/* Currently it doesnt store packages that have state-status of not-installed
88 * So it only really has to be the size of the maximum number of packages
89 * likely to be installed at any one time, so there is a bit of leeway here */
90#define STATUS_HASH_PRIME 8191
91typedef struct status_node_s {
92 unsigned int package:14; /* has to fit PACKAGE_HASH_PRIME */
93 unsigned int status:14; /* has to fit STATUS_HASH_PRIME */
94} status_node_t;
95status_node_t *status_hashtable[STATUS_HASH_PRIME + 1];
96
97/* Even numbers are for 'extras', like ored dependencies or null */
98enum edge_type_e {
99 EDGE_NULL = 0,
100 EDGE_PRE_DEPENDS = 1,
101 EDGE_OR_PRE_DEPENDS = 2,
102 EDGE_DEPENDS = 3,
103 EDGE_OR_DEPENDS = 4,
104 EDGE_REPLACES = 5,
105 EDGE_PROVIDES = 7,
106 EDGE_CONFLICTS = 9,
107 EDGE_SUGGESTS = 11,
108 EDGE_RECOMMENDS = 13,
109 EDGE_ENHANCES = 15
110};
111enum operator_e {
112 VER_NULL = 0,
113 VER_EQUAL = 1,
114 VER_LESS = 2,
115 VER_LESS_EQUAL = 3,
116 VER_MORE = 4,
117 VER_MORE_EQUAL = 5,
118 VER_ANY = 6
119};
120
121enum dpkg_opt_e {
122 dpkg_opt_purge = 1,
123 dpkg_opt_remove = 2,
124 dpkg_opt_unpack = 4,
125 dpkg_opt_configure = 8,
126 dpkg_opt_install = 16,
127 dpkg_opt_package_name = 32,
128 dpkg_opt_filename = 64,
129 dpkg_opt_list_installed = 128,
130 dpkg_opt_force_ignore_depends = 256
131};
132
133typedef struct deb_file_s {
134 char *control_file;
135 char *filename;
136 unsigned int package:14;
137} deb_file_t;
138
139
140void make_hash(const char *key, unsigned int *start, unsigned int *decrement, const int hash_prime)
141{
142 unsigned long int hash_num = key[0];
143 int len = strlen(key);
144 int i;
145
146 /* Maybe i should have uses a "proper" hashing algorithm here instead
147 * of making one up myself, seems to be working ok though. */
148 for(i = 1; i < len; i++) {
149 /* shifts the ascii based value and adds it to previous value
150 * shift amount is mod 24 because long int is 32 bit and data
151 * to be shifted is 8, don't want to shift data to where it has
152 * no effect*/
153 hash_num += ((key[i] + key[i-1]) << ((key[i] * i) % 24));
154 }
155 *start = (unsigned int) hash_num % hash_prime;
156 *decrement = (unsigned int) 1 + (hash_num % (hash_prime - 1));
157}
158
159/* this adds the key to the hash table */
160int search_name_hashtable(const char *key)
161{
162 unsigned int probe_address = 0;
163 unsigned int probe_decrement = 0;
164// char *temp;
165
166 make_hash(key, &probe_address, &probe_decrement, NAME_HASH_PRIME);
167 while(name_hashtable[probe_address] != NULL) {
168 if (strcmp(name_hashtable[probe_address], key) == 0) {
169 return(probe_address);
170 } else {
171 probe_address -= probe_decrement;
172 if ((int)probe_address < 0) {
173 probe_address += NAME_HASH_PRIME;
174 }
175 }
176 }
177 name_hashtable[probe_address] = bb_xstrdup(key);
178 return(probe_address);
179}
180
181/* this DOESNT add the key to the hashtable
182 * TODO make it consistent with search_name_hashtable
183 */
184unsigned int search_status_hashtable(const char *key)
185{
186 unsigned int probe_address = 0;
187 unsigned int probe_decrement = 0;
188
189 make_hash(key, &probe_address, &probe_decrement, STATUS_HASH_PRIME);
190 while(status_hashtable[probe_address] != NULL) {
191 if (strcmp(key, name_hashtable[package_hashtable[status_hashtable[probe_address]->package]->name]) == 0) {
192 break;
193 } else {
194 probe_address -= probe_decrement;
195 if ((int)probe_address < 0) {
196 probe_address += STATUS_HASH_PRIME;
197 }
198 }
199 }
200 return(probe_address);
201}
202
203/* Need to rethink version comparison, maybe the official dpkg has something i can use ? */
204int version_compare_part(const char *version1, const char *version2)
205{
206 int upstream_len1 = 0;
207 int upstream_len2 = 0;
208 char *name1_char;
209 char *name2_char;
210 int len1 = 0;
211 int len2 = 0;
212 int tmp_int;
213 int ver_num1;
214 int ver_num2;
215 int ret;
216
217 if (version1 == NULL) {
218 version1 = bb_xstrdup("");
219 }
220 if (version2 == NULL) {
221 version2 = bb_xstrdup("");
222 }
223 upstream_len1 = strlen(version1);
224 upstream_len2 = strlen(version2);
225
226 while ((len1 < upstream_len1) || (len2 < upstream_len2)) {
227 /* Compare non-digit section */
228 tmp_int = strcspn(&version1[len1], "0123456789");
229 name1_char = bb_xstrndup(&version1[len1], tmp_int);
230 len1 += tmp_int;
231 tmp_int = strcspn(&version2[len2], "0123456789");
232 name2_char = bb_xstrndup(&version2[len2], tmp_int);
233 len2 += tmp_int;
234 tmp_int = strcmp(name1_char, name2_char);
235 free(name1_char);
236 free(name2_char);
237 if (tmp_int != 0) {
238 ret = tmp_int;
239 goto cleanup_version_compare_part;
240 }
241
242 /* Compare digits */
243 tmp_int = strspn(&version1[len1], "0123456789");
244 name1_char = bb_xstrndup(&version1[len1], tmp_int);
245 len1 += tmp_int;
246 tmp_int = strspn(&version2[len2], "0123456789");
247 name2_char = bb_xstrndup(&version2[len2], tmp_int);
248 len2 += tmp_int;
249 ver_num1 = atoi(name1_char);
250 ver_num2 = atoi(name2_char);
251 free(name1_char);
252 free(name2_char);
253 if (ver_num1 < ver_num2) {
254 ret = -1;
255 goto cleanup_version_compare_part;
256 }
257 else if (ver_num1 > ver_num2) {
258 ret = 1;
259 goto cleanup_version_compare_part;
260 }
261 }
262 ret = 0;
263cleanup_version_compare_part:
264 return(ret);
265}
266
267/* if ver1 < ver2 return -1,
268 * if ver1 = ver2 return 0,
269 * if ver1 > ver2 return 1,
270 */
271int version_compare(const unsigned int ver1, const unsigned int ver2)
272{
273 char *ch_ver1 = name_hashtable[ver1];
274 char *ch_ver2 = name_hashtable[ver2];
275
276 char epoch1, epoch2;
277 char *deb_ver1, *deb_ver2;
278 char *ver1_ptr, *ver2_ptr;
279 char *upstream_ver1;
280 char *upstream_ver2;
281 int result;
282
283 /* Compare epoch */
284 if (ch_ver1[1] == ':') {
285 epoch1 = ch_ver1[0];
286 ver1_ptr = strchr(ch_ver1, ':') + 1;
287 } else {
288 epoch1 = '0';
289 ver1_ptr = ch_ver1;
290 }
291 if (ch_ver2[1] == ':') {
292 epoch2 = ch_ver2[0];
293 ver2_ptr = strchr(ch_ver2, ':') + 1;
294 } else {
295 epoch2 = '0';
296 ver2_ptr = ch_ver2;
297 }
298 if (epoch1 < epoch2) {
299 return(-1);
300 }
301 else if (epoch1 > epoch2) {
302 return(1);
303 }
304
305 /* Compare upstream version */
306 upstream_ver1 = bb_xstrdup(ver1_ptr);
307 upstream_ver2 = bb_xstrdup(ver2_ptr);
308
309 /* Chop off debian version, and store for later use */
310 deb_ver1 = strrchr(upstream_ver1, '-');
311 deb_ver2 = strrchr(upstream_ver2, '-');
312 if (deb_ver1) {
313 deb_ver1[0] = '\0';
314 deb_ver1++;
315 }
316 if (deb_ver2) {
317 deb_ver2[0] = '\0';
318 deb_ver2++;
319 }
320 result = version_compare_part(upstream_ver1, upstream_ver2);
321
322 free(upstream_ver1);
323 free(upstream_ver2);
324
325 if (result != 0) {
326 return(result);
327 }
328
329 /* Compare debian versions */
330 return(version_compare_part(deb_ver1, deb_ver2));
331}
332
333int test_version(const unsigned int version1, const unsigned int version2, const unsigned int operator)
334{
335 const int version_result = version_compare(version1, version2);
336 switch(operator) {
337 case (VER_ANY):
338 return(TRUE);
339 case (VER_EQUAL):
340 if (version_result == 0) {
341 return(TRUE);
342 }
343 break;
344 case (VER_LESS):
345 if (version_result < 0) {
346 return(TRUE);
347 }
348 break;
349 case (VER_LESS_EQUAL):
350 if (version_result <= 0) {
351 return(TRUE);
352 }
353 break;
354 case (VER_MORE):
355 if (version_result > 0) {
356 return(TRUE);
357 }
358 break;
359 case (VER_MORE_EQUAL):
360 if (version_result >= 0) {
361 return(TRUE);
362 }
363 break;
364 }
365 return(FALSE);
366}
367
368
369int search_package_hashtable(const unsigned int name, const unsigned int version, const unsigned int operator)
370{
371 unsigned int probe_address = 0;
372 unsigned int probe_decrement = 0;
373
374 make_hash(name_hashtable[name], &probe_address, &probe_decrement, PACKAGE_HASH_PRIME);
375 while(package_hashtable[probe_address] != NULL) {
376 if (package_hashtable[probe_address]->name == name) {
377 if (operator == VER_ANY) {
378 return(probe_address);
379 }
380 if (test_version(package_hashtable[probe_address]->version, version, operator)) {
381 return(probe_address);
382 }
383 }
384 probe_address -= probe_decrement;
385 if ((int)probe_address < 0) {
386 probe_address += PACKAGE_HASH_PRIME;
387 }
388 }
389 return(probe_address);
390}
391
392/*
393 * This function searches through the entire package_hashtable looking
394 * for a package which provides "needle". It returns the index into
395 * the package_hashtable for the providing package.
396 *
397 * needle is the index into name_hashtable of the package we are
398 * looking for.
399 *
400 * start_at is the index in the package_hashtable to start looking
401 * at. If start_at is -1 then start at the beginning. This is to allow
402 * for repeated searches since more than one package might provide
403 * needle.
404 *
405 * FIXME: I don't think this is very efficient, but I thought I'd keep
406 * it simple for now until it proves to be a problem.
407 */
408int search_for_provides(int needle, int start_at) {
409 int i, j;
410 common_node_t *p;
411 for (i = start_at + 1; i < PACKAGE_HASH_PRIME; i++) {
412 p = package_hashtable[i];
413 if ( p == NULL ) continue;
414 for(j = 0; j < p->num_of_edges; j++)
415 if ( p->edge[j]->type == EDGE_PROVIDES && p->edge[j]->name == needle )
416 return i;
417 }
418 return -1;
419}
420
421/*
422 * Add an edge to a node
423 */
424void add_edge_to_node(common_node_t *node, edge_t *edge)
425{
426 node->num_of_edges++;
427 node->edge = xrealloc(node->edge, sizeof(edge_t) * (node->num_of_edges + 1));
428 node->edge[node->num_of_edges - 1] = edge;
429}
430
431/*
432 * Create one new node and one new edge for every dependency.
433 *
434 * Dependencies which contain multiple alternatives are represented as
435 * an EDGE_OR_PRE_DEPENDS or EDGE_OR_DEPENDS node, followed by a
436 * number of EDGE_PRE_DEPENDS or EDGE_DEPENDS nodes. The name field of
437 * the OR edge contains the full dependency string while the version
438 * field contains the number of EDGE nodes which follow as part of
439 * this alternative.
440 */
441void add_split_dependencies(common_node_t *parent_node, const char *whole_line, unsigned int edge_type)
442{
443 char *line = bb_xstrdup(whole_line);
444 char *line2;
445 char *line_ptr1 = NULL;
446 char *line_ptr2 = NULL;
447 char *field;
448 char *field2;
449 char *version;
450 edge_t *edge;
451 edge_t *or_edge;
452 int offset_ch;
453
454 field = strtok_r(line, ",", &line_ptr1);
455 do {
456 /* skip leading spaces */
457 field += strspn(field, " ");
458 line2 = bb_xstrdup(field);
459 field2 = strtok_r(line2, "|", &line_ptr2);
460 if ( (edge_type == EDGE_DEPENDS || edge_type == EDGE_PRE_DEPENDS) &&
461 (strcmp(field, field2) != 0)) {
462 or_edge = (edge_t *)xmalloc(sizeof(edge_t));
463 or_edge->type = edge_type + 1;
464 } else {
465 or_edge = NULL;
466 }
467
468 if ( or_edge ) {
469 or_edge->name = search_name_hashtable(field);
470 or_edge->version = 0; // tracks the number of altenatives
471
472 add_edge_to_node(parent_node, or_edge);
473 }
474
475 do {
476 edge = (edge_t *) xmalloc(sizeof(edge_t));
477 edge->type = edge_type;
478
479 /* Skip any extra leading spaces */
480 field2 += strspn(field2, " ");
481
482 /* Get dependency version info */
483 version = strchr(field2, '(');
484 if (version == NULL) {
485 edge->operator = VER_ANY;
486 /* Get the versions hash number, adding it if the number isnt already in there */
487 edge->version = search_name_hashtable("ANY");
488 } else {
489 /* Skip leading ' ' or '(' */
490 version += strspn(field2, " ");
491 version += strspn(version, "(");
492 /* Calculate length of any operator characters */
493 offset_ch = strspn(version, "<=>");
494 /* Determine operator */
495 if (offset_ch > 0) {
496 if (strncmp(version, "=", offset_ch) == 0) {
497 edge->operator = VER_EQUAL;
498 }
499 else if (strncmp(version, "<<", offset_ch) == 0) {
500 edge->operator = VER_LESS;
501 }
502 else if (strncmp(version, "<=", offset_ch) == 0) {
503 edge->operator = VER_LESS_EQUAL;
504 }
505 else if (strncmp(version, ">>", offset_ch) == 0) {
506 edge->operator = VER_MORE;
507 }
508 else if (strncmp(version, ">=", offset_ch) == 0) {
509 edge->operator = VER_MORE_EQUAL;
510 } else {
511 bb_error_msg_and_die("Illegal operator\n");
512 }
513 }
514 /* skip to start of version numbers */
515 version += offset_ch;
516 version += strspn(version, " ");
517
518 /* Truncate version at trailing ' ' or ')' */
519 version[strcspn(version, " )")] = '\0';
520 /* Get the versions hash number, adding it if the number isnt already in there */
521 edge->version = search_name_hashtable(version);
522 }
523
524 /* Get the dependency name */
525 field2[strcspn(field2, " (")] = '\0';
526 edge->name = search_name_hashtable(field2);
527
528 if ( or_edge )
529 or_edge->version++;
530
531 add_edge_to_node(parent_node, edge);
532 } while ((field2 = strtok_r(NULL, "|", &line_ptr2)) != NULL);
533 free(line2);
534 } while ((field = strtok_r(NULL, ",", &line_ptr1)) != NULL);
535 free(line);
536
537 return;
538}
539
540void free_package(common_node_t *node)
541{
542 unsigned short i;
543 if (node) {
544 for (i = 0; i < node->num_of_edges; i++) {
545 free(node->edge[i]);
546 }
547 if ( node->edge )
548 free(node->edge);
549 free(node);
550 }
551}
552
553unsigned int fill_package_struct(char *control_buffer)
554{
555 common_node_t *new_node = (common_node_t *) xcalloc(1, sizeof(common_node_t));
556 const char *field_names[] = { "Package", "Version", "Pre-Depends", "Depends",
557 "Replaces", "Provides", "Conflicts", "Suggests", "Recommends", "Enhances", 0};
558 char *field_name;
559 char *field_value;
560 int field_start = 0;
561 int num = -1;
562 int buffer_length = strlen(control_buffer);
563
564 new_node->version = search_name_hashtable("unknown");
565 while (field_start < buffer_length) {
566 unsigned short field_num;
567
568 field_start += read_package_field(&control_buffer[field_start],
569 &field_name, &field_value);
570
571 if (field_name == NULL) {
572 goto fill_package_struct_cleanup; /* Oh no, the dreaded goto statement ! */
573 }
574
575 field_num = compare_string_array(field_names, field_name);
576 switch(field_num) {
577 case 0: /* Package */
578 new_node->name = search_name_hashtable(field_value);
579 break;
580 case 1: /* Version */
581 new_node->version = search_name_hashtable(field_value);
582 break;
583 case 2: /* Pre-Depends */
584 add_split_dependencies(new_node, field_value, EDGE_PRE_DEPENDS);
585 break;
586 case 3: /* Depends */
587 add_split_dependencies(new_node, field_value, EDGE_DEPENDS);
588 break;
589 case 4: /* Replaces */
590 add_split_dependencies(new_node, field_value, EDGE_REPLACES);
591 break;
592 case 5: /* Provides */
593 add_split_dependencies(new_node, field_value, EDGE_PROVIDES);
594 break;
595 case 6: /* Conflicts */
596 add_split_dependencies(new_node, field_value, EDGE_CONFLICTS);
597 break;
598 case 7: /* Suggests */
599 add_split_dependencies(new_node, field_value, EDGE_SUGGESTS);
600 break;
601 case 8: /* Recommends */
602 add_split_dependencies(new_node, field_value, EDGE_RECOMMENDS);
603 break;
604 case 9: /* Enhances */
605 add_split_dependencies(new_node, field_value, EDGE_ENHANCES);
606 break;
607 }
608fill_package_struct_cleanup:
609 free(field_name);
610 free(field_value);
611 }
612
613 if (new_node->version == search_name_hashtable("unknown")) {
614 free_package(new_node);
615 return(-1);
616 }
617 num = search_package_hashtable(new_node->name, new_node->version, VER_EQUAL);
618 if (package_hashtable[num] == NULL) {
619 package_hashtable[num] = new_node;
620 } else {
621 free_package(new_node);
622 }
623 return(num);
624}
625
626/* if num = 1, it returns the want status, 2 returns flag, 3 returns status */
627unsigned int get_status(const unsigned int status_node, const int num)
628{
629 char *status_string = name_hashtable[status_hashtable[status_node]->status];
630 char *state_sub_string;
631 unsigned int state_sub_num;
632 int len;
633 int i;
634
635 /* set tmp_string to point to the start of the word number */
636 for (i = 1; i < num; i++) {
637 /* skip past a word */
638 status_string += strcspn(status_string, " ");
639 /* skip past the separating spaces */
640 status_string += strspn(status_string, " ");
641 }
642 len = strcspn(status_string, " \n\0");
643 state_sub_string = bb_xstrndup(status_string, len);
644 state_sub_num = search_name_hashtable(state_sub_string);
645 free(state_sub_string);
646 return(state_sub_num);
647}
648
649void set_status(const unsigned int status_node_num, const char *new_value, const int position)
650{
651 const unsigned int new_value_len = strlen(new_value);
652 const unsigned int new_value_num = search_name_hashtable(new_value);
653 unsigned int want = get_status(status_node_num, 1);
654 unsigned int flag = get_status(status_node_num, 2);
655 unsigned int status = get_status(status_node_num, 3);
656 int want_len = strlen(name_hashtable[want]);
657 int flag_len = strlen(name_hashtable[flag]);
658 int status_len = strlen(name_hashtable[status]);
659 char *new_status;
660
661 switch (position) {
662 case (1):
663 want = new_value_num;
664 want_len = new_value_len;
665 break;
666 case (2):
667 flag = new_value_num;
668 flag_len = new_value_len;
669 break;
670 case (3):
671 status = new_value_num;
672 status_len = new_value_len;
673 break;
674 default:
675 bb_error_msg_and_die("DEBUG ONLY: this shouldnt happen");
676 }
677
678 new_status = (char *) xmalloc(want_len + flag_len + status_len + 3);
679 sprintf(new_status, "%s %s %s", name_hashtable[want], name_hashtable[flag], name_hashtable[status]);
680 status_hashtable[status_node_num]->status = search_name_hashtable(new_status);
681 free(new_status);
682 return;
683}
684
685const char *describe_status(int status_num) {
686 int status_want, status_state ;
687 if ( status_hashtable[status_num] == NULL || status_hashtable[status_num]->status == 0 )
688 return "is not installed or flagged to be installed\n";
689
690 status_want = get_status(status_num, 1);
691 status_state = get_status(status_num, 3);
692
693 if ( status_state == search_name_hashtable("installed") ) {
694 if ( status_want == search_name_hashtable("install") )
695 return "is installed";
696 if ( status_want == search_name_hashtable("deinstall") )
697 return "is marked to be removed";
698 if ( status_want == search_name_hashtable("purge") )
699 return "is marked to be purged";
700 }
701 if ( status_want == search_name_hashtable("unknown") )
702 return "is in an indeterminate state";
703 if ( status_want == search_name_hashtable("install") )
704 return "is marked to be installed";
705
706 return "is not installed or flagged to be installed";
707}
708
709
710void index_status_file(const char *filename)
711{
712 FILE *status_file;
713 char *control_buffer;
714 char *status_line;
715 status_node_t *status_node = NULL;
716 unsigned int status_num;
717
718 status_file = bb_xfopen(filename, "r");
719 while ((control_buffer = fgets_str(status_file, "\n\n")) != NULL) {
720 const unsigned int package_num = fill_package_struct(control_buffer);
721 if (package_num != -1) {
722 status_node = xmalloc(sizeof(status_node_t));
723 /* fill_package_struct doesnt handle the status field */
724 status_line = strstr(control_buffer, "Status:");
725 if (status_line != NULL) {
726 status_line += 7;
727 status_line += strspn(status_line, " \n\t");
728 status_line = bb_xstrndup(status_line, strcspn(status_line, "\n\0"));
729 status_node->status = search_name_hashtable(status_line);
730 free(status_line);
731 }
732 status_node->package = package_num;
733 status_num = search_status_hashtable(name_hashtable[package_hashtable[status_node->package]->name]);
734 status_hashtable[status_num] = status_node;
735 }
736 free(control_buffer);
737 }
738 fclose(status_file);
739 return;
740}
741
742#if 0 /* this code is no longer used */
743char *get_depends_field(common_node_t *package, const int depends_type)
744{
745 char *depends = NULL;
746 char *old_sep = (char *)xcalloc(1, 3);
747 char *new_sep = (char *)xcalloc(1, 3);
748 int line_size = 0;
749 int depends_size;
750
751 int i;
752
753 for (i = 0; i < package->num_of_edges; i++) {
754 if ((package->edge[i]->type == EDGE_OR_PRE_DEPENDS) ||
755 (package->edge[i]->type == EDGE_OR_DEPENDS)) {
756 }
757
758 if ((package->edge[i]->type == depends_type) ||
759 (package->edge[i]->type == depends_type + 1)) {
760 /* Check if its the first time through */
761
762 depends_size = 8 + strlen(name_hashtable[package->edge[i]->name])
763 + strlen(name_hashtable[package->edge[i]->version]);
764 line_size += depends_size;
765 depends = (char *) xrealloc(depends, line_size + 1);
766
767 /* Check to see if this dependency is the type we are looking for
768 * +1 to check for 'extra' types, e.g. ored dependecies */
769 strcpy(old_sep, new_sep);
770 if (package->edge[i]->type == depends_type) {
771 strcpy(new_sep, ", ");
772 }
773 else if (package->edge[i]->type == depends_type + 1) {
774 strcpy(new_sep, "| ");
775 }
776
777 if (depends_size == line_size) {
778 strcpy(depends, "");
779 } else {
780 if ((strcmp(old_sep, "| ") == 0) && (strcmp(new_sep, "| ") == 0)) {
781 strcat(depends, " | ");
782 } else {
783 strcat(depends, ", ");
784 }
785 }
786
787 strcat(depends, name_hashtable[package->edge[i]->name]);
788 if (strcmp(name_hashtable[package->edge[i]->version], "NULL") != 0) {
789 if (package->edge[i]->operator == VER_EQUAL) {
790 strcat(depends, " (= ");
791 }
792 else if (package->edge[i]->operator == VER_LESS) {
793 strcat(depends, " (<< ");
794 }
795 else if (package->edge[i]->operator == VER_LESS_EQUAL) {
796 strcat(depends, " (<= ");
797 }
798 else if (package->edge[i]->operator == VER_MORE) {
799 strcat(depends, " (>> ");
800 }
801 else if (package->edge[i]->operator == VER_MORE_EQUAL) {
802 strcat(depends, " (>= ");
803 } else {
804 strcat(depends, " (");
805 }
806 strcat(depends, name_hashtable[package->edge[i]->version]);
807 strcat(depends, ")");
808 }
809 }
810 }
811 return(depends);
812}
813#endif
814
815void write_buffer_no_status(FILE *new_status_file, const char *control_buffer)
816{
817 char *name;
818 char *value;
819 int start = 0;
820 while (1) {
821 start += read_package_field(&control_buffer[start], &name, &value);
822 if (name == NULL) {
823 break;
824 }
825 if (strcmp(name, "Status") != 0) {
826 fprintf(new_status_file, "%s: %s\n", name, value);
827 }
828 }
829 return;
830}
831
832/* This could do with a cleanup */
833void write_status_file(deb_file_t **deb_file)
834{
835 FILE *old_status_file = bb_xfopen("/var/lib/dpkg/status", "r");
836 FILE *new_status_file = bb_xfopen("/var/lib/dpkg/status.udeb", "w");
837 char *package_name;
838 char *status_from_file;
839 char *control_buffer = NULL;
840 char *tmp_string;
841 int status_num;
842 int field_start = 0;
843 int write_flag;
844 int i = 0;
845
846 /* Update previously known packages */
847 while ((control_buffer = fgets_str(old_status_file, "\n\n")) != NULL) {
848 if ((tmp_string = strstr(control_buffer, "Package:")) == NULL) {
849 continue;
850 }
851
852 tmp_string += 8;
853 tmp_string += strspn(tmp_string, " \n\t");
854 package_name = bb_xstrndup(tmp_string, strcspn(tmp_string, "\n\0"));
855 write_flag = FALSE;
856 tmp_string = strstr(control_buffer, "Status:");
857 if (tmp_string != NULL) {
858 /* Seperate the status value from the control buffer */
859 tmp_string += 7;
860 tmp_string += strspn(tmp_string, " \n\t");
861 status_from_file = bb_xstrndup(tmp_string, strcspn(tmp_string, "\n"));
862 } else {
863 status_from_file = NULL;
864 }
865
866 /* Find this package in the status hashtable */
867 status_num = search_status_hashtable(package_name);
868 if (status_hashtable[status_num] != NULL) {
869 const char *status_from_hashtable = name_hashtable[status_hashtable[status_num]->status];
870 if (strcmp(status_from_file, status_from_hashtable) != 0) {
871 /* New status isnt exactly the same as old status */
872 const int state_status = get_status(status_num, 3);
873 if ((strcmp("installed", name_hashtable[state_status]) == 0) ||
874 (strcmp("unpacked", name_hashtable[state_status]) == 0)) {
875 /* We need to add the control file from the package */
876 i = 0;
877 while(deb_file[i] != NULL) {
878 if (strcmp(package_name, name_hashtable[package_hashtable[deb_file[i]->package]->name]) == 0) {
879 /* Write a status file entry with a modified status */
880 /* remove trailing \n's */
881 write_buffer_no_status(new_status_file, deb_file[i]->control_file);
882 set_status(status_num, "ok", 2);
883 fprintf(new_status_file, "Status: %s\n\n", name_hashtable[status_hashtable[status_num]->status]);
884 write_flag = TRUE;
885 break;
886 }
887 i++;
888 }
889 /* This is temperary, debugging only */
890 if (deb_file[i] == NULL) {
891 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);
892 }
893 }
894 else if (strcmp("not-installed", name_hashtable[state_status]) == 0) {
895 /* Only write the Package, Status, Priority and Section lines */
896 fprintf(new_status_file, "Package: %s\n", package_name);
897 fprintf(new_status_file, "Status: %s\n", status_from_hashtable);
898
899 while (1) {
900 char *field_name;
901 char *field_value;
902 field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value);
903 if (field_name == NULL) {
904 break;
905 }
906 if ((strcmp(field_name, "Priority") == 0) ||
907 (strcmp(field_name, "Section") == 0)) {
908 fprintf(new_status_file, "%s: %s\n", field_name, field_value);
909 }
910 }
911 write_flag = TRUE;
912 fputs("\n", new_status_file);
913 }
914 else if (strcmp("config-files", name_hashtable[state_status]) == 0) {
915 /* only change the status line */
916 while (1) {
917 char *field_name;
918 char *field_value;
919 field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value);
920 if (field_name == NULL) {
921 break;
922 }
923 /* Setup start point for next field */
924 if (strcmp(field_name, "Status") == 0) {
925 fprintf(new_status_file, "Status: %s\n", status_from_hashtable);
926 } else {
927 fprintf(new_status_file, "%s: %s\n", field_name, field_value);
928 }
929 }
930 write_flag = TRUE;
931 fputs("\n", new_status_file);
932 }
933 }
934 }
935 /* If the package from the status file wasnt handle above, do it now*/
936 if (! write_flag) {
937 fprintf(new_status_file, "%s\n\n", control_buffer);
938 }
939
940 free(status_from_file);
941 free(package_name);
942 free(control_buffer);
943 }
944
945 /* Write any new packages */
946 for(i = 0; deb_file[i] != NULL; i++) {
947 status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[i]->package]->name]);
948 if (strcmp("reinstreq", name_hashtable[get_status(status_num, 2)]) == 0) {
949 write_buffer_no_status(new_status_file, deb_file[i]->control_file);
950 set_status(status_num, "ok", 2);
951 fprintf(new_status_file, "Status: %s\n\n", name_hashtable[status_hashtable[status_num]->status]);
952 }
953 }
954 fclose(old_status_file);
955 fclose(new_status_file);
956
957
958 /* Create a separate backfile to dpkg */
959 if (rename("/var/lib/dpkg/status", "/var/lib/dpkg/status.udeb.bak") == -1) {
960 struct stat stat_buf;
961 if (stat("/var/lib/dpkg/status", &stat_buf) == 0) {
962 bb_error_msg_and_die("Couldnt create backup status file");
963 }
964 /* Its ok if renaming the status file fails because status
965 * file doesnt exist, maybe we are starting from scratch */
966 bb_error_msg("No status file found, creating new one");
967 }
968
969 if (rename("/var/lib/dpkg/status.udeb", "/var/lib/dpkg/status") == -1) {
970 bb_error_msg_and_die("DANGER: Couldnt create status file, you need to manually repair your status file");
971 }
972}
973
974/* This function returns TRUE if the given package can satisfy a
975 * dependency of type depend_type.
976 *
977 * A pre-depends is satisfied only if a package is already installed,
978 * which a regular depends can be satisfied by a package which we want
979 * to install.
980 */
981int package_satisfies_dependency(int package, int depend_type)
982{
983 int status_num = search_status_hashtable(name_hashtable[package_hashtable[package]->name]);
984
985 /* status could be unknown if package is a pure virtual
986 * provides which cannot satisfy any dependency by itself.
987 */
988 if ( status_hashtable[status_num] == NULL )
989 return 0;
990
991 switch (depend_type) {
992 case EDGE_PRE_DEPENDS: return get_status(status_num, 3) == search_name_hashtable("installed");
993 case EDGE_DEPENDS: return get_status(status_num, 1) == search_name_hashtable("install");
994 }
995 return 0;
996}
997
998int check_deps(deb_file_t **deb_file, int deb_start, int dep_max_count)
999{
1000 int *conflicts = NULL;
1001 int conflicts_num = 0;
1002 int i = deb_start;
1003 int j;
1004
1005 /* Check for conflicts
1006 * TODO: TEST if conflicts with other packages to be installed
1007 *
1008 * Add install packages and the packages they provide
1009 * to the list of files to check conflicts for
1010 */
1011
1012 /* Create array of package numbers to check against
1013 * installed package for conflicts*/
1014 while (deb_file[i] != NULL) {
1015 const unsigned int package_num = deb_file[i]->package;
1016 conflicts = xrealloc(conflicts, sizeof(int) * (conflicts_num + 1));
1017 conflicts[conflicts_num] = package_num;
1018 conflicts_num++;
1019 /* add provides to conflicts list */
1020 for (j = 0; j < package_hashtable[package_num]->num_of_edges; j++) {
1021 if (package_hashtable[package_num]->edge[j]->type == EDGE_PROVIDES) {
1022 const int conflicts_package_num = search_package_hashtable(
1023 package_hashtable[package_num]->edge[j]->name,
1024 package_hashtable[package_num]->edge[j]->version,
1025 package_hashtable[package_num]->edge[j]->operator);
1026 if (package_hashtable[conflicts_package_num] == NULL) {
1027 /* create a new package */
1028 common_node_t *new_node = (common_node_t *) xmalloc(sizeof(common_node_t));
1029 new_node->name = package_hashtable[package_num]->edge[j]->name;
1030 new_node->version = package_hashtable[package_num]->edge[j]->version;
1031 new_node->num_of_edges = 0;
1032 new_node->edge = NULL;
1033 package_hashtable[conflicts_package_num] = new_node;
1034 }
1035 conflicts = xrealloc(conflicts, sizeof(int) * (conflicts_num + 1));
1036 conflicts[conflicts_num] = conflicts_package_num;
1037 conflicts_num++;
1038 }
1039 }
1040 i++;
1041 }
1042
1043 /* Check conflicts */
1044 i = 0;
1045 while (deb_file[i] != NULL) {
1046 const common_node_t *package_node = package_hashtable[deb_file[i]->package];
1047 int status_num = 0;
1048 status_num = search_status_hashtable(name_hashtable[package_node->name]);
1049
1050 if (get_status(status_num, 3) == search_name_hashtable("installed")) {
1051 i++;
1052 continue;
1053 }
1054
1055 for (j = 0; j < package_node->num_of_edges; j++) {
1056 const edge_t *package_edge = package_node->edge[j];
1057
1058 if (package_edge->type == EDGE_CONFLICTS) {
1059 const unsigned int package_num =
1060 search_package_hashtable(package_edge->name,
1061 package_edge->version,
1062 package_edge->operator);
1063 int result = 0;
1064 if (package_hashtable[package_num] != NULL) {
1065 status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]);
1066
1067 if (get_status(status_num, 1) == search_name_hashtable("install")) {
1068 result = test_version(package_hashtable[deb_file[i]->package]->version,
1069 package_edge->version, package_edge->operator);
1070 }
1071 }
1072
1073 if (result) {
1074 bb_error_msg_and_die("Package %s conflicts with %s",
1075 name_hashtable[package_node->name],
1076 name_hashtable[package_edge->name]);
1077 }
1078 }
1079 }
1080 i++;
1081 }
1082
1083
1084 /* Check dependendcies */
1085 for (i = 0; i < PACKAGE_HASH_PRIME; i++) {
1086 int status_num = 0;
1087 int number_of_alternatives = 0;
1088 const edge_t * root_of_alternatives = NULL;
1089 const common_node_t *package_node = package_hashtable[i];
1090
1091 /* If the package node does not exist then this
1092 * package is a virtual one. In which case there are
1093 * no dependencies to check.
1094 */
1095 if ( package_node == NULL ) continue;
1096
1097 status_num = search_status_hashtable(name_hashtable[package_node->name]);
1098
1099 /* If there is no status then this package is a
1100 * virtual one provided by something else. In which
1101 * case there are no dependencies to check.
1102 */
1103 if ( status_hashtable[status_num] == NULL ) continue;
1104
1105 /* If we don't want this package installed then we may
1106 * as well ignore it's dependencies.
1107 */
1108 if (get_status(status_num, 1) != search_name_hashtable("install")) {
1109 continue;
1110 }
1111
1112#if 0
1113 /* This might be needed so we don't complain about
1114 * things which are broken but unrelated to the
1115 * packages that are currently being installed
1116 */
1117 if (state_status == search_name_hashtable("installed"))
1118 continue;
1119#endif
1120
1121 /* This code is tested only for EDGE_DEPENDS, since I
1122 * have no suitable pre-depends available. There is no
1123 * reason that it shouldn't work though :-)
1124 */
1125 for (j = 0; j < package_node->num_of_edges; j++) {
1126 const edge_t *package_edge = package_node->edge[j];
1127 unsigned int package_num;
1128
1129 if ( package_edge->type == EDGE_OR_PRE_DEPENDS ||
1130 package_edge->type == EDGE_OR_DEPENDS ) { /* start an EDGE_OR_ list */
1131 number_of_alternatives = package_edge->version;
1132 root_of_alternatives = package_edge;
1133 continue;
1134 } else if ( number_of_alternatives == 0 ) { /* not in the middle of an EDGE_OR_ list */
1135 number_of_alternatives = 1;
1136 root_of_alternatives = NULL;
1137 }
1138
1139 package_num = search_package_hashtable(package_edge->name, package_edge->version, package_edge->operator);
1140
1141 if (package_edge->type == EDGE_PRE_DEPENDS ||
1142 package_edge->type == EDGE_DEPENDS ) {
1143 int result=1;
1144 status_num = 0;
1145
1146 /* If we are inside an alternative then check
1147 * this edge is the right type.
1148 *
1149 * EDGE_DEPENDS == OR_DEPENDS -1
1150 * EDGE_PRE_DEPENDS == OR_PRE_DEPENDS -1
1151 */
1152 if ( root_of_alternatives && package_edge->type != root_of_alternatives->type - 1)
1153 bb_error_msg_and_die("Fatal error. Package dependencies corrupt: %d != %d - 1 \n",
1154 package_edge->type, root_of_alternatives->type);
1155
1156 if (package_hashtable[package_num] != NULL)
1157 result = !package_satisfies_dependency(package_num, package_edge->type);
1158
1159 if (result) { /* check for other package which provide what we are looking for */
1160 int provider = -1;
1161
1162 while ( (provider = search_for_provides(package_edge->name, provider) ) > -1 ) {
1163 if ( package_hashtable[provider] == NULL ) {
1164 printf("Have a provider but no package information for it\n");
1165 continue;
1166 }
1167 result = !package_satisfies_dependency(provider, package_edge->type);
1168
1169 if ( result == 0 )
1170 break;
1171 }
1172 }
1173
1174 /* It must be already installed, or to be installed */
1175 number_of_alternatives--;
1176 if (result && number_of_alternatives == 0) {
1177 if ( root_of_alternatives )
1178 bb_error_msg_and_die(
1179 "Package %s %sdepends on %s, "
1180 "which cannot be satisfied",
1181 name_hashtable[package_node->name],
1182 package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "",
1183 name_hashtable[root_of_alternatives->name]);
1184 else
1185 bb_error_msg_and_die(
1186 "Package %s %sdepends on %s, which %s\n",
1187 name_hashtable[package_node->name],
1188 package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "",
1189 name_hashtable[package_edge->name],
1190 describe_status(status_num));
1191 } else if ( result == 0 && number_of_alternatives ) {
1192 /* we've found a package which
1193 * satisfies the dependency,
1194 * so skip over the rest of
1195 * the alternatives.
1196 */
1197 j += number_of_alternatives;
1198 number_of_alternatives = 0;
1199 }
1200 }
1201 }
1202 }
1203 free(conflicts);
1204 return(TRUE);
1205}
1206
1207char **create_list(const char *filename)
1208{
1209 FILE *list_stream;
1210 char **file_list = NULL;
1211 char *line = NULL;
1212 int count = 0;
1213
1214 /* don't use [xw]fopen here, handle error ourself */
1215 list_stream = fopen(filename, "r");
1216 if (list_stream == NULL) {
1217 return(NULL);
1218 }
1219
1220 while ((line = bb_get_chomped_line_from_file(list_stream)) != NULL) {
1221 file_list = xrealloc(file_list, sizeof(char *) * (count + 2));
1222 file_list[count] = line;
1223 count++;
1224 }
1225 fclose(list_stream);
1226
1227 if (count == 0) {
1228 return(NULL);
1229 } else {
1230 file_list[count] = NULL;
1231 return(file_list);
1232 }
1233}
1234
1235/* maybe i should try and hook this into remove_file.c somehow */
1236int remove_file_array(char **remove_names, char **exclude_names)
1237{
1238 struct stat path_stat;
1239 int match_flag;
1240 int remove_flag = FALSE;
1241 int i,j;
1242
1243 if (remove_names == NULL) {
1244 return(FALSE);
1245 }
1246 for (i = 0; remove_names[i] != NULL; i++) {
1247 match_flag = FALSE;
1248 if (exclude_names != NULL) {
1249 for (j = 0; exclude_names[j] != 0; j++) {
1250 if (strcmp(remove_names[i], exclude_names[j]) == 0) {
1251 match_flag = TRUE;
1252 break;
1253 }
1254 }
1255 }
1256 if (!match_flag) {
1257 if (lstat(remove_names[i], &path_stat) < 0) {
1258 continue;
1259 }
1260 if (S_ISDIR(path_stat.st_mode)) {
1261 if (rmdir(remove_names[i]) != -1) {
1262 remove_flag = TRUE;
1263 }
1264 } else {
1265 if (unlink(remove_names[i]) != -1) {
1266 remove_flag = TRUE;
1267 }
1268 }
1269 }
1270 }
1271 return(remove_flag);
1272}
1273
1274int run_package_script(const char *package_name, const char *script_type)
1275{
1276 struct stat path_stat;
1277 char *script_path;
1278 int result;
1279
1280 script_path = xmalloc(strlen(package_name) + strlen(script_type) + 21);
1281 sprintf(script_path, "/var/lib/dpkg/info/%s.%s", package_name, script_type);
1282
1283 /* If the file doesnt exist is isnt a fatal */
1284 if (lstat(script_path, &path_stat) < 0) {
1285 result = EXIT_SUCCESS;
1286 } else {
1287 result = system(script_path);
1288 }
1289 free(script_path);
1290 return(result);
1291}
1292
1293const char *all_control_files[] = {"preinst", "postinst", "prerm", "postrm",
1294 "list", "md5sums", "shlibs", "conffiles", "config", "templates", NULL };
1295
1296char **all_control_list(const char *package_name)
1297{
1298 unsigned short i = 0;
1299 char **remove_files;
1300
1301 /* Create a list of all /var/lib/dpkg/info/<package> files */
1302 remove_files = malloc(sizeof(all_control_files));
1303 while (all_control_files[i]) {
1304 remove_files[i] = xmalloc(strlen(package_name) + strlen(all_control_files[i]) + 21);
1305 sprintf(remove_files[i], "/var/lib/dpkg/info/%s.%s", package_name, all_control_files[i]);
1306 i++;
1307 }
1308 remove_files[sizeof(all_control_files)/sizeof(char*) - 1] = NULL;
1309
1310 return(remove_files);
1311}
1312
1313void free_array(char **array)
1314{
1315
1316 if (array) {
1317 unsigned short i = 0;
1318 while (array[i]) {
1319 free(array[i]);
1320 i++;
1321 }
1322 free(array);
1323 }
1324}
1325
1326/* This function lists information on the installed packages. It loops through
1327 * the status_hashtable to retrieve the info. This results in smaller code than
1328 * scanning the status file. The resulting list, however, is unsorted.
1329 */
1330void list_packages(void)
1331{
1332 int i;
1333
1334 printf(" Name Version\n");
1335 printf("+++-==============-==============\n");
1336
1337 /* go through status hash, dereference package hash and finally strings */
1338 for (i=0; i<STATUS_HASH_PRIME+1; i++) {
1339
1340 if (status_hashtable[i]) {
1341 const char *stat_str; /* status string */
1342 const char *name_str; /* package name */
1343 const char *vers_str; /* version */
1344 char s1, s2; /* status abbreviations */
1345 int spccnt; /* space count */
1346 int j;
1347
1348 stat_str = name_hashtable[status_hashtable[i]->status];
1349 name_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->name];
1350 vers_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->version];
1351
1352 /* get abbreviation for status field 1 */
1353 s1 = stat_str[0] == 'i' ? 'i' : 'r';
1354
1355 /* get abbreviation for status field 2 */
1356 for (j=0, spccnt=0; stat_str[j] && spccnt<2; j++) {
1357 if (stat_str[j] == ' ') spccnt++;
1358 }
1359 s2 = stat_str[j];
1360
1361 /* print out the line formatted like Debian dpkg */
1362 printf("%c%c %-14s %s\n", s1, s2, name_str, vers_str);
1363 }
1364 }
1365}
1366
1367void remove_package(const unsigned int package_num, int noisy)
1368{
1369 const char *package_name = name_hashtable[package_hashtable[package_num]->name];
1370 const char *package_version = name_hashtable[package_hashtable[package_num]->version];
1371 const unsigned int status_num = search_status_hashtable(package_name);
1372 const int package_name_length = strlen(package_name);
1373 char **remove_files;
1374 char **exclude_files;
1375 char list_name[package_name_length + 25];
1376 char conffile_name[package_name_length + 30];
1377 int return_value;
1378
1379 if ( noisy )
1380 printf("Removing %s (%s) ...\n", package_name, package_version);
1381
1382 /* run prerm script */
1383 return_value = run_package_script(package_name, "prerm");
1384 if (return_value == -1) {
1385 bb_error_msg_and_die("script failed, prerm failure");
1386 }
1387
1388 /* Create a list of files to remove, and a separate list of those to keep */
1389 sprintf(list_name, "/var/lib/dpkg/info/%s.list", package_name);
1390 remove_files = create_list(list_name);
1391
1392 sprintf(conffile_name, "/var/lib/dpkg/info/%s.conffiles", package_name);
1393 exclude_files = create_list(conffile_name);
1394
1395 /* Some directories cant be removed straight away, so do multiple passes */
1396 while (remove_file_array(remove_files, exclude_files));
1397 free_array(exclude_files);
1398 free_array(remove_files);
1399
1400 /* Create a list of files in /var/lib/dpkg/info/<package>.* to keep */
1401 exclude_files = xmalloc(sizeof(char*) * 3);
1402 exclude_files[0] = bb_xstrdup(conffile_name);
1403 exclude_files[1] = xmalloc(package_name_length + 27);
1404 sprintf(exclude_files[1], "/var/lib/dpkg/info/%s.postrm", package_name);
1405 exclude_files[2] = NULL;
1406
1407 /* Create a list of all /var/lib/dpkg/info/<package> files */
1408 remove_files = all_control_list(package_name);
1409
1410 remove_file_array(remove_files, exclude_files);
1411 free_array(remove_files);
1412 free_array(exclude_files);
1413
1414 /* rename <package>.conffile to <package>.list */
1415 rename(conffile_name, list_name);
1416
1417 /* Change package status */
1418 set_status(status_num, "config-files", 3);
1419}
1420
1421void purge_package(const unsigned int package_num)
1422{
1423 const char *package_name = name_hashtable[package_hashtable[package_num]->name];
1424 const char *package_version = name_hashtable[package_hashtable[package_num]->version];
1425 const unsigned int status_num = search_status_hashtable(package_name);
1426 char **remove_files;
1427 char **exclude_files;
1428 char list_name[strlen(package_name) + 25];
1429
1430 printf("Purging %s (%s) ...\n", package_name, package_version);
1431
1432 /* run prerm script */
1433 if (run_package_script(package_name, "prerm") != 0) {
1434 bb_error_msg_and_die("script failed, prerm failure");
1435 }
1436
1437 /* Create a list of files to remove */
1438 sprintf(list_name, "/var/lib/dpkg/info/%s.list", package_name);
1439 remove_files = create_list(list_name);
1440
1441 exclude_files = xmalloc(sizeof(char*));
1442 exclude_files[0] = NULL;
1443
1444 /* Some directories cant be removed straight away, so do multiple passes */
1445 while (remove_file_array(remove_files, exclude_files));
1446 free_array(remove_files);
1447
1448 /* Create a list of all /var/lib/dpkg/info/<package> files */
1449 remove_files = all_control_list(package_name);
1450 remove_file_array(remove_files, exclude_files);
1451 free_array(remove_files);
1452 free(exclude_files);
1453
1454 /* run postrm script */
1455 if (run_package_script(package_name, "postrm") == -1) {
1456 bb_error_msg_and_die("postrm fialure.. set status to what?");
1457 }
1458
1459 /* Change package status */
1460 set_status(status_num, "not-installed", 3);
1461}
1462
1463static archive_handle_t *init_archive_deb_ar(const char *filename)
1464{
1465 archive_handle_t *ar_handle;
1466
1467 /* Setup an ar archive handle that refers to the gzip sub archive */
1468 ar_handle = init_handle();
1469 ar_handle->filter = filter_accept_list_reassign;
1470 ar_handle->src_fd = bb_xopen(filename, O_RDONLY);
1471
1472 return(ar_handle);
1473}
1474
1475static void init_archive_deb_control(archive_handle_t *ar_handle)
1476{
1477 archive_handle_t *tar_handle;
1478
1479 /* Setup the tar archive handle */
1480 tar_handle = init_handle();
1481 tar_handle->src_fd = ar_handle->src_fd;
1482
1483 /* We don't care about data.tar.* or debian-binary, just control.tar.* */
1484#ifdef CONFIG_FEATURE_DEB_TAR_GZ
1485 ar_handle->accept = llist_add_to(NULL, "control.tar.gz");
1486#endif
1487#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
1488 ar_handle->accept = llist_add_to(ar_handle->accept, "control.tar.bz2");
1489#endif
1490
1491 /* Assign the tar handle as a subarchive of the ar handle */
1492 ar_handle->sub_archive = tar_handle;
1493
1494 return;
1495}
1496
1497static void init_archive_deb_data(archive_handle_t *ar_handle)
1498{
1499 archive_handle_t *tar_handle;
1500
1501 /* Setup the tar archive handle */
1502 tar_handle = init_handle();
1503 tar_handle->src_fd = ar_handle->src_fd;
1504
1505 /* We don't care about control.tar.* or debian-binary, just data.tar.* */
1506#ifdef CONFIG_FEATURE_DEB_TAR_GZ
1507 ar_handle->accept = llist_add_to(NULL, "data.tar.gz");
1508#endif
1509#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
1510 ar_handle->accept = llist_add_to(ar_handle->accept, "data.tar.bz2");
1511#endif
1512
1513 /* Assign the tar handle as a subarchive of the ar handle */
1514 ar_handle->sub_archive = tar_handle;
1515
1516 return;
1517}
1518
1519static char *deb_extract_control_file_to_buffer(archive_handle_t *ar_handle, llist_t *myaccept)
1520{
1521 ar_handle->sub_archive->action_data = data_extract_to_buffer;
1522 ar_handle->sub_archive->accept = myaccept;
1523
1524 unpack_ar_archive(ar_handle);
1525 close(ar_handle->src_fd);
1526
1527 return(ar_handle->sub_archive->buffer);
1528}
1529
1530static void data_extract_all_prefix(archive_handle_t *archive_handle)
1531{
1532 char *name_ptr = archive_handle->file_header->name;
1533
1534 name_ptr += strspn(name_ptr, "./");
1535 if (name_ptr[0] != '\0') {
1536 archive_handle->file_header->name = xmalloc(strlen(archive_handle->buffer) + 2 + strlen(name_ptr));
1537 strcpy(archive_handle->file_header->name, archive_handle->buffer);
1538 strcat(archive_handle->file_header->name, name_ptr);
1539 data_extract_all(archive_handle);
1540 }
1541 return;
1542}
1543
1544static void unpack_package(deb_file_t *deb_file)
1545{
1546 const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name];
1547 const unsigned int status_num = search_status_hashtable(package_name);
1548 const unsigned int status_package_num = status_hashtable[status_num]->package;
1549 char *info_prefix;
1550 archive_handle_t *archive_handle;
1551 FILE *out_stream;
1552 llist_t *accept_list = NULL;
1553 int i = 0;
1554
1555 /* If existing version, remove it first */
1556 if (strcmp(name_hashtable[get_status(status_num, 3)], "installed") == 0) {
1557 /* Package is already installed, remove old version first */
1558 printf("Preparing to replace %s %s (using %s) ...\n", package_name,
1559 name_hashtable[package_hashtable[status_package_num]->version],
1560 deb_file->filename);
1561 remove_package(status_package_num, 0);
1562 } else {
1563 printf("Unpacking %s (from %s) ...\n", package_name, deb_file->filename);
1564 }
1565
1566 /* Extract control.tar.gz to /var/lib/dpkg/info/<package>.filename */
1567 info_prefix = (char *) xmalloc(strlen(package_name) + 20 + 4 + 2);
1568 sprintf(info_prefix, "/var/lib/dpkg/info/%s.", package_name);
1569 archive_handle = init_archive_deb_ar(deb_file->filename);
1570 init_archive_deb_control(archive_handle);
1571
1572 while(all_control_files[i]) {
1573 char *c = (char *) xmalloc(3 + bb_strlen(all_control_files[i]));
1574 sprintf(c, "./%s", all_control_files[i]);
1575 accept_list= llist_add_to(accept_list, c);
1576 i++;
1577 }
1578 archive_handle->sub_archive->accept = accept_list;
1579 archive_handle->sub_archive->filter = filter_accept_list;
1580 archive_handle->sub_archive->action_data = data_extract_all_prefix;
1581 archive_handle->sub_archive->buffer = info_prefix;
1582 archive_handle->sub_archive->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
1583 unpack_ar_archive(archive_handle);
1584
1585 /* Run the preinst prior to extracting */
1586 if (run_package_script(package_name, "preinst") != 0) {
1587 /* when preinst returns exit code != 0 then quit installation process */
1588 bb_error_msg_and_die("subprocess pre-installation script returned error.");
1589 }
1590
1591 /* Extract data.tar.gz to the root directory */
1592 archive_handle = init_archive_deb_ar(deb_file->filename);
1593 init_archive_deb_data(archive_handle);
1594 archive_handle->sub_archive->action_data = data_extract_all_prefix;
1595 archive_handle->sub_archive->buffer = "/";
1596 archive_handle->sub_archive->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
1597 unpack_ar_archive(archive_handle);
1598
1599 /* Create the list file */
1600 strcat(info_prefix, "list");
1601 out_stream = bb_xfopen(info_prefix, "w");
1602 while (archive_handle->sub_archive->passed) {
1603 /* the leading . has been stripped by data_extract_all_prefix already */
1604 fputs(archive_handle->sub_archive->passed->data, out_stream);
1605 fputc('\n', out_stream);
1606 archive_handle->sub_archive->passed = archive_handle->sub_archive->passed->link;
1607 }
1608 fclose(out_stream);
1609
1610 /* change status */
1611 set_status(status_num, "install", 1);
1612 set_status(status_num, "unpacked", 3);
1613
1614 free(info_prefix);
1615}
1616
1617void configure_package(deb_file_t *deb_file)
1618{
1619 const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name];
1620 const char *package_version = name_hashtable[package_hashtable[deb_file->package]->version];
1621 const int status_num = search_status_hashtable(package_name);
1622
1623 printf("Setting up %s (%s) ...\n", package_name, package_version);
1624
1625 /* Run the postinst script */
1626 if (run_package_script(package_name, "postinst") != 0) {
1627 /* TODO: handle failure gracefully */
1628 bb_error_msg_and_die("postrm failure.. set status to what?");
1629 }
1630 /* Change status to reflect success */
1631 set_status(status_num, "install", 1);
1632 set_status(status_num, "installed", 3);
1633}
1634
1635int dpkg_main(int argc, char **argv)
1636{
1637 deb_file_t **deb_file = NULL;
1638 status_node_t *status_node;
1639 int opt;
1640 int package_num;
1641 int dpkg_opt = 0;
1642 int deb_count = 0;
1643 int state_status;
1644 int status_num;
1645 int i;
1646
1647 while ((opt = getopt(argc, argv, "CF:ilPru")) != -1) {
1648 switch (opt) {
1649 case 'C': // equivalent to --configure in official dpkg
1650 dpkg_opt |= dpkg_opt_configure;
1651 dpkg_opt |= dpkg_opt_package_name;
1652 break;
1653 case 'F': // equivalent to --force in official dpkg
1654 if (strcmp(optarg, "depends") == 0) {
1655 dpkg_opt |= dpkg_opt_force_ignore_depends;
1656 }
1657 break;
1658 case 'i':
1659 dpkg_opt |= dpkg_opt_install;
1660 dpkg_opt |= dpkg_opt_filename;
1661 break;
1662 case 'l':
1663 dpkg_opt |= dpkg_opt_list_installed;
1664 break;
1665 case 'P':
1666 dpkg_opt |= dpkg_opt_purge;
1667 dpkg_opt |= dpkg_opt_package_name;
1668 break;
1669 case 'r':
1670 dpkg_opt |= dpkg_opt_remove;
1671 dpkg_opt |= dpkg_opt_package_name;
1672 break;
1673 case 'u': /* Equivalent to --unpack in official dpkg */
1674 dpkg_opt |= dpkg_opt_unpack;
1675 dpkg_opt |= dpkg_opt_filename;
1676 break;
1677 default:
1678 bb_show_usage();
1679 }
1680 }
1681 /* check for non-otion argument if expected */
1682 if ((dpkg_opt == 0) || ((argc == optind) && !(dpkg_opt && dpkg_opt_list_installed))) {
1683 bb_show_usage();
1684 }
1685
1686/* puts("(Reading database ... xxxxx files and directories installed.)"); */
1687 index_status_file("/var/lib/dpkg/status");
1688
1689 /* if the list action was given print the installed packages and exit */
1690 if (dpkg_opt & dpkg_opt_list_installed) {
1691 list_packages();
1692 return(EXIT_SUCCESS);
1693 }
1694
1695 /* Read arguments and store relevant info in structs */
1696 while (optind < argc) {
1697 /* deb_count = nb_elem - 1 and we need nb_elem + 1 to allocate terminal node [NULL pointer] */
1698 deb_file = xrealloc(deb_file, sizeof(deb_file_t *) * (deb_count + 2));
1699 deb_file[deb_count] = (deb_file_t *) xmalloc(sizeof(deb_file_t));
1700 if (dpkg_opt & dpkg_opt_filename) {
1701 archive_handle_t *archive_handle;
1702 llist_t *control_list = NULL;
1703
1704 /* Extract the control file */
1705 control_list = llist_add_to(NULL, "./control");
1706 archive_handle = init_archive_deb_ar(argv[optind]);
1707 init_archive_deb_control(archive_handle);
1708 deb_file[deb_count]->control_file = deb_extract_control_file_to_buffer(archive_handle, control_list);
1709 if (deb_file[deb_count]->control_file == NULL) {
1710 bb_error_msg_and_die("Couldnt extract control file");
1711 }
1712 deb_file[deb_count]->filename = bb_xstrdup(argv[optind]);
1713 package_num = fill_package_struct(deb_file[deb_count]->control_file);
1714
1715 if (package_num == -1) {
1716 bb_error_msg("Invalid control file in %s", argv[optind]);
1717 continue;
1718 }
1719 deb_file[deb_count]->package = (unsigned int) package_num;
1720
1721 /* Add the package to the status hashtable */
1722 if ((dpkg_opt & dpkg_opt_unpack) || (dpkg_opt & dpkg_opt_install)) {
1723 /* Try and find a currently installed version of this package */
1724 status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]);
1725 /* If no previous entry was found initialise a new entry */
1726 if ((status_hashtable[status_num] == NULL) ||
1727 (status_hashtable[status_num]->status == 0)) {
1728 status_node = (status_node_t *) xmalloc(sizeof(status_node_t));
1729 status_node->package = deb_file[deb_count]->package;
1730 /* reinstreq isnt changed to "ok" until the package control info
1731 * is written to the status file*/
1732 status_node->status = search_name_hashtable("install reinstreq not-installed");
1733 status_hashtable[status_num] = status_node;
1734 } else {
1735 set_status(status_num, "install", 1);
1736 set_status(status_num, "reinstreq", 2);
1737 }
1738 }
1739 }
1740 else if (dpkg_opt & dpkg_opt_package_name) {
1741 deb_file[deb_count]->filename = NULL;
1742 deb_file[deb_count]->control_file = NULL;
1743 deb_file[deb_count]->package = search_package_hashtable(
1744 search_name_hashtable(argv[optind]),
1745 search_name_hashtable("ANY"), VER_ANY);
1746 if (package_hashtable[deb_file[deb_count]->package] == NULL) {
1747 bb_error_msg_and_die("Package %s is uninstalled or unknown\n", argv[optind]);
1748 }
1749 package_num = deb_file[deb_count]->package;
1750 status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]);
1751 state_status = get_status(status_num, 3);
1752
1753 /* check package status is "installed" */
1754 if (dpkg_opt & dpkg_opt_remove) {
1755 if ((strcmp(name_hashtable[state_status], "not-installed") == 0) ||
1756 (strcmp(name_hashtable[state_status], "config-files") == 0)) {
1757 bb_error_msg_and_die("%s is already removed.", name_hashtable[package_hashtable[package_num]->name]);
1758 }
1759 set_status(status_num, "deinstall", 1);
1760 }
1761 else if (dpkg_opt & dpkg_opt_purge) {
1762 /* if package status is "conf-files" then its ok */
1763 if (strcmp(name_hashtable[state_status], "not-installed") == 0) {
1764 bb_error_msg_and_die("%s is already purged.", name_hashtable[package_hashtable[package_num]->name]);
1765 }
1766 set_status(status_num, "purge", 1);
1767 }
1768 }
1769 deb_count++;
1770 optind++;
1771 }
1772 deb_file[deb_count] = NULL;
1773
1774 /* Check that the deb file arguments are installable */
1775 if ((dpkg_opt & dpkg_opt_force_ignore_depends) != dpkg_opt_force_ignore_depends) {
1776 if (!check_deps(deb_file, 0, deb_count)) {
1777 bb_error_msg_and_die("Dependency check failed");
1778 }
1779 }
1780
1781 /* TODO: install or remove packages in the correct dependency order */
1782 for (i = 0; i < deb_count; i++) {
1783 /* Remove or purge packages */
1784 if (dpkg_opt & dpkg_opt_remove) {
1785 remove_package(deb_file[i]->package, 1);
1786 }
1787 else if (dpkg_opt & dpkg_opt_purge) {
1788 purge_package(deb_file[i]->package);
1789 }
1790 else if (dpkg_opt & dpkg_opt_unpack) {
1791 unpack_package(deb_file[i]);
1792 }
1793 else if (dpkg_opt & dpkg_opt_install) {
1794 unpack_package(deb_file[i]);
1795 /* package is configured in second pass below */
1796 }
1797 else if (dpkg_opt & dpkg_opt_configure) {
1798 configure_package(deb_file[i]);
1799 }
1800 }
1801 /* configure installed packages */
1802 if (dpkg_opt & dpkg_opt_install) {
1803 for (i = 0; i < deb_count; i++)
1804 configure_package(deb_file[i]);
1805 }
1806
1807 write_status_file(deb_file);
1808
1809 for (i = 0; i < deb_count; i++) {
1810 free(deb_file[i]->control_file);
1811 free(deb_file[i]->filename);
1812 free(deb_file[i]);
1813 }
1814
1815 free(deb_file);
1816
1817 for (i = 0; i < NAME_HASH_PRIME; i++) {
1818 free(name_hashtable[i]);
1819 }
1820
1821 for (i = 0; i < PACKAGE_HASH_PRIME; i++) {
1822 if (package_hashtable[i] != NULL) {
1823 free_package(package_hashtable[i]);
1824 }
1825 }
1826
1827 for (i = 0; i < STATUS_HASH_PRIME; i++) {
1828 free(status_hashtable[i]);
1829 }
1830
1831 return(EXIT_SUCCESS);
1832}
1833
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Library General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 *
16 */
17#include <fcntl.h>
18#include <stdlib.h>
19#include <string.h>
20#include <unistd.h>
21#include <getopt.h>
22
23#include "unarchive.h"
24#include "busybox.h"
25
26#define DPKG_DEB_OPT_CONTENTS 1
27#define DPKG_DEB_OPT_CONTROL 2
28#define DPKG_DEB_OPT_FIELD 4
29#define DPKG_DEB_OPT_EXTRACT 8
30#define DPKG_DEB_OPT_EXTRACT_VERBOSE 16
31
32extern int dpkg_deb_main(int argc, char **argv)
33{
34 archive_handle_t *ar_archive;
35 archive_handle_t *tar_archive;
36 llist_t *control_tar_llist = NULL;
37 unsigned long opt;
38 char *extract_dir = NULL;
39 short argcount = 1;
40
41 /* Setup the tar archive handle */
42 tar_archive = init_handle();
43
44 /* Setup an ar archive handle that refers to the gzip sub archive */
45 ar_archive = init_handle();
46 ar_archive->sub_archive = tar_archive;
47 ar_archive->filter = filter_accept_list_reassign;
48
49#ifdef CONFIG_FEATURE_DEB_TAR_GZ
50 ar_archive->accept = llist_add_to(NULL, "data.tar.gz");
51 control_tar_llist = llist_add_to(NULL, "control.tar.gz");
52#endif
53
54#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
55 ar_archive->accept = llist_add_to(ar_archive->accept, "data.tar.bz2");
56 control_tar_llist = llist_add_to(control_tar_llist, "control.tar.bz2");
57#endif
58
59 bb_opt_complementaly = "c~efXx:e~cfXx:f~ceXx:X~cefx:x~cefX";
60 opt = bb_getopt_ulflags(argc, argv, "cefXx");
61
62 if (opt & DPKG_DEB_OPT_CONTENTS) {
63 tar_archive->action_header = header_verbose_list;
64 }
65 if (opt & DPKG_DEB_OPT_CONTROL) {
66 ar_archive->accept = control_tar_llist;
67 tar_archive->action_data = data_extract_all;
68 if (optind + 1 == argc) {
69 extract_dir = "./DEBIAN";
70 } else {
71 argcount++;
72 }
73 }
74 if (opt & DPKG_DEB_OPT_FIELD) {
75 /* Print the entire control file
76 * it should accept a second argument which specifies a
77 * specific field to print */
78 ar_archive->accept = control_tar_llist;
79 tar_archive->accept = llist_add_to(NULL, "./control");;
80 tar_archive->filter = filter_accept_list;
81 tar_archive->action_data = data_extract_to_stdout;
82 }
83 if (opt & DPKG_DEB_OPT_EXTRACT) {
84 tar_archive->action_header = header_list;
85 }
86 if (opt & (DPKG_DEB_OPT_EXTRACT_VERBOSE | DPKG_DEB_OPT_EXTRACT)) {
87 tar_archive->action_data = data_extract_all;
88 argcount = 2;
89 }
90
91 if ((optind + argcount != argc) || (opt & 0x80000000UL)) {
92 bb_show_usage();
93 }
94
95 tar_archive->src_fd = ar_archive->src_fd = bb_xopen(argv[optind++], O_RDONLY);
96
97 /* Workout where to extract the files */
98 /* 2nd argument is a dir name */
99 if (argv[optind]) {
100 extract_dir = argv[optind];
101 }
102 if (extract_dir) {
103 mkdir(extract_dir, 0777);
104 chdir(extract_dir);
105 }
106 unpack_ar_archive(ar_archive);
107
108 /* Cleanup */
109 close (ar_archive->src_fd);
110
111 return(EXIT_SUCCESS);
112}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Gzip implementation for busybox
4 *
5 * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
6 *
7 * Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
8 * based on gzip sources
9 *
10 * Adjusted further by Erik Andersen <andersen@codepoet.org> to support files as
11 * well as stdin/stdout, and to generally behave itself wrt command line
12 * handling.
13 *
14 * General cleanup to better adhere to the style guide and make use of standard
15 * busybox functions by Glenn McGrath <bug1@iinet.net.au>
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 *
31 *
32 * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
33 * Copyright (C) 1992-1993 Jean-loup Gailly
34 * The unzip code was written and put in the public domain by Mark Adler.
35 * Portions of the lzw code are derived from the public domain 'compress'
36 * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
37 * Ken Turkowski, Dave Mack and Peter Jannesen.
38 *
39 * See the license_msg below and the file COPYING for the software license.
40 * See the file algorithm.doc for the compression algorithms and file formats.
41 */
42
43#if 0
44static char *license_msg[] = {
45 " Copyright (C) 1992-1993 Jean-loup Gailly",
46 " This program is free software; you can redistribute it and/or modify",
47 " it under the terms of the GNU General Public License as published by",
48 " the Free Software Foundation; either version 2, or (at your option)",
49 " any later version.",
50 "",
51 " This program is distributed in the hope that it will be useful,",
52 " but WITHOUT ANY WARRANTY; without even the implied warranty of",
53 " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the",
54 " GNU General Public License for more details.",
55 "",
56 " You should have received a copy of the GNU General Public License",
57 " along with this program; if not, write to the Free Software",
58 " Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.",
59 0
60};
61#endif
62
63#include <stdlib.h>
64#include <string.h>
65#include <unistd.h>
66#include <getopt.h>
67#include <sys/types.h>
68#include <sys/stat.h>
69#include <fcntl.h>
70
71#include "busybox.h"
72#include "unarchive.h"
73
74#define GUNZIP_OPT_STDOUT 1
75#define GUNZIP_OPT_FORCE 2
76#define GUNZIP_OPT_TEST 4
77#define GUNZIP_OPT_DECOMPRESS 8
78
79extern int gunzip_main(int argc, char **argv)
80{
81 char status = EXIT_SUCCESS;
82 unsigned long opt;
83
84 opt = bb_getopt_ulflags(argc, argv, "cftd");
85 /* if called as zcat */
86 if (strcmp(bb_applet_name, "zcat") == 0) {
87 opt |= GUNZIP_OPT_STDOUT;
88 }
89
90 do {
91 struct stat stat_buf;
92 const char *old_path = argv[optind];
93 const char *delete_path = NULL;
94 char *new_path = NULL;
95 int src_fd;
96 int dst_fd;
97
98 optind++;
99
100 if (old_path == NULL || strcmp(old_path, "-") == 0) {
101 src_fd = STDIN_FILENO;
102 opt |= GUNZIP_OPT_STDOUT;
103 } else {
104 src_fd = bb_xopen(old_path, O_RDONLY);
105
106 /* Get the time stamp on the input file. */
107 if (stat(old_path, &stat_buf) < 0) {
108 bb_error_msg_and_die("Couldn't stat file %s", old_path);
109 }
110 }
111
112 /* Check that the input is sane. */
113 if (isatty(src_fd) && ((opt & GUNZIP_OPT_FORCE) == 0)) {
114 bb_error_msg_and_die
115 ("compressed data not read from terminal. Use -f to force it.");
116 }
117
118 /* Set output filename and number */
119 if (opt & GUNZIP_OPT_TEST) {
120 dst_fd = bb_xopen("/dev/null", O_WRONLY); /* why does test use filenum 2 ? */
121 } else if (opt & GUNZIP_OPT_STDOUT) {
122 dst_fd = STDOUT_FILENO;
123 } else {
124 char *extension;
125
126 new_path = bb_xstrdup(old_path);
127
128 extension = strrchr(new_path, '.');
129#ifdef CONFIG_FEATURE_GUNZIP_UNCOMPRESS
130 if (extension && (strcmp(extension, ".Z") == 0)) {
131 *extension = '\0';
132 } else
133#endif
134 if (extension && (strcmp(extension, ".gz") == 0)) {
135 *extension = '\0';
136 } else if (extension && (strcmp(extension, ".tgz") == 0)) {
137 extension[2] = 'a';
138 extension[3] = 'r';
139 } else {
140 bb_error_msg_and_die("Invalid extension");
141 }
142
143 /* Open output file */
144 dst_fd = bb_xopen(new_path, O_WRONLY | O_CREAT);
145
146 /* Set permissions on the file */
147 chmod(new_path, stat_buf.st_mode);
148
149 /* If unzip succeeds remove the old file */
150 delete_path = old_path;
151 }
152
153 /* do the decompression, and cleanup */
154 if (bb_xread_char(src_fd) == 0x1f) {
155 unsigned char magic2;
156
157 magic2 = bb_xread_char(src_fd);
158#ifdef CONFIG_FEATURE_GUNZIP_UNCOMPRESS
159 if (magic2 == 0x9d) {
160 status = uncompress(src_fd, dst_fd);
161 } else
162#endif
163 if (magic2 == 0x8b) {
164 check_header_gzip(src_fd);
165 status = inflate_gunzip(src_fd, dst_fd);
166 if (status != 0) {
167 bb_error_msg_and_die("Error inflating");
168 }
169 } else {
170 bb_error_msg_and_die("Invalid magic");
171 }
172 } else {
173 bb_error_msg_and_die("Invalid magic");
174 }
175
176 if ((status != EXIT_SUCCESS) && (new_path)) {
177 /* Unzip failed, remove new path instead of old path */
178 delete_path = new_path;
179 }
180
181 if (dst_fd != STDOUT_FILENO) {
182 close(dst_fd);
183 }
184 if (src_fd != STDIN_FILENO) {
185 close(src_fd);
186 }
187
188 /* delete_path will be NULL if in test mode or from stdin */
189 if (delete_path && (unlink(delete_path) == -1)) {
190 bb_error_msg_and_die("Couldn't remove %s", delete_path);
191 }
192
193 free(new_path);
194
195 } while (optind < argc);
196
197 return status;
198}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Gzip implementation for busybox
4 *
5 * Based on GNU gzip Copyright (C) 1992-1993 Jean-loup Gailly.
6 *
7 * Originally adjusted for busybox by Charles P. Wright <cpw@unix.asb.com>
8 * "this is a stripped down version of gzip I put into busybox, it does
9 * only standard in to standard out with -9 compression. It also requires
10 * the zcat module for some important functions."
11 *
12 * Adjusted further by Erik Andersen <andersen@codepoet.org> to support
13 * files as well as stdin/stdout, and to generally behave itself wrt
14 * command line handling.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 *
30 */
31
32/* These defines are very important for BusyBox. Without these,
33 * huge chunks of ram are pre-allocated making the BusyBox bss
34 * size Freaking Huge(tm), which is a bad thing.*/
35#define SMALL_MEM
36#define DYN_ALLOC
37
38#include <stdlib.h>
39#include <stdio.h>
40#include <string.h>
41#include <unistd.h>
42#include <errno.h>
43#include <sys/types.h>
44#include <signal.h>
45#include <utime.h>
46#include <ctype.h>
47#include <sys/types.h>
48#include <unistd.h>
49#include <dirent.h>
50#include <fcntl.h>
51#include <time.h>
52#include "busybox.h"
53
54#define memzero(s, n) memset ((void *)(s), 0, (n))
55
56#ifndef RETSIGTYPE
57# define RETSIGTYPE void
58#endif
59
60typedef unsigned char uch;
61typedef unsigned short ush;
62typedef unsigned long ulg;
63
64/* Return codes from gzip */
65#define OK 0
66#define ERROR 1
67#define WARNING 2
68
69/* Compression methods (see algorithm.doc) */
70/* Only STORED and DEFLATED are supported by this BusyBox module */
71#define STORED 0
72/* methods 4 to 7 reserved */
73#define DEFLATED 8
74
75/* To save memory for 16 bit systems, some arrays are overlaid between
76 * the various modules:
77 * deflate: prev+head window d_buf l_buf outbuf
78 * unlzw: tab_prefix tab_suffix stack inbuf outbuf
79 * For compression, input is done in window[]. For decompression, output
80 * is done in window except for unlzw.
81 */
82
83#ifndef INBUFSIZ
84# ifdef SMALL_MEM
85# define INBUFSIZ 0x2000 /* input buffer size */
86# else
87# define INBUFSIZ 0x8000 /* input buffer size */
88# endif
89#endif
90#define INBUF_EXTRA 64 /* required by unlzw() */
91
92#ifndef OUTBUFSIZ
93# ifdef SMALL_MEM
94# define OUTBUFSIZ 8192 /* output buffer size */
95# else
96# define OUTBUFSIZ 16384 /* output buffer size */
97# endif
98#endif
99#define OUTBUF_EXTRA 2048 /* required by unlzw() */
100
101#ifndef DIST_BUFSIZE
102# ifdef SMALL_MEM
103# define DIST_BUFSIZE 0x2000 /* buffer for distances, see trees.c */
104# else
105# define DIST_BUFSIZE 0x8000 /* buffer for distances, see trees.c */
106# endif
107#endif
108
109#ifdef DYN_ALLOC
110# define DECLARE(type, array, size) static type * array
111# define ALLOC(type, array, size) { \
112 array = (type*)xcalloc((size_t)(((size)+1L)/2), 2*sizeof(type)); \
113 }
114# define FREE(array) {free(array), array=NULL;}
115#else
116# define DECLARE(type, array, size) static type array[size]
117# define ALLOC(type, array, size)
118# define FREE(array)
119#endif
120
121#define tab_suffix window
122#define tab_prefix prev /* hash link (see deflate.c) */
123#define head (prev+WSIZE) /* hash head (see deflate.c) */
124
125static long bytes_in; /* number of input bytes */
126
127#define isize bytes_in
128/* for compatibility with old zip sources (to be cleaned) */
129
130typedef int file_t; /* Do not use stdio */
131
132#define NO_FILE (-1) /* in memory compression */
133
134
135#define PACK_MAGIC "\037\036" /* Magic header for packed files */
136#define GZIP_MAGIC "\037\213" /* Magic header for gzip files, 1F 8B */
137#define OLD_GZIP_MAGIC "\037\236" /* Magic header for gzip 0.5 = freeze 1.x */
138#define LZH_MAGIC "\037\240" /* Magic header for SCO LZH Compress files */
139#define PKZIP_MAGIC "\120\113\003\004" /* Magic header for pkzip files */
140
141/* gzip flag byte */
142#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
143#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
144#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
145#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
146#define COMMENT 0x10 /* bit 4 set: file comment present */
147#define RESERVED 0xC0 /* bit 6,7: reserved */
148
149/* internal file attribute */
150#define UNKNOWN 0xffff
151#define BINARY 0
152#define ASCII 1
153
154#ifndef WSIZE
155# define WSIZE 0x8000 /* window size--must be a power of two, and */
156#endif /* at least 32K for zip's deflate method */
157
158#define MIN_MATCH 3
159#define MAX_MATCH 258
160/* The minimum and maximum match lengths */
161
162#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
163/* Minimum amount of lookahead, except at the end of the input file.
164 * See deflate.c for comments about the MIN_MATCH+1.
165 */
166
167#define MAX_DIST (WSIZE-MIN_LOOKAHEAD)
168/* In order to simplify the code, particularly on 16 bit machines, match
169 * distances are limited to MAX_DIST instead of WSIZE.
170 */
171
172/* put_byte is used for the compressed output */
173#define put_byte(c) {outbuf[outcnt++]=(uch)(c); if (outcnt==OUTBUFSIZ)\
174 flush_outbuf();}
175
176
177/* Output a 32 bit value to the bit stream, lsb first */
178#if 0
179#define put_long(n) { \
180 put_short((n) & 0xffff); \
181 put_short(((ulg)(n)) >> 16); \
182}
183#endif
184
185#define seekable() 0 /* force sequential output */
186#define translate_eol 0 /* no option -a yet */
187
188/* Diagnostic functions */
189#ifdef DEBUG
190# define Assert(cond,msg) {if(!(cond)) bb_error_msg(msg);}
191# define Trace(x) fprintf x
192# define Tracev(x) {if (verbose) fprintf x ;}
193# define Tracevv(x) {if (verbose>1) fprintf x ;}
194# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
195# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
196#else
197# define Assert(cond,msg)
198# define Trace(x)
199# define Tracev(x)
200# define Tracevv(x)
201# define Tracec(c,x)
202# define Tracecv(c,x)
203#endif
204
205#define WARN(msg) {if (!quiet) fprintf msg ; \
206 if (exit_code == OK) exit_code = WARNING;}
207
208#ifndef MAX_PATH_LEN
209# define MAX_PATH_LEN 1024 /* max pathname length */
210#endif
211
212
213 /* from zip.c: */
214static int zip(int in, int out);
215static int file_read(char *buf, unsigned size);
216
217 /* from gzip.c */
218static RETSIGTYPE abort_gzip(void);
219
220 /* from deflate.c */
221static void lm_init(ush * flags);
222static ulg deflate(void);
223
224 /* from trees.c */
225static void ct_init(ush * attr, int *methodp);
226static int ct_tally(int dist, int lc);
227static ulg flush_block(char *buf, ulg stored_len, int eof);
228
229 /* from bits.c */
230static void bi_init(file_t zipfile);
231static void send_bits(int value, int length);
232static unsigned bi_reverse(unsigned value, int length);
233static void bi_windup(void);
234static void copy_block(char *buf, unsigned len, int header);
235static int (*read_buf) (char *buf, unsigned size);
236
237 /* from util.c: */
238static void flush_outbuf(void);
239
240/* lzw.h -- define the lzw functions.
241 * Copyright (C) 1992-1993 Jean-loup Gailly.
242 * This is free software; you can redistribute it and/or modify it under the
243 * terms of the GNU General Public License, see the file COPYING.
244 */
245
246#if !defined(OF) && defined(lint)
247# include "gzip.h"
248#endif
249
250#ifndef BITS
251# define BITS 16
252#endif
253#define INIT_BITS 9 /* Initial number of bits per code */
254
255#define BIT_MASK 0x1f /* Mask for 'number of compression bits' */
256/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free.
257 * It's a pity that old uncompress does not check bit 0x20. That makes
258 * extension of the format actually undesirable because old compress
259 * would just crash on the new format instead of giving a meaningful
260 * error message. It does check the number of bits, but it's more
261 * helpful to say "unsupported format, get a new version" than
262 * "can only handle 16 bits".
263 */
264
265/* tailor.h -- target dependent definitions
266 * Copyright (C) 1992-1993 Jean-loup Gailly.
267 * This is free software; you can redistribute it and/or modify it under the
268 * terms of the GNU General Public License, see the file COPYING.
269 */
270
271/* The target dependent definitions should be defined here only.
272 * The target dependent functions should be defined in tailor.c.
273 */
274
275
276 /* Common defaults */
277
278#ifndef OS_CODE
279# define OS_CODE 0x03 /* assume Unix */
280#endif
281
282#ifndef PATH_SEP
283# define PATH_SEP '/'
284#endif
285
286#ifndef OPTIONS_VAR
287# define OPTIONS_VAR "GZIP"
288#endif
289
290#ifndef Z_SUFFIX
291# define Z_SUFFIX ".gz"
292#endif
293
294#ifdef MAX_EXT_CHARS
295# define MAX_SUFFIX MAX_EXT_CHARS
296#else
297# define MAX_SUFFIX 30
298#endif
299
300 /* global buffers */
301
302DECLARE(uch, inbuf, INBUFSIZ + INBUF_EXTRA);
303DECLARE(uch, outbuf, OUTBUFSIZ + OUTBUF_EXTRA);
304DECLARE(ush, d_buf, DIST_BUFSIZE);
305DECLARE(uch, window, 2L * WSIZE);
306DECLARE(ush, tab_prefix, 1L << BITS);
307
308static int foreground; /* set if program run in foreground */
309static int method = DEFLATED; /* compression method */
310static int exit_code = OK; /* program exit code */
311static int part_nb; /* number of parts in .gz file */
312static long time_stamp; /* original time stamp (modification time) */
313static long ifile_size; /* input file size, -1 for devices (debug only) */
314static char z_suffix[MAX_SUFFIX + 1]; /* default suffix (can be set with --suffix) */
315static int z_len; /* strlen(z_suffix) */
316
317static int ifd; /* input file descriptor */
318static int ofd; /* output file descriptor */
319static unsigned insize; /* valid bytes in inbuf */
320static unsigned outcnt; /* bytes in output buffer */
321
322
323/* Output a 16 bit value, lsb first */
324static void put_short(ush w)
325{
326 if (outcnt < OUTBUFSIZ - 2) {
327 outbuf[outcnt++] = (uch) ((w) & 0xff);
328 outbuf[outcnt++] = (uch) ((ush) (w) >> 8);
329 } else {
330 put_byte((uch) ((w) & 0xff));
331 put_byte((uch) ((ush) (w) >> 8));
332 }
333}
334
335/* ========================================================================
336 * Signal and error handler.
337 */
338static void abort_gzip()
339{
340 exit(ERROR);
341}
342
343/* ===========================================================================
344 * Clear input and output buffers
345 */
346static void clear_bufs(void)
347{
348 outcnt = 0;
349 insize = 0;
350 bytes_in = 0L;
351}
352
353static void write_bb_error_msg(void)
354{
355 fputc('\n', stderr);
356 bb_perror_nomsg();
357 abort_gzip();
358}
359
360/* ===========================================================================
361 * Does the same as write(), but also handles partial pipe writes and checks
362 * for error return.
363 */
364static void write_buf(int fd, void *buf, unsigned cnt)
365{
366 unsigned n;
367
368 while ((n = write(fd, buf, cnt)) != cnt) {
369 if (n == (unsigned) (-1)) {
370 write_bb_error_msg();
371 }
372 cnt -= n;
373 buf = (void *) ((char *) buf + n);
374 }
375}
376
377/* ===========================================================================
378 * Run a set of bytes through the crc shift register. If s is a NULL
379 * pointer, then initialize the crc shift register contents instead.
380 * Return the current crc in either case.
381 */
382static ulg updcrc(uch * s, unsigned n)
383{
384 static ulg crc = (ulg) 0xffffffffL; /* shift register contents */
385 register ulg c; /* temporary variable */
386 static unsigned long crc_32_tab[256];
387
388 if (crc_32_tab[1] == 0x00000000L) {
389 unsigned long csr; /* crc shift register */
390 const unsigned long e = 0xedb88320L; /* polynomial exclusive-or pattern */
391 int i; /* counter for all possible eight bit values */
392 int k; /* byte being shifted into crc apparatus */
393
394 /* Compute table of CRC's. */
395 for (i = 1; i < 256; i++) {
396 csr = i;
397 /* The idea to initialize the register with the byte instead of
398 * zero was stolen from Haruhiko Okumura's ar002
399 */
400 for (k = 8; k; k--)
401 csr = csr & 1 ? (csr >> 1) ^ e : csr >> 1;
402 crc_32_tab[i] = csr;
403 }
404 }
405
406 if (s == NULL) {
407 c = 0xffffffffL;
408 } else {
409 c = crc;
410 if (n)
411 do {
412 c = crc_32_tab[((int) c ^ (*s++)) & 0xff] ^ (c >> 8);
413 } while (--n);
414 }
415 crc = c;
416 return c ^ 0xffffffffL; /* (instead of ~c for 64-bit machines) */
417}
418
419/* bits.c -- output variable-length bit strings
420 * Copyright (C) 1992-1993 Jean-loup Gailly
421 * This is free software; you can redistribute it and/or modify it under the
422 * terms of the GNU General Public License, see the file COPYING.
423 */
424
425
426/*
427 * PURPOSE
428 *
429 * Output variable-length bit strings. Compression can be done
430 * to a file or to memory. (The latter is not supported in this version.)
431 *
432 * DISCUSSION
433 *
434 * The PKZIP "deflate" file format interprets compressed file data
435 * as a sequence of bits. Multi-bit strings in the file may cross
436 * byte boundaries without restriction.
437 *
438 * The first bit of each byte is the low-order bit.
439 *
440 * The routines in this file allow a variable-length bit value to
441 * be output right-to-left (useful for literal values). For
442 * left-to-right output (useful for code strings from the tree routines),
443 * the bits must have been reversed first with bi_reverse().
444 *
445 * For in-memory compression, the compressed bit stream goes directly
446 * into the requested output buffer. The input data is read in blocks
447 * by the mem_read() function. The buffer is limited to 64K on 16 bit
448 * machines.
449 *
450 * INTERFACE
451 *
452 * void bi_init (FILE *zipfile)
453 * Initialize the bit string routines.
454 *
455 * void send_bits (int value, int length)
456 * Write out a bit string, taking the source bits right to
457 * left.
458 *
459 * int bi_reverse (int value, int length)
460 * Reverse the bits of a bit string, taking the source bits left to
461 * right and emitting them right to left.
462 *
463 * void bi_windup (void)
464 * Write out any remaining bits in an incomplete byte.
465 *
466 * void copy_block(char *buf, unsigned len, int header)
467 * Copy a stored block to the zip file, storing first the length and
468 * its one's complement if requested.
469 *
470 */
471
472/* ===========================================================================
473 * Local data used by the "bit string" routines.
474 */
475
476static file_t zfile; /* output gzip file */
477
478static unsigned short bi_buf;
479
480/* Output buffer. bits are inserted starting at the bottom (least significant
481 * bits).
482 */
483
484#define Buf_size (8 * 2*sizeof(char))
485/* Number of bits used within bi_buf. (bi_buf might be implemented on
486 * more than 16 bits on some systems.)
487 */
488
489static int bi_valid;
490
491/* Current input function. Set to mem_read for in-memory compression */
492
493#ifdef DEBUG
494ulg bits_sent; /* bit length of the compressed data */
495#endif
496
497/* ===========================================================================
498 * Initialize the bit string routines.
499 */
500static void bi_init(file_t zipfile)
501{
502 zfile = zipfile;
503 bi_buf = 0;
504 bi_valid = 0;
505#ifdef DEBUG
506 bits_sent = 0L;
507#endif
508
509 /* Set the defaults for file compression. They are set by memcompress
510 * for in-memory compression.
511 */
512 if (zfile != NO_FILE) {
513 read_buf = file_read;
514 }
515}
516
517/* ===========================================================================
518 * Send a value on a given number of bits.
519 * IN assertion: length <= 16 and value fits in length bits.
520 */
521static void send_bits(int value, int length)
522{
523#ifdef DEBUG
524 Tracev((stderr, " l %2d v %4x ", length, value));
525 Assert(length > 0 && length <= 15, "invalid length");
526 bits_sent += (ulg) length;
527#endif
528 /* If not enough room in bi_buf, use (valid) bits from bi_buf and
529 * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
530 * unused bits in value.
531 */
532 if (bi_valid > (int) Buf_size - length) {
533 bi_buf |= (value << bi_valid);
534 put_short(bi_buf);
535 bi_buf = (ush) value >> (Buf_size - bi_valid);
536 bi_valid += length - Buf_size;
537 } else {
538 bi_buf |= value << bi_valid;
539 bi_valid += length;
540 }
541}
542
543/* ===========================================================================
544 * Reverse the first len bits of a code, using straightforward code (a faster
545 * method would use a table)
546 * IN assertion: 1 <= len <= 15
547 */
548static unsigned bi_reverse(unsigned code, int len)
549{
550 register unsigned res = 0;
551
552 do {
553 res |= code & 1;
554 code >>= 1, res <<= 1;
555 } while (--len > 0);
556 return res >> 1;
557}
558
559/* ===========================================================================
560 * Write out any remaining bits in an incomplete byte.
561 */
562static void bi_windup()
563{
564 if (bi_valid > 8) {
565 put_short(bi_buf);
566 } else if (bi_valid > 0) {
567 put_byte(bi_buf);
568 }
569 bi_buf = 0;
570 bi_valid = 0;
571#ifdef DEBUG
572 bits_sent = (bits_sent + 7) & ~7;
573#endif
574}
575
576/* ===========================================================================
577 * Copy a stored block to the zip file, storing first the length and its
578 * one's complement if requested.
579 */
580static void copy_block(char *buf, unsigned len, int header)
581{
582 bi_windup(); /* align on byte boundary */
583
584 if (header) {
585 put_short((ush) len);
586 put_short((ush) ~ len);
587#ifdef DEBUG
588 bits_sent += 2 * 16;
589#endif
590 }
591#ifdef DEBUG
592 bits_sent += (ulg) len << 3;
593#endif
594 while (len--) {
595 put_byte(*buf++);
596 }
597}
598
599/* deflate.c -- compress data using the deflation algorithm
600 * Copyright (C) 1992-1993 Jean-loup Gailly
601 * This is free software; you can redistribute it and/or modify it under the
602 * terms of the GNU General Public License, see the file COPYING.
603 */
604
605/*
606 * PURPOSE
607 *
608 * Identify new text as repetitions of old text within a fixed-
609 * length sliding window trailing behind the new text.
610 *
611 * DISCUSSION
612 *
613 * The "deflation" process depends on being able to identify portions
614 * of the input text which are identical to earlier input (within a
615 * sliding window trailing behind the input currently being processed).
616 *
617 * The most straightforward technique turns out to be the fastest for
618 * most input files: try all possible matches and select the longest.
619 * The key feature of this algorithm is that insertions into the string
620 * dictionary are very simple and thus fast, and deletions are avoided
621 * completely. Insertions are performed at each input character, whereas
622 * string matches are performed only when the previous match ends. So it
623 * is preferable to spend more time in matches to allow very fast string
624 * insertions and avoid deletions. The matching algorithm for small
625 * strings is inspired from that of Rabin & Karp. A brute force approach
626 * is used to find longer strings when a small match has been found.
627 * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
628 * (by Leonid Broukhis).
629 * A previous version of this file used a more sophisticated algorithm
630 * (by Fiala and Greene) which is guaranteed to run in linear amortized
631 * time, but has a larger average cost, uses more memory and is patented.
632 * However the F&G algorithm may be faster for some highly redundant
633 * files if the parameter max_chain_length (described below) is too large.
634 *
635 * ACKNOWLEDGMENTS
636 *
637 * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
638 * I found it in 'freeze' written by Leonid Broukhis.
639 * Thanks to many info-zippers for bug reports and testing.
640 *
641 * REFERENCES
642 *
643 * APPNOTE.TXT documentation file in PKZIP 1.93a distribution.
644 *
645 * A description of the Rabin and Karp algorithm is given in the book
646 * "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
647 *
648 * Fiala,E.R., and Greene,D.H.
649 * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
650 *
651 * INTERFACE
652 *
653 * void lm_init (int pack_level, ush *flags)
654 * Initialize the "longest match" routines for a new file
655 *
656 * ulg deflate (void)
657 * Processes a new input file and return its compressed length. Sets
658 * the compressed length, crc, deflate flags and internal file
659 * attributes.
660 */
661
662
663/* ===========================================================================
664 * Configuration parameters
665 */
666
667/* Compile with MEDIUM_MEM to reduce the memory requirements or
668 * with SMALL_MEM to use as little memory as possible. Use BIG_MEM if the
669 * entire input file can be held in memory (not possible on 16 bit systems).
670 * Warning: defining these symbols affects HASH_BITS (see below) and thus
671 * affects the compression ratio. The compressed output
672 * is still correct, and might even be smaller in some cases.
673 */
674
675#ifdef SMALL_MEM
676# define HASH_BITS 13 /* Number of bits used to hash strings */
677#endif
678#ifdef MEDIUM_MEM
679# define HASH_BITS 14
680#endif
681#ifndef HASH_BITS
682# define HASH_BITS 15
683 /* For portability to 16 bit machines, do not use values above 15. */
684#endif
685
686/* To save space (see unlzw.c), we overlay prev+head with tab_prefix and
687 * window with tab_suffix. Check that we can do this:
688 */
689#if (WSIZE<<1) > (1<<BITS)
690# error cannot overlay window with tab_suffix and prev with tab_prefix0
691#endif
692#if HASH_BITS > BITS-1
693# error cannot overlay head with tab_prefix1
694#endif
695#define HASH_SIZE (unsigned)(1<<HASH_BITS)
696#define HASH_MASK (HASH_SIZE-1)
697#define WMASK (WSIZE-1)
698/* HASH_SIZE and WSIZE must be powers of two */
699#define NIL 0
700/* Tail of hash chains */
701#define FAST 4
702#define SLOW 2
703/* speed options for the general purpose bit flag */
704#ifndef TOO_FAR
705# define TOO_FAR 4096
706#endif
707/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
708/* ===========================================================================
709 * Local data used by the "longest match" routines.
710 */
711typedef ush Pos;
712typedef unsigned IPos;
713
714/* A Pos is an index in the character window. We use short instead of int to
715 * save space in the various tables. IPos is used only for parameter passing.
716 */
717
718/* DECLARE(uch, window, 2L*WSIZE); */
719/* Sliding window. Input bytes are read into the second half of the window,
720 * and move to the first half later to keep a dictionary of at least WSIZE
721 * bytes. With this organization, matches are limited to a distance of
722 * WSIZE-MAX_MATCH bytes, but this ensures that IO is always
723 * performed with a length multiple of the block size. Also, it limits
724 * the window size to 64K, which is quite useful on MSDOS.
725 * To do: limit the window size to WSIZE+BSZ if SMALL_MEM (the code would
726 * be less efficient).
727 */
728
729/* DECLARE(Pos, prev, WSIZE); */
730/* Link to older string with same hash index. To limit the size of this
731 * array to 64K, this link is maintained only for the last 32K strings.
732 * An index in this array is thus a window index modulo 32K.
733 */
734
735/* DECLARE(Pos, head, 1<<HASH_BITS); */
736/* Heads of the hash chains or NIL. */
737
738static const ulg window_size = (ulg) 2 * WSIZE;
739
740/* window size, 2*WSIZE except for MMAP or BIG_MEM, where it is the
741 * input file length plus MIN_LOOKAHEAD.
742 */
743
744static long block_start;
745
746/* window position at the beginning of the current output block. Gets
747 * negative when the window is moved backwards.
748 */
749
750static unsigned ins_h; /* hash index of string to be inserted */
751
752#define H_SHIFT ((HASH_BITS+MIN_MATCH-1)/MIN_MATCH)
753/* Number of bits by which ins_h and del_h must be shifted at each
754 * input step. It must be such that after MIN_MATCH steps, the oldest
755 * byte no longer takes part in the hash key, that is:
756 * H_SHIFT * MIN_MATCH >= HASH_BITS
757 */
758
759static unsigned int prev_length;
760
761/* Length of the best match at previous step. Matches not greater than this
762 * are discarded. This is used in the lazy match evaluation.
763 */
764
765static unsigned strstart; /* start of string to insert */
766static unsigned match_start; /* start of matching string */
767static int eofile; /* flag set at end of input file */
768static unsigned lookahead; /* number of valid bytes ahead in window */
769
770static const unsigned max_chain_length = 4096;
771
772/* To speed up deflation, hash chains are never searched beyond this length.
773 * A higher limit improves compression ratio but degrades the speed.
774 */
775
776static const unsigned int max_lazy_match = 258;
777
778/* Attempt to find a better match only when the current match is strictly
779 * smaller than this value. This mechanism is used only for compression
780 * levels >= 4.
781 */
782#define max_insert_length max_lazy_match
783/* Insert new strings in the hash table only if the match length
784 * is not greater than this length. This saves time but degrades compression.
785 * max_insert_length is used only for compression levels <= 3.
786 */
787
788static const unsigned good_match = 32;
789
790/* Use a faster search when the previous match is longer than this */
791
792
793/* Values for max_lazy_match, good_match and max_chain_length, depending on
794 * the desired pack level (0..9). The values given below have been tuned to
795 * exclude worst case performance for pathological files. Better values may be
796 * found for specific files.
797 */
798
799static const int nice_match = 258; /* Stop searching when current match exceeds this */
800
801/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
802 * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
803 * meaning.
804 */
805
806#define EQUAL 0
807/* result of memcmp for equal strings */
808
809/* ===========================================================================
810 * Prototypes for local functions.
811 */
812static void fill_window(void);
813
814static int longest_match(IPos cur_match);
815
816#ifdef DEBUG
817static void check_match(IPos start, IPos match, int length);
818#endif
819
820/* ===========================================================================
821 * Update a hash value with the given input byte
822 * IN assertion: all calls to to UPDATE_HASH are made with consecutive
823 * input characters, so that a running hash key can be computed from the
824 * previous key instead of complete recalculation each time.
825 */
826#define UPDATE_HASH(h,c) (h = (((h)<<H_SHIFT) ^ (c)) & HASH_MASK)
827
828/* ===========================================================================
829 * Insert string s in the dictionary and set match_head to the previous head
830 * of the hash chain (the most recent string with same hash key). Return
831 * the previous length of the hash chain.
832 * IN assertion: all calls to to INSERT_STRING are made with consecutive
833 * input characters and the first MIN_MATCH bytes of s are valid
834 * (except for the last MIN_MATCH-1 bytes of the input file).
835 */
836#define INSERT_STRING(s, match_head) \
837 (UPDATE_HASH(ins_h, window[(s) + MIN_MATCH-1]), \
838 prev[(s) & WMASK] = match_head = head[ins_h], \
839 head[ins_h] = (s))
840
841/* ===========================================================================
842 * Initialize the "longest match" routines for a new file
843 */
844static void lm_init(ush * flags)
845{
846 register unsigned j;
847
848 /* Initialize the hash table. */
849 memzero((char *) head, HASH_SIZE * sizeof(*head));
850 /* prev will be initialized on the fly */
851
852 *flags |= SLOW;
853 /* ??? reduce max_chain_length for binary files */
854
855 strstart = 0;
856 block_start = 0L;
857
858 lookahead = read_buf((char *) window,
859 sizeof(int) <= 2 ? (unsigned) WSIZE : 2 * WSIZE);
860
861 if (lookahead == 0 || lookahead == (unsigned) EOF) {
862 eofile = 1, lookahead = 0;
863 return;
864 }
865 eofile = 0;
866 /* Make sure that we always have enough lookahead. This is important
867 * if input comes from a device such as a tty.
868 */
869 while (lookahead < MIN_LOOKAHEAD && !eofile)
870 fill_window();
871
872 ins_h = 0;
873 for (j = 0; j < MIN_MATCH - 1; j++)
874 UPDATE_HASH(ins_h, window[j]);
875 /* If lookahead < MIN_MATCH, ins_h is garbage, but this is
876 * not important since only literal bytes will be emitted.
877 */
878}
879
880/* ===========================================================================
881 * Set match_start to the longest match starting at the given string and
882 * return its length. Matches shorter or equal to prev_length are discarded,
883 * in which case the result is equal to prev_length and match_start is
884 * garbage.
885 * IN assertions: cur_match is the head of the hash chain for the current
886 * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
887 */
888
889/* For MSDOS, OS/2 and 386 Unix, an optimized version is in match.asm or
890 * match.s. The code is functionally equivalent, so you can use the C version
891 * if desired.
892 */
893static int longest_match(IPos cur_match)
894{
895 unsigned chain_length = max_chain_length; /* max hash chain length */
896 register uch *scan = window + strstart; /* current string */
897 register uch *match; /* matched string */
898 register int len; /* length of current match */
899 int best_len = prev_length; /* best match length so far */
900 IPos limit =
901 strstart > (IPos) MAX_DIST ? strstart - (IPos) MAX_DIST : NIL;
902 /* Stop when cur_match becomes <= limit. To simplify the code,
903 * we prevent matches with the string of window index 0.
904 */
905
906/* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
907 * It is easy to get rid of this optimization if necessary.
908 */
909#if HASH_BITS < 8 || MAX_MATCH != 258
910# error Code too clever
911#endif
912 register uch *strend = window + strstart + MAX_MATCH;
913 register uch scan_end1 = scan[best_len - 1];
914 register uch scan_end = scan[best_len];
915
916 /* Do not waste too much time if we already have a good match: */
917 if (prev_length >= good_match) {
918 chain_length >>= 2;
919 }
920 Assert(strstart <= window_size - MIN_LOOKAHEAD, "insufficient lookahead");
921
922 do {
923 Assert(cur_match < strstart, "no future");
924 match = window + cur_match;
925
926 /* Skip to next match if the match length cannot increase
927 * or if the match length is less than 2:
928 */
929 if (match[best_len] != scan_end ||
930 match[best_len - 1] != scan_end1 ||
931 *match != *scan || *++match != scan[1])
932 continue;
933
934 /* The check at best_len-1 can be removed because it will be made
935 * again later. (This heuristic is not always a win.)
936 * It is not necessary to compare scan[2] and match[2] since they
937 * are always equal when the other bytes match, given that
938 * the hash keys are equal and that HASH_BITS >= 8.
939 */
940 scan += 2, match++;
941
942 /* We check for insufficient lookahead only every 8th comparison;
943 * the 256th check will be made at strstart+258.
944 */
945 do {
946 } while (*++scan == *++match && *++scan == *++match &&
947 *++scan == *++match && *++scan == *++match &&
948 *++scan == *++match && *++scan == *++match &&
949 *++scan == *++match && *++scan == *++match && scan < strend);
950
951 len = MAX_MATCH - (int) (strend - scan);
952 scan = strend - MAX_MATCH;
953
954 if (len > best_len) {
955 match_start = cur_match;
956 best_len = len;
957 if (len >= nice_match)
958 break;
959 scan_end1 = scan[best_len - 1];
960 scan_end = scan[best_len];
961 }
962 } while ((cur_match = prev[cur_match & WMASK]) > limit
963 && --chain_length != 0);
964
965 return best_len;
966}
967
968#ifdef DEBUG
969/* ===========================================================================
970 * Check that the match at match_start is indeed a match.
971 */
972static void check_match(IPos start, IPos match, int length)
973{
974 /* check that the match is indeed a match */
975 if (memcmp((char *) window + match,
976 (char *) window + start, length) != EQUAL) {
977 bb_error_msg(" start %d, match %d, length %d", start, match, length);
978 bb_error_msg("invalid match");
979 }
980 if (verbose > 1) {
981 bb_error_msg("\\[%d,%d]", start - match, length);
982 do {
983 putc(window[start++], stderr);
984 } while (--length != 0);
985 }
986}
987#else
988# define check_match(start, match, length)
989#endif
990
991/* ===========================================================================
992 * Fill the window when the lookahead becomes insufficient.
993 * Updates strstart and lookahead, and sets eofile if end of input file.
994 * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0
995 * OUT assertions: at least one byte has been read, or eofile is set;
996 * file reads are performed for at least two bytes (required for the
997 * translate_eol option).
998 */
999static void fill_window()
1000{
1001 register unsigned n, m;
1002 unsigned more =
1003 (unsigned) (window_size - (ulg) lookahead - (ulg) strstart);
1004 /* Amount of free space at the end of the window. */
1005
1006 /* If the window is almost full and there is insufficient lookahead,
1007 * move the upper half to the lower one to make room in the upper half.
1008 */
1009 if (more == (unsigned) EOF) {
1010 /* Very unlikely, but possible on 16 bit machine if strstart == 0
1011 * and lookahead == 1 (input done one byte at time)
1012 */
1013 more--;
1014 } else if (strstart >= WSIZE + MAX_DIST) {
1015 /* By the IN assertion, the window is not empty so we can't confuse
1016 * more == 0 with more == 64K on a 16 bit machine.
1017 */
1018 Assert(window_size == (ulg) 2 * WSIZE, "no sliding with BIG_MEM");
1019
1020 memcpy((char *) window, (char *) window + WSIZE, (unsigned) WSIZE);
1021 match_start -= WSIZE;
1022 strstart -= WSIZE; /* we now have strstart >= MAX_DIST: */
1023
1024 block_start -= (long) WSIZE;
1025
1026 for (n = 0; n < HASH_SIZE; n++) {
1027 m = head[n];
1028 head[n] = (Pos) (m >= WSIZE ? m - WSIZE : NIL);
1029 }
1030 for (n = 0; n < WSIZE; n++) {
1031 m = prev[n];
1032 prev[n] = (Pos) (m >= WSIZE ? m - WSIZE : NIL);
1033 /* If n is not on any hash chain, prev[n] is garbage but
1034 * its value will never be used.
1035 */
1036 }
1037 more += WSIZE;
1038 }
1039 /* At this point, more >= 2 */
1040 if (!eofile) {
1041 n = read_buf((char *) window + strstart + lookahead, more);
1042 if (n == 0 || n == (unsigned) EOF) {
1043 eofile = 1;
1044 } else {
1045 lookahead += n;
1046 }
1047 }
1048}
1049
1050/* ===========================================================================
1051 * Flush the current block, with given end-of-file flag.
1052 * IN assertion: strstart is set to the end of the current match.
1053 */
1054#define FLUSH_BLOCK(eof) \
1055 flush_block(block_start >= 0L ? (char*)&window[(unsigned)block_start] : \
1056 (char*)NULL, (long)strstart - block_start, (eof))
1057
1058/* ===========================================================================
1059 * Same as above, but achieves better compression. We use a lazy
1060 * evaluation for matches: a match is finally adopted only if there is
1061 * no better match at the next window position.
1062 */
1063static ulg deflate()
1064{
1065 IPos hash_head; /* head of hash chain */
1066 IPos prev_match; /* previous match */
1067 int flush; /* set if current block must be flushed */
1068 int match_available = 0; /* set if previous match exists */
1069 register unsigned match_length = MIN_MATCH - 1; /* length of best match */
1070
1071 /* Process the input block. */
1072 while (lookahead != 0) {
1073 /* Insert the string window[strstart .. strstart+2] in the
1074 * dictionary, and set hash_head to the head of the hash chain:
1075 */
1076 INSERT_STRING(strstart, hash_head);
1077
1078 /* Find the longest match, discarding those <= prev_length.
1079 */
1080 prev_length = match_length, prev_match = match_start;
1081 match_length = MIN_MATCH - 1;
1082
1083 if (hash_head != NIL && prev_length < max_lazy_match &&
1084 strstart - hash_head <= MAX_DIST) {
1085 /* To simplify the code, we prevent matches with the string
1086 * of window index 0 (in particular we have to avoid a match
1087 * of the string with itself at the start of the input file).
1088 */
1089 match_length = longest_match(hash_head);
1090 /* longest_match() sets match_start */
1091 if (match_length > lookahead)
1092 match_length = lookahead;
1093
1094 /* Ignore a length 3 match if it is too distant: */
1095 if (match_length == MIN_MATCH && strstart - match_start > TOO_FAR) {
1096 /* If prev_match is also MIN_MATCH, match_start is garbage
1097 * but we will ignore the current match anyway.
1098 */
1099 match_length--;
1100 }
1101 }
1102 /* If there was a match at the previous step and the current
1103 * match is not better, output the previous match:
1104 */
1105 if (prev_length >= MIN_MATCH && match_length <= prev_length) {
1106
1107 check_match(strstart - 1, prev_match, prev_length);
1108
1109 flush =
1110 ct_tally(strstart - 1 - prev_match, prev_length - MIN_MATCH);
1111
1112 /* Insert in hash table all strings up to the end of the match.
1113 * strstart-1 and strstart are already inserted.
1114 */
1115 lookahead -= prev_length - 1;
1116 prev_length -= 2;
1117 do {
1118 strstart++;
1119 INSERT_STRING(strstart, hash_head);
1120 /* strstart never exceeds WSIZE-MAX_MATCH, so there are
1121 * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH
1122 * these bytes are garbage, but it does not matter since the
1123 * next lookahead bytes will always be emitted as literals.
1124 */
1125 } while (--prev_length != 0);
1126 match_available = 0;
1127 match_length = MIN_MATCH - 1;
1128 strstart++;
1129 if (flush)
1130 FLUSH_BLOCK(0), block_start = strstart;
1131
1132 } else if (match_available) {
1133 /* If there was no match at the previous position, output a
1134 * single literal. If there was a match but the current match
1135 * is longer, truncate the previous match to a single literal.
1136 */
1137 Tracevv((stderr, "%c", window[strstart - 1]));
1138 if (ct_tally(0, window[strstart - 1])) {
1139 FLUSH_BLOCK(0), block_start = strstart;
1140 }
1141 strstart++;
1142 lookahead--;
1143 } else {
1144 /* There is no previous match to compare with, wait for
1145 * the next step to decide.
1146 */
1147 match_available = 1;
1148 strstart++;
1149 lookahead--;
1150 }
1151 Assert(strstart <= isize && lookahead <= isize, "a bit too far");
1152
1153 /* Make sure that we always have enough lookahead, except
1154 * at the end of the input file. We need MAX_MATCH bytes
1155 * for the next match, plus MIN_MATCH bytes to insert the
1156 * string following the next match.
1157 */
1158 while (lookahead < MIN_LOOKAHEAD && !eofile)
1159 fill_window();
1160 }
1161 if (match_available)
1162 ct_tally(0, window[strstart - 1]);
1163
1164 return FLUSH_BLOCK(1); /* eof */
1165}
1166
1167/* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
1168 * Copyright (C) 1992-1993 Jean-loup Gailly
1169 * The unzip code was written and put in the public domain by Mark Adler.
1170 * Portions of the lzw code are derived from the public domain 'compress'
1171 * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
1172 * Ken Turkowski, Dave Mack and Peter Jannesen.
1173 *
1174 * See the license_msg below and the file COPYING for the software license.
1175 * See the file algorithm.doc for the compression algorithms and file formats.
1176 */
1177
1178/* Compress files with zip algorithm and 'compress' interface.
1179 * See usage() and help() functions below for all options.
1180 * Outputs:
1181 * file.gz: compressed file with same mode, owner, and utimes
1182 * or stdout with -c option or if stdin used as input.
1183 * If the output file name had to be truncated, the original name is kept
1184 * in the compressed file.
1185 */
1186
1187 /* configuration */
1188
1189typedef struct dirent dir_type;
1190
1191typedef RETSIGTYPE(*sig_type) (int);
1192
1193/* ======================================================================== */
1194int gzip_main(int argc, char **argv)
1195{
1196 int result;
1197 int inFileNum;
1198 int outFileNum;
1199 struct stat statBuf;
1200 char *delFileName;
1201 int tostdout = 0;
1202 int force = 0;
1203 int opt;
1204
1205 while ((opt = getopt(argc, argv, "cf123456789dq")) != -1) {
1206 switch (opt) {
1207 case 'c':
1208 tostdout = 1;
1209 break;
1210 case 'f':
1211 force = 1;
1212 break;
1213 /* Ignore 1-9 (compression level) options */
1214 case '1':
1215 case '2':
1216 case '3':
1217 case '4':
1218 case '5':
1219 case '6':
1220 case '7':
1221 case '8':
1222 case '9':
1223 break;
1224 case 'q':
1225 break;
1226#ifdef CONFIG_GUNZIP
1227 case 'd':
1228 optind = 1;
1229 return gunzip_main(argc, argv);
1230#endif
1231 default:
1232 bb_show_usage();
1233 }
1234 }
1235
1236 foreground = signal(SIGINT, SIG_IGN) != SIG_IGN;
1237 if (foreground) {
1238 (void) signal(SIGINT, (sig_type) abort_gzip);
1239 }
1240#ifdef SIGTERM
1241 if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
1242 (void) signal(SIGTERM, (sig_type) abort_gzip);
1243 }
1244#endif
1245#ifdef SIGHUP
1246 if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
1247 (void) signal(SIGHUP, (sig_type) abort_gzip);
1248 }
1249#endif
1250
1251 strncpy(z_suffix, Z_SUFFIX, sizeof(z_suffix) - 1);
1252 z_len = strlen(z_suffix);
1253
1254 /* Allocate all global buffers (for DYN_ALLOC option) */
1255 ALLOC(uch, inbuf, INBUFSIZ + INBUF_EXTRA);
1256 ALLOC(uch, outbuf, OUTBUFSIZ + OUTBUF_EXTRA);
1257 ALLOC(ush, d_buf, DIST_BUFSIZE);
1258 ALLOC(uch, window, 2L * WSIZE);
1259 ALLOC(ush, tab_prefix, 1L << BITS);
1260
1261 clear_bufs();
1262 part_nb = 0;
1263
1264 if (optind == argc) {
1265 time_stamp = 0;
1266 ifile_size = -1L;
1267 zip(STDIN_FILENO, STDOUT_FILENO);
1268 } else {
1269 int i;
1270
1271 for (i = optind; i < argc; i++) {
1272 char *path = NULL;
1273
1274 if (strcmp(argv[i], "-") == 0) {
1275 time_stamp = 0;
1276 ifile_size = -1L;
1277 inFileNum = STDIN_FILENO;
1278 outFileNum = STDOUT_FILENO;
1279 } else {
1280 inFileNum = open(argv[i], O_RDONLY);
1281 if (inFileNum < 0 || fstat(inFileNum, &statBuf) < 0)
1282 bb_perror_msg_and_die("%s", argv[i]);
1283 time_stamp = statBuf.st_ctime;
1284 ifile_size = statBuf.st_size;
1285
1286 if (!tostdout) {
1287 path = xmalloc(strlen(argv[i]) + 4);
1288 strcpy(path, argv[i]);
1289 strcat(path, ".gz");
1290
1291 /* Open output file */
1292#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
1293 outFileNum =
1294 open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW);
1295#else
1296 outFileNum = open(path, O_RDWR | O_CREAT | O_EXCL);
1297#endif
1298 if (outFileNum < 0) {
1299 bb_perror_msg("%s", path);
1300 free(path);
1301 continue;
1302 }
1303
1304 /* Set permissions on the file */
1305 fchmod(outFileNum, statBuf.st_mode);
1306 } else
1307 outFileNum = STDOUT_FILENO;
1308 }
1309
1310 if (path == NULL && isatty(outFileNum) && force == 0) {
1311 bb_error_msg
1312 ("compressed data not written to a terminal. Use -f to force compression.");
1313 free(path);
1314 continue;
1315 }
1316
1317 result = zip(inFileNum, outFileNum);
1318
1319 if (path != NULL) {
1320 close(inFileNum);
1321 close(outFileNum);
1322
1323 /* Delete the original file */
1324 if (result == OK)
1325 delFileName = argv[i];
1326 else
1327 delFileName = path;
1328
1329 if (unlink(delFileName) < 0)
1330 bb_perror_msg("%s", delFileName);
1331 }
1332
1333 free(path);
1334 }
1335 }
1336
1337 return (exit_code);
1338}
1339
1340/* trees.c -- output deflated data using Huffman coding
1341 * Copyright (C) 1992-1993 Jean-loup Gailly
1342 * This is free software; you can redistribute it and/or modify it under the
1343 * terms of the GNU General Public License, see the file COPYING.
1344 */
1345
1346/*
1347 * PURPOSE
1348 *
1349 * Encode various sets of source values using variable-length
1350 * binary code trees.
1351 *
1352 * DISCUSSION
1353 *
1354 * The PKZIP "deflation" process uses several Huffman trees. The more
1355 * common source values are represented by shorter bit sequences.
1356 *
1357 * Each code tree is stored in the ZIP file in a compressed form
1358 * which is itself a Huffman encoding of the lengths of
1359 * all the code strings (in ascending order by source values).
1360 * The actual code strings are reconstructed from the lengths in
1361 * the UNZIP process, as described in the "application note"
1362 * (APPNOTE.TXT) distributed as part of PKWARE's PKZIP program.
1363 *
1364 * REFERENCES
1365 *
1366 * Lynch, Thomas J.
1367 * Data Compression: Techniques and Applications, pp. 53-55.
1368 * Lifetime Learning Publications, 1985. ISBN 0-534-03418-7.
1369 *
1370 * Storer, James A.
1371 * Data Compression: Methods and Theory, pp. 49-50.
1372 * Computer Science Press, 1988. ISBN 0-7167-8156-5.
1373 *
1374 * Sedgewick, R.
1375 * Algorithms, p290.
1376 * Addison-Wesley, 1983. ISBN 0-201-06672-6.
1377 *
1378 * INTERFACE
1379 *
1380 * void ct_init (ush *attr, int *methodp)
1381 * Allocate the match buffer, initialize the various tables and save
1382 * the location of the internal file attribute (ascii/binary) and
1383 * method (DEFLATE/STORE)
1384 *
1385 * void ct_tally (int dist, int lc);
1386 * Save the match info and tally the frequency counts.
1387 *
1388 * long flush_block (char *buf, ulg stored_len, int eof)
1389 * Determine the best encoding for the current block: dynamic trees,
1390 * static trees or store, and output the encoded block to the zip
1391 * file. Returns the total compressed length for the file so far.
1392 *
1393 */
1394
1395/* ===========================================================================
1396 * Constants
1397 */
1398
1399#define MAX_BITS 15
1400/* All codes must not exceed MAX_BITS bits */
1401
1402#define MAX_BL_BITS 7
1403/* Bit length codes must not exceed MAX_BL_BITS bits */
1404
1405#define LENGTH_CODES 29
1406/* number of length codes, not counting the special END_BLOCK code */
1407
1408#define LITERALS 256
1409/* number of literal bytes 0..255 */
1410
1411#define END_BLOCK 256
1412/* end of block literal code */
1413
1414#define L_CODES (LITERALS+1+LENGTH_CODES)
1415/* number of Literal or Length codes, including the END_BLOCK code */
1416
1417#define D_CODES 30
1418/* number of distance codes */
1419
1420#define BL_CODES 19
1421/* number of codes used to transfer the bit lengths */
1422
1423typedef uch extra_bits_t;
1424
1425/* extra bits for each length code */
1426static const extra_bits_t extra_lbits[LENGTH_CODES]
1427 = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4,
1428 4, 4, 5, 5, 5, 5, 0
1429};
1430
1431/* extra bits for each distance code */
1432static const extra_bits_t extra_dbits[D_CODES]
1433 = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,
1434 10, 10, 11, 11, 12, 12, 13, 13
1435};
1436
1437/* extra bits for each bit length code */
1438static const extra_bits_t extra_blbits[BL_CODES]
1439= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7 };
1440
1441#define STORED_BLOCK 0
1442#define STATIC_TREES 1
1443#define DYN_TREES 2
1444/* The three kinds of block type */
1445
1446#ifndef LIT_BUFSIZE
1447# ifdef SMALL_MEM
1448# define LIT_BUFSIZE 0x2000
1449# else
1450# ifdef MEDIUM_MEM
1451# define LIT_BUFSIZE 0x4000
1452# else
1453# define LIT_BUFSIZE 0x8000
1454# endif
1455# endif
1456#endif
1457#ifndef DIST_BUFSIZE
1458# define DIST_BUFSIZE LIT_BUFSIZE
1459#endif
1460/* Sizes of match buffers for literals/lengths and distances. There are
1461 * 4 reasons for limiting LIT_BUFSIZE to 64K:
1462 * - frequencies can be kept in 16 bit counters
1463 * - if compression is not successful for the first block, all input data is
1464 * still in the window so we can still emit a stored block even when input
1465 * comes from standard input. (This can also be done for all blocks if
1466 * LIT_BUFSIZE is not greater than 32K.)
1467 * - if compression is not successful for a file smaller than 64K, we can
1468 * even emit a stored file instead of a stored block (saving 5 bytes).
1469 * - creating new Huffman trees less frequently may not provide fast
1470 * adaptation to changes in the input data statistics. (Take for
1471 * example a binary file with poorly compressible code followed by
1472 * a highly compressible string table.) Smaller buffer sizes give
1473 * fast adaptation but have of course the overhead of transmitting trees
1474 * more frequently.
1475 * - I can't count above 4
1476 * The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save
1477 * memory at the expense of compression). Some optimizations would be possible
1478 * if we rely on DIST_BUFSIZE == LIT_BUFSIZE.
1479 */
1480#if LIT_BUFSIZE > INBUFSIZ
1481#error cannot overlay l_buf and inbuf
1482#endif
1483#define REP_3_6 16
1484/* repeat previous bit length 3-6 times (2 bits of repeat count) */
1485#define REPZ_3_10 17
1486/* repeat a zero length 3-10 times (3 bits of repeat count) */
1487#define REPZ_11_138 18
1488/* repeat a zero length 11-138 times (7 bits of repeat count) */
1489
1490/* ===========================================================================
1491 * Local data
1492 */
1493
1494/* Data structure describing a single value and its code string. */
1495typedef struct ct_data {
1496 union {
1497 ush freq; /* frequency count */
1498 ush code; /* bit string */
1499 } fc;
1500 union {
1501 ush dad; /* father node in Huffman tree */
1502 ush len; /* length of bit string */
1503 } dl;
1504} ct_data;
1505
1506#define Freq fc.freq
1507#define Code fc.code
1508#define Dad dl.dad
1509#define Len dl.len
1510
1511#define HEAP_SIZE (2*L_CODES+1)
1512/* maximum heap size */
1513
1514static ct_data dyn_ltree[HEAP_SIZE]; /* literal and length tree */
1515static ct_data dyn_dtree[2 * D_CODES + 1]; /* distance tree */
1516
1517static ct_data static_ltree[L_CODES + 2];
1518
1519/* The static literal tree. Since the bit lengths are imposed, there is no
1520 * need for the L_CODES extra codes used during heap construction. However
1521 * The codes 286 and 287 are needed to build a canonical tree (see ct_init
1522 * below).
1523 */
1524
1525static ct_data static_dtree[D_CODES];
1526
1527/* The static distance tree. (Actually a trivial tree since all codes use
1528 * 5 bits.)
1529 */
1530
1531static ct_data bl_tree[2 * BL_CODES + 1];
1532
1533/* Huffman tree for the bit lengths */
1534
1535typedef struct tree_desc {
1536 ct_data *dyn_tree; /* the dynamic tree */
1537 ct_data *static_tree; /* corresponding static tree or NULL */
1538 const extra_bits_t *extra_bits; /* extra bits for each code or NULL */
1539 int extra_base; /* base index for extra_bits */
1540 int elems; /* max number of elements in the tree */
1541 int max_length; /* max bit length for the codes */
1542 int max_code; /* largest code with non zero frequency */
1543} tree_desc;
1544
1545static tree_desc l_desc =
1546 { dyn_ltree, static_ltree, extra_lbits, LITERALS + 1, L_CODES,
1547 MAX_BITS, 0
1548};
1549
1550static tree_desc d_desc =
1551 { dyn_dtree, static_dtree, extra_dbits, 0, D_CODES, MAX_BITS, 0 };
1552
1553static tree_desc bl_desc =
1554 { bl_tree, (ct_data *) 0, extra_blbits, 0, BL_CODES, MAX_BL_BITS,
1555 0
1556};
1557
1558
1559static ush bl_count[MAX_BITS + 1];
1560
1561/* number of codes at each bit length for an optimal tree */
1562
1563static const uch bl_order[BL_CODES]
1564= { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
1565
1566/* The lengths of the bit length codes are sent in order of decreasing
1567 * probability, to avoid transmitting the lengths for unused bit length codes.
1568 */
1569
1570static int heap[2 * L_CODES + 1]; /* heap used to build the Huffman trees */
1571static int heap_len; /* number of elements in the heap */
1572static int heap_max; /* element of largest frequency */
1573
1574/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
1575 * The same heap array is used to build all trees.
1576 */
1577
1578static uch depth[2 * L_CODES + 1];
1579
1580/* Depth of each subtree used as tie breaker for trees of equal frequency */
1581
1582static uch length_code[MAX_MATCH - MIN_MATCH + 1];
1583
1584/* length code for each normalized match length (0 == MIN_MATCH) */
1585
1586static uch dist_code[512];
1587
1588/* distance codes. The first 256 values correspond to the distances
1589 * 3 .. 258, the last 256 values correspond to the top 8 bits of
1590 * the 15 bit distances.
1591 */
1592
1593static int base_length[LENGTH_CODES];
1594
1595/* First normalized length for each code (0 = MIN_MATCH) */
1596
1597static int base_dist[D_CODES];
1598
1599/* First normalized distance for each code (0 = distance of 1) */
1600
1601#define l_buf inbuf
1602/* DECLARE(uch, l_buf, LIT_BUFSIZE); buffer for literals or lengths */
1603
1604/* DECLARE(ush, d_buf, DIST_BUFSIZE); buffer for distances */
1605
1606static uch flag_buf[(LIT_BUFSIZE / 8)];
1607
1608/* flag_buf is a bit array distinguishing literals from lengths in
1609 * l_buf, thus indicating the presence or absence of a distance.
1610 */
1611
1612static unsigned last_lit; /* running index in l_buf */
1613static unsigned last_dist; /* running index in d_buf */
1614static unsigned last_flags; /* running index in flag_buf */
1615static uch flags; /* current flags not yet saved in flag_buf */
1616static uch flag_bit; /* current bit used in flags */
1617
1618/* bits are filled in flags starting at bit 0 (least significant).
1619 * Note: these flags are overkill in the current code since we don't
1620 * take advantage of DIST_BUFSIZE == LIT_BUFSIZE.
1621 */
1622
1623static ulg opt_len; /* bit length of current block with optimal trees */
1624static ulg static_len; /* bit length of current block with static trees */
1625
1626static ulg compressed_len; /* total bit length of compressed file */
1627
1628
1629static ush *file_type; /* pointer to UNKNOWN, BINARY or ASCII */
1630static int *file_method; /* pointer to DEFLATE or STORE */
1631
1632/* ===========================================================================
1633 * Local (static) routines in this file.
1634 */
1635
1636static void init_block(void);
1637static void pqdownheap(ct_data * tree, int k);
1638static void gen_bitlen(tree_desc * desc);
1639static void gen_codes(ct_data * tree, int max_code);
1640static void build_tree(tree_desc * desc);
1641static void scan_tree(ct_data * tree, int max_code);
1642static void send_tree(ct_data * tree, int max_code);
1643static int build_bl_tree(void);
1644static void send_all_trees(int lcodes, int dcodes, int blcodes);
1645static void compress_block(ct_data * ltree, ct_data * dtree);
1646static void set_file_type(void);
1647
1648
1649#ifndef DEBUG
1650# define send_code(c, tree) send_bits(tree[c].Code, tree[c].Len)
1651 /* Send a code of the given tree. c and tree must not have side effects */
1652
1653#else /* DEBUG */
1654# define send_code(c, tree) \
1655 { if (verbose>1) bb_error_msg("\ncd %3d ",(c)); \
1656 send_bits(tree[c].Code, tree[c].Len); }
1657#endif
1658
1659#define d_code(dist) \
1660 ((dist) < 256 ? dist_code[dist] : dist_code[256+((dist)>>7)])
1661/* Mapping from a distance to a distance code. dist is the distance - 1 and
1662 * must not have side effects. dist_code[256] and dist_code[257] are never
1663 * used.
1664 */
1665
1666/* the arguments must not have side effects */
1667
1668/* ===========================================================================
1669 * Allocate the match buffer, initialize the various tables and save the
1670 * location of the internal file attribute (ascii/binary) and method
1671 * (DEFLATE/STORE).
1672 */
1673static void ct_init(ush * attr, int *methodp)
1674{
1675 int n; /* iterates over tree elements */
1676 int bits; /* bit counter */
1677 int length; /* length value */
1678 int code; /* code value */
1679 int dist; /* distance index */
1680
1681 file_type = attr;
1682 file_method = methodp;
1683 compressed_len = 0L;
1684
1685 if (static_dtree[0].Len != 0)
1686 return; /* ct_init already called */
1687
1688 /* Initialize the mapping length (0..255) -> length code (0..28) */
1689 length = 0;
1690 for (code = 0; code < LENGTH_CODES - 1; code++) {
1691 base_length[code] = length;
1692 for (n = 0; n < (1 << extra_lbits[code]); n++) {
1693 length_code[length++] = (uch) code;
1694 }
1695 }
1696 Assert(length == 256, "ct_init: length != 256");
1697 /* Note that the length 255 (match length 258) can be represented
1698 * in two different ways: code 284 + 5 bits or code 285, so we
1699 * overwrite length_code[255] to use the best encoding:
1700 */
1701 length_code[length - 1] = (uch) code;
1702
1703 /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
1704 dist = 0;
1705 for (code = 0; code < 16; code++) {
1706 base_dist[code] = dist;
1707 for (n = 0; n < (1 << extra_dbits[code]); n++) {
1708 dist_code[dist++] = (uch) code;
1709 }
1710 }
1711 Assert(dist == 256, "ct_init: dist != 256");
1712 dist >>= 7; /* from now on, all distances are divided by 128 */
1713 for (; code < D_CODES; code++) {
1714 base_dist[code] = dist << 7;
1715 for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) {
1716 dist_code[256 + dist++] = (uch) code;
1717 }
1718 }
1719 Assert(dist == 256, "ct_init: 256+dist != 512");
1720
1721 /* Construct the codes of the static literal tree */
1722 for (bits = 0; bits <= MAX_BITS; bits++)
1723 bl_count[bits] = 0;
1724 n = 0;
1725 while (n <= 143)
1726 static_ltree[n++].Len = 8, bl_count[8]++;
1727 while (n <= 255)
1728 static_ltree[n++].Len = 9, bl_count[9]++;
1729 while (n <= 279)
1730 static_ltree[n++].Len = 7, bl_count[7]++;
1731 while (n <= 287)
1732 static_ltree[n++].Len = 8, bl_count[8]++;
1733 /* Codes 286 and 287 do not exist, but we must include them in the
1734 * tree construction to get a canonical Huffman tree (longest code
1735 * all ones)
1736 */
1737 gen_codes((ct_data *) static_ltree, L_CODES + 1);
1738
1739 /* The static distance tree is trivial: */
1740 for (n = 0; n < D_CODES; n++) {
1741 static_dtree[n].Len = 5;
1742 static_dtree[n].Code = bi_reverse(n, 5);
1743 }
1744
1745 /* Initialize the first block of the first file: */
1746 init_block();
1747}
1748
1749/* ===========================================================================
1750 * Initialize a new block.
1751 */
1752static void init_block()
1753{
1754 int n; /* iterates over tree elements */
1755
1756 /* Initialize the trees. */
1757 for (n = 0; n < L_CODES; n++)
1758 dyn_ltree[n].Freq = 0;
1759 for (n = 0; n < D_CODES; n++)
1760 dyn_dtree[n].Freq = 0;
1761 for (n = 0; n < BL_CODES; n++)
1762 bl_tree[n].Freq = 0;
1763
1764 dyn_ltree[END_BLOCK].Freq = 1;
1765 opt_len = static_len = 0L;
1766 last_lit = last_dist = last_flags = 0;
1767 flags = 0;
1768 flag_bit = 1;
1769}
1770
1771#define SMALLEST 1
1772/* Index within the heap array of least frequent node in the Huffman tree */
1773
1774
1775/* ===========================================================================
1776 * Remove the smallest element from the heap and recreate the heap with
1777 * one less element. Updates heap and heap_len.
1778 */
1779#define pqremove(tree, top) \
1780{\
1781 top = heap[SMALLEST]; \
1782 heap[SMALLEST] = heap[heap_len--]; \
1783 pqdownheap(tree, SMALLEST); \
1784}
1785
1786/* ===========================================================================
1787 * Compares to subtrees, using the tree depth as tie breaker when
1788 * the subtrees have equal frequency. This minimizes the worst case length.
1789 */
1790#define smaller(tree, n, m) \
1791 (tree[n].Freq < tree[m].Freq || \
1792 (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
1793
1794/* ===========================================================================
1795 * Restore the heap property by moving down the tree starting at node k,
1796 * exchanging a node with the smallest of its two sons if necessary, stopping
1797 * when the heap property is re-established (each father smaller than its
1798 * two sons).
1799 */
1800static void pqdownheap(ct_data * tree, int k)
1801{
1802 int v = heap[k];
1803 int j = k << 1; /* left son of k */
1804
1805 while (j <= heap_len) {
1806 /* Set j to the smallest of the two sons: */
1807 if (j < heap_len && smaller(tree, heap[j + 1], heap[j]))
1808 j++;
1809
1810 /* Exit if v is smaller than both sons */
1811 if (smaller(tree, v, heap[j]))
1812 break;
1813
1814 /* Exchange v with the smallest son */
1815 heap[k] = heap[j];
1816 k = j;
1817
1818 /* And continue down the tree, setting j to the left son of k */
1819 j <<= 1;
1820 }
1821 heap[k] = v;
1822}
1823
1824/* ===========================================================================
1825 * Compute the optimal bit lengths for a tree and update the total bit length
1826 * for the current block.
1827 * IN assertion: the fields freq and dad are set, heap[heap_max] and
1828 * above are the tree nodes sorted by increasing frequency.
1829 * OUT assertions: the field len is set to the optimal bit length, the
1830 * array bl_count contains the frequencies for each bit length.
1831 * The length opt_len is updated; static_len is also updated if stree is
1832 * not null.
1833 */
1834static void gen_bitlen(tree_desc * desc)
1835{
1836 ct_data *tree = desc->dyn_tree;
1837 const extra_bits_t *extra = desc->extra_bits;
1838 int base = desc->extra_base;
1839 int max_code = desc->max_code;
1840 int max_length = desc->max_length;
1841 ct_data *stree = desc->static_tree;
1842 int h; /* heap index */
1843 int n, m; /* iterate over the tree elements */
1844 int bits; /* bit length */
1845 int xbits; /* extra bits */
1846 ush f; /* frequency */
1847 int overflow = 0; /* number of elements with bit length too large */
1848
1849 for (bits = 0; bits <= MAX_BITS; bits++)
1850 bl_count[bits] = 0;
1851
1852 /* In a first pass, compute the optimal bit lengths (which may
1853 * overflow in the case of the bit length tree).
1854 */
1855 tree[heap[heap_max]].Len = 0; /* root of the heap */
1856
1857 for (h = heap_max + 1; h < HEAP_SIZE; h++) {
1858 n = heap[h];
1859 bits = tree[tree[n].Dad].Len + 1;
1860 if (bits > max_length)
1861 bits = max_length, overflow++;
1862 tree[n].Len = (ush) bits;
1863 /* We overwrite tree[n].Dad which is no longer needed */
1864
1865 if (n > max_code)
1866 continue; /* not a leaf node */
1867
1868 bl_count[bits]++;
1869 xbits = 0;
1870 if (n >= base)
1871 xbits = extra[n - base];
1872 f = tree[n].Freq;
1873 opt_len += (ulg) f *(bits + xbits);
1874
1875 if (stree)
1876 static_len += (ulg) f *(stree[n].Len + xbits);
1877 }
1878 if (overflow == 0)
1879 return;
1880
1881 Trace((stderr, "\nbit length overflow\n"));
1882 /* This happens for example on obj2 and pic of the Calgary corpus */
1883
1884 /* Find the first bit length which could increase: */
1885 do {
1886 bits = max_length - 1;
1887 while (bl_count[bits] == 0)
1888 bits--;
1889 bl_count[bits]--; /* move one leaf down the tree */
1890 bl_count[bits + 1] += 2; /* move one overflow item as its brother */
1891 bl_count[max_length]--;
1892 /* The brother of the overflow item also moves one step up,
1893 * but this does not affect bl_count[max_length]
1894 */
1895 overflow -= 2;
1896 } while (overflow > 0);
1897
1898 /* Now recompute all bit lengths, scanning in increasing frequency.
1899 * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
1900 * lengths instead of fixing only the wrong ones. This idea is taken
1901 * from 'ar' written by Haruhiko Okumura.)
1902 */
1903 for (bits = max_length; bits != 0; bits--) {
1904 n = bl_count[bits];
1905 while (n != 0) {
1906 m = heap[--h];
1907 if (m > max_code)
1908 continue;
1909 if (tree[m].Len != (unsigned) bits) {
1910 Trace((stderr, "code %d bits %d->%d\n", m, tree[m].Len,
1911 bits));
1912 opt_len +=
1913 ((long) bits - (long) tree[m].Len) * (long) tree[m].Freq;
1914 tree[m].Len = (ush) bits;
1915 }
1916 n--;
1917 }
1918 }
1919}
1920
1921/* ===========================================================================
1922 * Generate the codes for a given tree and bit counts (which need not be
1923 * optimal).
1924 * IN assertion: the array bl_count contains the bit length statistics for
1925 * the given tree and the field len is set for all tree elements.
1926 * OUT assertion: the field code is set for all tree elements of non
1927 * zero code length.
1928 */
1929static void gen_codes(ct_data * tree, int max_code)
1930{
1931 ush next_code[MAX_BITS + 1]; /* next code value for each bit length */
1932 ush code = 0; /* running code value */
1933 int bits; /* bit index */
1934 int n; /* code index */
1935
1936 /* The distribution counts are first used to generate the code values
1937 * without bit reversal.
1938 */
1939 for (bits = 1; bits <= MAX_BITS; bits++) {
1940 next_code[bits] = code = (code + bl_count[bits - 1]) << 1;
1941 }
1942 /* Check that the bit counts in bl_count are consistent. The last code
1943 * must be all ones.
1944 */
1945 Assert(code + bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1,
1946 "inconsistent bit counts");
1947 Tracev((stderr, "\ngen_codes: max_code %d ", max_code));
1948
1949 for (n = 0; n <= max_code; n++) {
1950 int len = tree[n].Len;
1951
1952 if (len == 0)
1953 continue;
1954 /* Now reverse the bits */
1955 tree[n].Code = bi_reverse(next_code[len]++, len);
1956
1957 Tracec(tree != static_ltree,
1958 (stderr, "\nn %3d %c l %2d c %4x (%x) ", n,
1959 (isgraph(n) ? n : ' '), len, tree[n].Code,
1960 next_code[len] - 1));
1961 }
1962}
1963
1964/* ===========================================================================
1965 * Construct one Huffman tree and assigns the code bit strings and lengths.
1966 * Update the total bit length for the current block.
1967 * IN assertion: the field freq is set for all tree elements.
1968 * OUT assertions: the fields len and code are set to the optimal bit length
1969 * and corresponding code. The length opt_len is updated; static_len is
1970 * also updated if stree is not null. The field max_code is set.
1971 */
1972static void build_tree(tree_desc * desc)
1973{
1974 ct_data *tree = desc->dyn_tree;
1975 ct_data *stree = desc->static_tree;
1976 int elems = desc->elems;
1977 int n, m; /* iterate over heap elements */
1978 int max_code = -1; /* largest code with non zero frequency */
1979 int node = elems; /* next internal node of the tree */
1980
1981 /* Construct the initial heap, with least frequent element in
1982 * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
1983 * heap[0] is not used.
1984 */
1985 heap_len = 0, heap_max = HEAP_SIZE;
1986
1987 for (n = 0; n < elems; n++) {
1988 if (tree[n].Freq != 0) {
1989 heap[++heap_len] = max_code = n;
1990 depth[n] = 0;
1991 } else {
1992 tree[n].Len = 0;
1993 }
1994 }
1995
1996 /* The pkzip format requires that at least one distance code exists,
1997 * and that at least one bit should be sent even if there is only one
1998 * possible code. So to avoid special checks later on we force at least
1999 * two codes of non zero frequency.
2000 */
2001 while (heap_len < 2) {
2002 int new = heap[++heap_len] = (max_code < 2 ? ++max_code : 0);
2003
2004 tree[new].Freq = 1;
2005 depth[new] = 0;
2006 opt_len--;
2007 if (stree)
2008 static_len -= stree[new].Len;
2009 /* new is 0 or 1 so it does not have extra bits */
2010 }
2011 desc->max_code = max_code;
2012
2013 /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
2014 * establish sub-heaps of increasing lengths:
2015 */
2016 for (n = heap_len / 2; n >= 1; n--)
2017 pqdownheap(tree, n);
2018
2019 /* Construct the Huffman tree by repeatedly combining the least two
2020 * frequent nodes.
2021 */
2022 do {
2023 pqremove(tree, n); /* n = node of least frequency */
2024 m = heap[SMALLEST]; /* m = node of next least frequency */
2025
2026 heap[--heap_max] = n; /* keep the nodes sorted by frequency */
2027 heap[--heap_max] = m;
2028
2029 /* Create a new node father of n and m */
2030 tree[node].Freq = tree[n].Freq + tree[m].Freq;
2031 depth[node] = (uch) (MAX(depth[n], depth[m]) + 1);
2032 tree[n].Dad = tree[m].Dad = (ush) node;
2033#ifdef DUMP_BL_TREE
2034 if (tree == bl_tree) {
2035 bb_error_msg("\nnode %d(%d), sons %d(%d) %d(%d)",
2036 node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
2037 }
2038#endif
2039 /* and insert the new node in the heap */
2040 heap[SMALLEST] = node++;
2041 pqdownheap(tree, SMALLEST);
2042
2043 } while (heap_len >= 2);
2044
2045 heap[--heap_max] = heap[SMALLEST];
2046
2047 /* At this point, the fields freq and dad are set. We can now
2048 * generate the bit lengths.
2049 */
2050 gen_bitlen((tree_desc *) desc);
2051
2052 /* The field len is now set, we can generate the bit codes */
2053 gen_codes((ct_data *) tree, max_code);
2054}
2055
2056/* ===========================================================================
2057 * Scan a literal or distance tree to determine the frequencies of the codes
2058 * in the bit length tree. Updates opt_len to take into account the repeat
2059 * counts. (The contribution of the bit length codes will be added later
2060 * during the construction of bl_tree.)
2061 */
2062static void scan_tree(ct_data * tree, int max_code)
2063{
2064 int n; /* iterates over all tree elements */
2065 int prevlen = -1; /* last emitted length */
2066 int curlen; /* length of current code */
2067 int nextlen = tree[0].Len; /* length of next code */
2068 int count = 0; /* repeat count of the current code */
2069 int max_count = 7; /* max repeat count */
2070 int min_count = 4; /* min repeat count */
2071
2072 if (nextlen == 0)
2073 max_count = 138, min_count = 3;
2074 tree[max_code + 1].Len = (ush) 0xffff; /* guard */
2075
2076 for (n = 0; n <= max_code; n++) {
2077 curlen = nextlen;
2078 nextlen = tree[n + 1].Len;
2079 if (++count < max_count && curlen == nextlen) {
2080 continue;
2081 } else if (count < min_count) {
2082 bl_tree[curlen].Freq += count;
2083 } else if (curlen != 0) {
2084 if (curlen != prevlen)
2085 bl_tree[curlen].Freq++;
2086 bl_tree[REP_3_6].Freq++;
2087 } else if (count <= 10) {
2088 bl_tree[REPZ_3_10].Freq++;
2089 } else {
2090 bl_tree[REPZ_11_138].Freq++;
2091 }
2092 count = 0;
2093 prevlen = curlen;
2094 if (nextlen == 0) {
2095 max_count = 138, min_count = 3;
2096 } else if (curlen == nextlen) {
2097 max_count = 6, min_count = 3;
2098 } else {
2099 max_count = 7, min_count = 4;
2100 }
2101 }
2102}
2103
2104/* ===========================================================================
2105 * Send a literal or distance tree in compressed form, using the codes in
2106 * bl_tree.
2107 */
2108static void send_tree(ct_data * tree, int max_code)
2109{
2110 int n; /* iterates over all tree elements */
2111 int prevlen = -1; /* last emitted length */
2112 int curlen; /* length of current code */
2113 int nextlen = tree[0].Len; /* length of next code */
2114 int count = 0; /* repeat count of the current code */
2115 int max_count = 7; /* max repeat count */
2116 int min_count = 4; /* min repeat count */
2117
2118/* tree[max_code+1].Len = -1; *//* guard already set */
2119 if (nextlen == 0)
2120 max_count = 138, min_count = 3;
2121
2122 for (n = 0; n <= max_code; n++) {
2123 curlen = nextlen;
2124 nextlen = tree[n + 1].Len;
2125 if (++count < max_count && curlen == nextlen) {
2126 continue;
2127 } else if (count < min_count) {
2128 do {
2129 send_code(curlen, bl_tree);
2130 } while (--count != 0);
2131
2132 } else if (curlen != 0) {
2133 if (curlen != prevlen) {
2134 send_code(curlen, bl_tree);
2135 count--;
2136 }
2137 Assert(count >= 3 && count <= 6, " 3_6?");
2138 send_code(REP_3_6, bl_tree);
2139 send_bits(count - 3, 2);
2140
2141 } else if (count <= 10) {
2142 send_code(REPZ_3_10, bl_tree);
2143 send_bits(count - 3, 3);
2144
2145 } else {
2146 send_code(REPZ_11_138, bl_tree);
2147 send_bits(count - 11, 7);
2148 }
2149 count = 0;
2150 prevlen = curlen;
2151 if (nextlen == 0) {
2152 max_count = 138, min_count = 3;
2153 } else if (curlen == nextlen) {
2154 max_count = 6, min_count = 3;
2155 } else {
2156 max_count = 7, min_count = 4;
2157 }
2158 }
2159}
2160
2161/* ===========================================================================
2162 * Construct the Huffman tree for the bit lengths and return the index in
2163 * bl_order of the last bit length code to send.
2164 */
2165static const int build_bl_tree()
2166{
2167 int max_blindex; /* index of last bit length code of non zero freq */
2168
2169 /* Determine the bit length frequencies for literal and distance trees */
2170 scan_tree((ct_data *) dyn_ltree, l_desc.max_code);
2171 scan_tree((ct_data *) dyn_dtree, d_desc.max_code);
2172
2173 /* Build the bit length tree: */
2174 build_tree((tree_desc *) (&bl_desc));
2175 /* opt_len now includes the length of the tree representations, except
2176 * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
2177 */
2178
2179 /* Determine the number of bit length codes to send. The pkzip format
2180 * requires that at least 4 bit length codes be sent. (appnote.txt says
2181 * 3 but the actual value used is 4.)
2182 */
2183 for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) {
2184 if (bl_tree[bl_order[max_blindex]].Len != 0)
2185 break;
2186 }
2187 /* Update opt_len to include the bit length tree and counts */
2188 opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;
2189 Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", opt_len, static_len));
2190
2191 return max_blindex;
2192}
2193
2194/* ===========================================================================
2195 * Send the header for a block using dynamic Huffman trees: the counts, the
2196 * lengths of the bit length codes, the literal tree and the distance tree.
2197 * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
2198 */
2199static void send_all_trees(int lcodes, int dcodes, int blcodes)
2200{
2201 int rank; /* index in bl_order */
2202
2203 Assert(lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
2204 Assert(lcodes <= L_CODES && dcodes <= D_CODES
2205 && blcodes <= BL_CODES, "too many codes");
2206 Tracev((stderr, "\nbl counts: "));
2207 send_bits(lcodes - 257, 5); /* not +255 as stated in appnote.txt */
2208 send_bits(dcodes - 1, 5);
2209 send_bits(blcodes - 4, 4); /* not -3 as stated in appnote.txt */
2210 for (rank = 0; rank < blcodes; rank++) {
2211 Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
2212 send_bits(bl_tree[bl_order[rank]].Len, 3);
2213 }
2214 Tracev((stderr, "\nbl tree: sent %ld", bits_sent));
2215
2216 send_tree((ct_data *) dyn_ltree, lcodes - 1); /* send the literal tree */
2217 Tracev((stderr, "\nlit tree: sent %ld", bits_sent));
2218
2219 send_tree((ct_data *) dyn_dtree, dcodes - 1); /* send the distance tree */
2220 Tracev((stderr, "\ndist tree: sent %ld", bits_sent));
2221}
2222
2223/* ===========================================================================
2224 * Determine the best encoding for the current block: dynamic trees, static
2225 * trees or store, and output the encoded block to the zip file. This function
2226 * returns the total compressed length for the file so far.
2227 */
2228static ulg flush_block(char *buf, ulg stored_len, int eof)
2229{
2230 ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
2231 int max_blindex; /* index of last bit length code of non zero freq */
2232
2233 flag_buf[last_flags] = flags; /* Save the flags for the last 8 items */
2234
2235 /* Check if the file is ascii or binary */
2236 if (*file_type == (ush) UNKNOWN)
2237 set_file_type();
2238
2239 /* Construct the literal and distance trees */
2240 build_tree((tree_desc *) (&l_desc));
2241 Tracev((stderr, "\nlit data: dyn %ld, stat %ld", opt_len, static_len));
2242
2243 build_tree((tree_desc *) (&d_desc));
2244 Tracev((stderr, "\ndist data: dyn %ld, stat %ld", opt_len, static_len));
2245 /* At this point, opt_len and static_len are the total bit lengths of
2246 * the compressed block data, excluding the tree representations.
2247 */
2248
2249 /* Build the bit length tree for the above two trees, and get the index
2250 * in bl_order of the last bit length code to send.
2251 */
2252 max_blindex = build_bl_tree();
2253
2254 /* Determine the best encoding. Compute first the block length in bytes */
2255 opt_lenb = (opt_len + 3 + 7) >> 3;
2256 static_lenb = (static_len + 3 + 7) >> 3;
2257
2258 Trace((stderr,
2259 "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ",
2260 opt_lenb, opt_len, static_lenb, static_len, stored_len,
2261 last_lit, last_dist));
2262
2263 if (static_lenb <= opt_lenb)
2264 opt_lenb = static_lenb;
2265
2266 /* If compression failed and this is the first and last block,
2267 * and if the zip file can be seeked (to rewrite the local header),
2268 * the whole file is transformed into a stored file:
2269 */
2270 if (stored_len <= opt_lenb && eof && compressed_len == 0L && seekable()) {
2271 /* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */
2272 if (buf == (char *) 0)
2273 bb_error_msg("block vanished");
2274
2275 copy_block(buf, (unsigned) stored_len, 0); /* without header */
2276 compressed_len = stored_len << 3;
2277 *file_method = STORED;
2278
2279 } else if (stored_len + 4 <= opt_lenb && buf != (char *) 0) {
2280 /* 4: two words for the lengths */
2281 /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
2282 * Otherwise we can't have processed more than WSIZE input bytes since
2283 * the last block flush, because compression would have been
2284 * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
2285 * transform a block into a stored block.
2286 */
2287 send_bits((STORED_BLOCK << 1) + eof, 3); /* send block type */
2288 compressed_len = (compressed_len + 3 + 7) & ~7L;
2289 compressed_len += (stored_len + 4) << 3;
2290
2291 copy_block(buf, (unsigned) stored_len, 1); /* with header */
2292
2293 } else if (static_lenb == opt_lenb) {
2294 send_bits((STATIC_TREES << 1) + eof, 3);
2295 compress_block((ct_data *) static_ltree, (ct_data *) static_dtree);
2296 compressed_len += 3 + static_len;
2297 } else {
2298 send_bits((DYN_TREES << 1) + eof, 3);
2299 send_all_trees(l_desc.max_code + 1, d_desc.max_code + 1,
2300 max_blindex + 1);
2301 compress_block((ct_data *) dyn_ltree, (ct_data *) dyn_dtree);
2302 compressed_len += 3 + opt_len;
2303 }
2304 Assert(compressed_len == bits_sent, "bad compressed size");
2305 init_block();
2306
2307 if (eof) {
2308 bi_windup();
2309 compressed_len += 7; /* align on byte boundary */
2310 }
2311 Tracev((stderr, "\ncomprlen %lu(%lu) ", compressed_len >> 3,
2312 compressed_len - 7 * eof));
2313
2314 return compressed_len >> 3;
2315}
2316
2317/* ===========================================================================
2318 * Save the match info and tally the frequency counts. Return true if
2319 * the current block must be flushed.
2320 */
2321static int ct_tally(int dist, int lc)
2322{
2323 l_buf[last_lit++] = (uch) lc;
2324 if (dist == 0) {
2325 /* lc is the unmatched char */
2326 dyn_ltree[lc].Freq++;
2327 } else {
2328 /* Here, lc is the match length - MIN_MATCH */
2329 dist--; /* dist = match distance - 1 */
2330 Assert((ush) dist < (ush) MAX_DIST &&
2331 (ush) lc <= (ush) (MAX_MATCH - MIN_MATCH) &&
2332 (ush) d_code(dist) < (ush) D_CODES, "ct_tally: bad match");
2333
2334 dyn_ltree[length_code[lc] + LITERALS + 1].Freq++;
2335 dyn_dtree[d_code(dist)].Freq++;
2336
2337 d_buf[last_dist++] = (ush) dist;
2338 flags |= flag_bit;
2339 }
2340 flag_bit <<= 1;
2341
2342 /* Output the flags if they fill a byte: */
2343 if ((last_lit & 7) == 0) {
2344 flag_buf[last_flags++] = flags;
2345 flags = 0, flag_bit = 1;
2346 }
2347 /* Try to guess if it is profitable to stop the current block here */
2348 if ((last_lit & 0xfff) == 0) {
2349 /* Compute an upper bound for the compressed length */
2350 ulg out_length = (ulg) last_lit * 8L;
2351 ulg in_length = (ulg) strstart - block_start;
2352 int dcode;
2353
2354 for (dcode = 0; dcode < D_CODES; dcode++) {
2355 out_length +=
2356 (ulg) dyn_dtree[dcode].Freq * (5L + extra_dbits[dcode]);
2357 }
2358 out_length >>= 3;
2359 Trace((stderr,
2360 "\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ",
2361 last_lit, last_dist, in_length, out_length,
2362 100L - out_length * 100L / in_length));
2363 if (last_dist < last_lit / 2 && out_length < in_length / 2)
2364 return 1;
2365 }
2366 return (last_lit == LIT_BUFSIZE - 1 || last_dist == DIST_BUFSIZE);
2367 /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K
2368 * on 16 bit machines and because stored blocks are restricted to
2369 * 64K-1 bytes.
2370 */
2371}
2372
2373/* ===========================================================================
2374 * Send the block data compressed using the given Huffman trees
2375 */
2376static void compress_block(ct_data * ltree, ct_data * dtree)
2377{
2378 unsigned dist; /* distance of matched string */
2379 int lc; /* match length or unmatched char (if dist == 0) */
2380 unsigned lx = 0; /* running index in l_buf */
2381 unsigned dx = 0; /* running index in d_buf */
2382 unsigned fx = 0; /* running index in flag_buf */
2383 uch flag = 0; /* current flags */
2384 unsigned code; /* the code to send */
2385 int extra; /* number of extra bits to send */
2386
2387 if (last_lit != 0)
2388 do {
2389 if ((lx & 7) == 0)
2390 flag = flag_buf[fx++];
2391 lc = l_buf[lx++];
2392 if ((flag & 1) == 0) {
2393 send_code(lc, ltree); /* send a literal byte */
2394 Tracecv(isgraph(lc), (stderr, " '%c' ", lc));
2395 } else {
2396 /* Here, lc is the match length - MIN_MATCH */
2397 code = length_code[lc];
2398 send_code(code + LITERALS + 1, ltree); /* send the length code */
2399 extra = extra_lbits[code];
2400 if (extra != 0) {
2401 lc -= base_length[code];
2402 send_bits(lc, extra); /* send the extra length bits */
2403 }
2404 dist = d_buf[dx++];
2405 /* Here, dist is the match distance - 1 */
2406 code = d_code(dist);
2407 Assert(code < D_CODES, "bad d_code");
2408
2409 send_code(code, dtree); /* send the distance code */
2410 extra = extra_dbits[code];
2411 if (extra != 0) {
2412 dist -= base_dist[code];
2413 send_bits(dist, extra); /* send the extra distance bits */
2414 }
2415 } /* literal or match pair ? */
2416 flag >>= 1;
2417 } while (lx < last_lit);
2418
2419 send_code(END_BLOCK, ltree);
2420}
2421
2422/* ===========================================================================
2423 * Set the file type to ASCII or BINARY, using a crude approximation:
2424 * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise.
2425 * IN assertion: the fields freq of dyn_ltree are set and the total of all
2426 * frequencies does not exceed 64K (to fit in an int on 16 bit machines).
2427 */
2428static void set_file_type()
2429{
2430 int n = 0;
2431 unsigned ascii_freq = 0;
2432 unsigned bin_freq = 0;
2433
2434 while (n < 7)
2435 bin_freq += dyn_ltree[n++].Freq;
2436 while (n < 128)
2437 ascii_freq += dyn_ltree[n++].Freq;
2438 while (n < LITERALS)
2439 bin_freq += dyn_ltree[n++].Freq;
2440 *file_type = bin_freq > (ascii_freq >> 2) ? BINARY : ASCII;
2441 if (*file_type == BINARY && translate_eol) {
2442 bb_error_msg("-l used on binary file");
2443 }
2444}
2445
2446/* zip.c -- compress files to the gzip or pkzip format
2447 * Copyright (C) 1992-1993 Jean-loup Gailly
2448 * This is free software; you can redistribute it and/or modify it under the
2449 * terms of the GNU General Public License, see the file COPYING.
2450 */
2451
2452
2453static ulg crc; /* crc on uncompressed file data */
2454static long header_bytes; /* number of bytes in gzip header */
2455
2456static void put_long(ulg n)
2457{
2458 put_short((n) & 0xffff);
2459 put_short(((ulg) (n)) >> 16);
2460}
2461
2462/* put_header_byte is used for the compressed output
2463 * - for the initial 4 bytes that can't overflow the buffer.
2464 */
2465#define put_header_byte(c) {outbuf[outcnt++]=(uch)(c);}
2466
2467/* ===========================================================================
2468 * Deflate in to out.
2469 * IN assertions: the input and output buffers are cleared.
2470 * The variables time_stamp and save_orig_name are initialized.
2471 */
2472static int zip(int in, int out)
2473{
2474 uch my_flags = 0; /* general purpose bit flags */
2475 ush attr = 0; /* ascii/binary flag */
2476 ush deflate_flags = 0; /* pkzip -es, -en or -ex equivalent */
2477
2478 ifd = in;
2479 ofd = out;
2480 outcnt = 0;
2481
2482 /* Write the header to the gzip file. See algorithm.doc for the format */
2483
2484
2485 method = DEFLATED;
2486 put_header_byte(GZIP_MAGIC[0]); /* magic header */
2487 put_header_byte(GZIP_MAGIC[1]);
2488 put_header_byte(DEFLATED); /* compression method */
2489
2490 put_header_byte(my_flags); /* general flags */
2491 put_long(time_stamp);
2492
2493 /* Write deflated file to zip file */
2494 crc = updcrc(0, 0);
2495
2496 bi_init(out);
2497 ct_init(&attr, &method);
2498 lm_init(&deflate_flags);
2499
2500 put_byte((uch) deflate_flags); /* extra flags */
2501 put_byte(OS_CODE); /* OS identifier */
2502
2503 header_bytes = (long) outcnt;
2504
2505 (void) deflate();
2506
2507 /* Write the crc and uncompressed size */
2508 put_long(crc);
2509 put_long(isize);
2510 header_bytes += 2 * sizeof(long);
2511
2512 flush_outbuf();
2513 return OK;
2514}
2515
2516
2517/* ===========================================================================
2518 * Read a new buffer from the current input file, perform end-of-line
2519 * translation, and update the crc and input file size.
2520 * IN assertion: size >= 2 (for end-of-line translation)
2521 */
2522static int file_read(char *buf, unsigned size)
2523{
2524 unsigned len;
2525
2526 Assert(insize == 0, "inbuf not empty");
2527
2528 len = read(ifd, buf, size);
2529 if (len == (unsigned) (-1) || len == 0)
2530 return (int) len;
2531
2532 crc = updcrc((uch *) buf, len);
2533 isize += (ulg) len;
2534 return (int) len;
2535}
2536
2537/* ===========================================================================
2538 * Write the output buffer outbuf[0..outcnt-1] and update bytes_out.
2539 * (used for the compressed data only)
2540 */
2541static void flush_outbuf()
2542{
2543 if (outcnt == 0)
2544 return;
2545
2546 write_buf(ofd, (char *) outbuf, outcnt);
2547 outcnt = 0;
2548}
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=../..
21top_builddir=../..
22srcdir=$(top_srcdir)/archival/libunarchive
23LIBUNARCHIVE_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include $(srcdir)/Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20LIBUNARCHIVE_AR:=libunarchive.a
21ifndef $(LIBUNARCHIVE_DIR)
22LIBUNARCHIVE_DIR:=$(top_builddir)/archival/libunarchive/
23endif
24srcdir=$(top_srcdir)/archvial/libunarchive
25
26LIBUNARCHIVE-y:= \
27\
28 data_skip.o \
29 data_extract_all.o \
30 data_extract_to_stdout.o \
31 data_extract_to_buffer.o \
32\
33 filter_accept_all.o \
34 filter_accept_list.o \
35 filter_accept_reject_list.o \
36\
37 header_skip.o \
38 header_list.o \
39 header_verbose_list.o \
40\
41 archive_xread_all.o \
42 archive_xread_all_eof.o \
43\
44 seek_by_char.o \
45 seek_by_jump.o \
46\
47 data_align.o \
48 find_list_entry.o \
49 open_transformer.o \
50 init_handle.o
51
52GUNZIP_FILES:= check_header_gzip.o decompress_unzip.o
53DPKG_FILES:= \
54 get_header_ar.o \
55 unpack_ar_archive.o \
56 get_header_tar.o \
57 filter_accept_list_reassign.o
58
59LIBUNARCHIVE-$(CONFIG_AR) += get_header_ar.o unpack_ar_archive.o
60LIBUNARCHIVE-$(CONFIG_BUNZIP2) += decompress_bunzip2.o
61LIBUNARCHIVE-$(CONFIG_CPIO) += get_header_cpio.o
62LIBUNARCHIVE-$(CONFIG_DPKG) += $(DPKG_FILES)
63LIBUNARCHIVE-$(CONFIG_DPKG_DEB) += $(DPKG_FILES)
64LIBUNARCHIVE-$(CONFIG_FEATURE_DEB_TAR_GZ) += $(GUNZIP_FILES) get_header_tar_gz.o
65LIBUNARCHIVE-$(CONFIG_FEATURE_DEB_TAR_BZ2) += decompress_bunzip2.o get_header_tar_bz2.o
66LIBUNARCHIVE-$(CONFIG_GUNZIP) += $(GUNZIP_FILES)
67LIBUNARCHIVE-$(CONFIG_FEATURE_GUNZIP_UNCOMPRESS) += decompress_uncompress.o
68LIBUNARCHIVE-$(CONFIG_RPM2CPIO) += $(GUNZIP_FILES) get_header_cpio.o
69LIBUNARCHIVE-$(CONFIG_RPM) += $(GUNZIP_FILES) get_header_cpio.o
70LIBUNARCHIVE-$(CONFIG_TAR) += get_header_tar.o
71LIBUNARCHIVE-$(CONFIG_FEATURE_TAR_BZIP2) += decompress_bunzip2.o get_header_tar_bz2.o
72LIBUNARCHIVE-$(CONFIG_FEATURE_TAR_GZIP) += $(GUNZIP_FILES) get_header_tar_gz.o
73LIBUNARCHIVE-$(CONFIG_FEATURE_TAR_COMPRESS) += decompress_uncompress.o
74LIBUNARCHIVE-$(CONFIG_UNCOMPRESS) += decompress_uncompress.o
75LIBUNARCHIVE-$(CONFIG_UNZIP) += $(GUNZIP_FILES)
76
77libraries-y+=$(LIBUNARCHIVE_DIR)$(LIBUNARCHIVE_AR)
78
79$(LIBUNARCHIVE_DIR)$(LIBUNARCHIVE_AR): $(patsubst %,$(LIBUNARCHIVE_DIR)%, $(LIBUNARCHIVE-y))
80 $(AR) -ro $@ $(patsubst %,$(LIBUNARCHIVE_DIR)%, $(LIBUNARCHIVE-y))
81
82$(LIBUNARCHIVA_DIR)%.o: $(srcdir)/%.c
83 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
84
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Library General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include "unarchive.h"
21#include "libbb.h"
22
23extern void archive_xread_all(const archive_handle_t *archive_handle, void *buf, const size_t count)
24{
25 ssize_t size;
26
27 size = bb_full_read(archive_handle->src_fd, buf, count);
28 if (size != count) {
29 bb_error_msg_and_die("Short read");
30 }
31 return;
32}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Library General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include "unarchive.h"
21#include "libbb.h"
22
23extern ssize_t archive_xread_all_eof(archive_handle_t *archive_handle, unsigned char *buf, size_t count)
24{
25 ssize_t size;
26
27 size = bb_full_read(archive_handle->src_fd, buf, count);
28 if ((size != 0) && (size != count)) {
29 bb_perror_msg_and_die("Short read, read %d of %d", size, count);
30 }
31 return(size);
32}
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 @@
1#include <stdlib.h>
2#include <unistd.h>
3#include "libbb.h"
4
5extern void check_header_gzip(int src_fd)
6{
7 union {
8 unsigned char raw[8];
9 struct {
10 unsigned char method;
11 unsigned char flags;
12 unsigned int mtime;
13 unsigned char xtra_flags;
14 unsigned char os_flags;
15 } formated;
16 } header;
17
18 bb_xread_all(src_fd, header.raw, 8);
19
20 /* Check the compression method */
21 if (header.formated.method != 8) {
22 bb_error_msg_and_die("Unknown compression method %d",
23 header.formated.method);
24 }
25
26 if (header.formated.flags & 0x04) {
27 /* bit 2 set: extra field present */
28 unsigned char extra_short;
29
30 extra_short = bb_xread_char(src_fd) + (bb_xread_char(src_fd) << 8);
31 while (extra_short > 0) {
32 /* Ignore extra field */
33 bb_xread_char(src_fd);
34 extra_short--;
35 }
36 }
37
38 /* Discard original name if any */
39 if (header.formated.flags & 0x08) {
40 /* bit 3 set: original file name present */
41 while(bb_xread_char(src_fd) != 0);
42 }
43
44 /* Discard file comment if any */
45 if (header.formated.flags & 0x10) {
46 /* bit 4 set: file comment present */
47 while(bb_xread_char(src_fd) != 0);
48 }
49
50 /* Read the header checksum */
51 if (header.formated.flags & 0x02) {
52 bb_xread_char(src_fd);
53 bb_xread_char(src_fd);
54 }
55
56 return;
57}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <sys/types.h>
18
19#include <errno.h>
20#include <unistd.h>
21
22#include "libbb.h"
23#include "unarchive.h"
24
25extern void data_align(archive_handle_t *archive_handle, const unsigned short boundary)
26{
27 const unsigned short skip_amount = (boundary - (archive_handle->offset % boundary)) % boundary;
28
29 archive_handle->seek(archive_handle, skip_amount);
30 archive_handle->offset += skip_amount;
31
32 return;
33}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <sys/types.h>
18
19#include <errno.h>
20#include <fcntl.h>
21#include <stdlib.h>
22#include <string.h>
23#include <utime.h>
24#include <unistd.h>
25#include <stdlib.h>
26
27#include "libbb.h"
28#include "unarchive.h"
29
30extern void data_extract_all(archive_handle_t *archive_handle)
31{
32 file_header_t *file_header = archive_handle->file_header;
33 int dst_fd;
34 int res;
35
36 if (archive_handle->flags & ARCHIVE_CREATE_LEADING_DIRS) {
37 char *name = bb_xstrdup(file_header->name);
38 bb_make_directory (dirname(name), -1, FILEUTILS_RECUR);
39 free(name);
40 }
41
42 /* Check if the file already exists */
43 if (archive_handle->flags & ARCHIVE_EXTRACT_UNCONDITIONAL) {
44 /* Remove the existing entry if it exists */
45 if (((file_header->mode & S_IFMT) != S_IFDIR) && (unlink(file_header->name) == -1) && (errno != ENOENT)) {
46 bb_perror_msg_and_die("Couldnt remove old file");
47 }
48 }
49 else if (archive_handle->flags & ARCHIVE_EXTRACT_NEWER) {
50 /* Remove the existing entry if its older than the extracted entry */
51 struct stat statbuf;
52 if (lstat(file_header->name, &statbuf) == -1) {
53 if (errno != ENOENT) {
54 bb_perror_msg_and_die("Couldnt stat old file");
55 }
56 }
57 else if (statbuf.st_mtime <= file_header->mtime) {
58 if (!(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
59 bb_error_msg("%s not created: newer or same age file exists", file_header->name);
60 }
61 data_skip(archive_handle);
62 return;
63 }
64 else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) {
65 bb_perror_msg_and_die("Couldnt remove old file %s", file_header->name);
66 }
67 }
68
69 /* Handle hard links separately
70 * We identified hard links as regular files of size 0 with a symlink */
71 if (S_ISREG(file_header->mode) && (file_header->link_name) && (file_header->size == 0)) {
72 /* hard link */
73 res = link(file_header->link_name, file_header->name);
74 if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
75 bb_perror_msg("Couldnt create hard link");
76 }
77 } else {
78 /* Create the filesystem entry */
79 switch(file_header->mode & S_IFMT) {
80 case S_IFREG: {
81 /* Regular file */
82 dst_fd = bb_xopen(file_header->name, O_WRONLY | O_CREAT | O_EXCL);
83 bb_copyfd_size(archive_handle->src_fd, dst_fd, file_header->size);
84 close(dst_fd);
85 break;
86 }
87 case S_IFDIR:
88 res = mkdir(file_header->name, file_header->mode);
89 if ((errno != EISDIR) && (res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
90 bb_perror_msg("extract_archive: %s", file_header->name);
91 }
92 break;
93 case S_IFLNK:
94 /* Symlink */
95 res = symlink(file_header->link_name, file_header->name);
96 if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
97 bb_perror_msg("Cannot create symlink from %s to '%s'", file_header->name, file_header->link_name);
98 }
99 break;
100 case S_IFSOCK:
101 case S_IFBLK:
102 case S_IFCHR:
103 case S_IFIFO:
104 res = mknod(file_header->name, file_header->mode, file_header->device);
105 if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
106 bb_perror_msg("Cannot create node %s", file_header->name);
107 }
108 break;
109 default:
110 bb_error_msg_and_die("Unrecognised file type");
111 }
112 }
113
114 lchown(file_header->name, file_header->uid, file_header->gid);
115 if ((file_header->mode & S_IFMT) != S_IFLNK) {
116 chmod(file_header->name, file_header->mode);
117 }
118
119 if (archive_handle->flags & ARCHIVE_PRESERVE_DATE) {
120 struct utimbuf t;
121 t.actime = t.modtime = file_header->mtime;
122 utime(file_header->name, &t);
123 }
124}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include "libbb.h"
18#include "unarchive.h"
19
20extern void data_extract_to_buffer(archive_handle_t *archive_handle)
21{
22 const unsigned int size = archive_handle->file_header->size;
23
24 archive_handle->buffer = xmalloc(size + 1);
25
26 archive_xread_all(archive_handle, archive_handle->buffer, size);
27 archive_handle->buffer[size] = '\0';
28}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include "unarchive.h"
18#include <unistd.h>
19
20extern void data_extract_to_stdout(archive_handle_t *archive_handle)
21{
22 bb_copyfd_size(archive_handle->src_fd, STDOUT_FILENO, archive_handle->file_header->size);
23}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <sys/types.h>
18#include <errno.h>
19#include <unistd.h>
20#include <stdlib.h>
21#include "unarchive.h"
22#include "libbb.h"
23
24extern void data_skip(archive_handle_t *archive_handle)
25{
26 archive_handle->seek(archive_handle, archive_handle->file_header->size);
27}
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 @@
1/* vi: set sw=4 ts=4: */
2/* Small bzip2 deflate implementation, by Rob Landley (rob@landley.net).
3
4 Based on bzip2 decompression code by Julian R Seward (jseward@acm.org),
5 which also acknowledges contributions by Mike Burrows, David Wheeler,
6 Peter Fenwick, Alistair Moffat, Radford Neal, Ian H. Witten,
7 Robert Sedgewick, and Jon L. Bentley.
8
9 This code is licensed under the LGPLv2:
10 LGPL (http://www.gnu.org/copyleft/lgpl.html
11*/
12
13/*
14 Size and speed optimizations by Manuel Novoa III (mjn3@codepoet.org).
15
16 More efficient reading of Huffman codes, a streamlined read_bunzip()
17 function, and various other tweaks. In (limited) tests, approximately
18 20% faster than bzcat on x86 and about 10% faster on arm.
19
20 Note that about 2/3 of the time is spent in read_unzip() reversing
21 the Burrows-Wheeler transformation. Much of that time is delay
22 resulting from cache misses.
23
24 I would ask that anyone benefiting from this work, especially those
25 using it in commercial products, consider making a donation to my local
26 non-profit hospice organization in the name of the woman I loved, who
27 passed away Feb. 12, 2003.
28
29 In memory of Toni W. Hagan
30
31 Hospice of Acadiana, Inc.
32 2600 Johnston St., Suite 200
33 Lafayette, LA 70503-3240
34
35 Phone (337) 232-1234 or 1-800-738-2226
36 Fax (337) 232-1297
37
38 http://www.hospiceacadiana.com/
39
40 Manuel
41 */
42
43#include <setjmp.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48#include <limits.h>
49
50#include "libbb.h"
51
52/* Constants for Huffman coding */
53#define MAX_GROUPS 6
54#define GROUP_SIZE 50 /* 64 would have been more efficient */
55#define MAX_HUFCODE_BITS 20 /* Longest Huffman code allowed */
56#define MAX_SYMBOLS 258 /* 256 literals + RUNA + RUNB */
57#define SYMBOL_RUNA 0
58#define SYMBOL_RUNB 1
59
60/* Status return values */
61#define RETVAL_OK 0
62#define RETVAL_LAST_BLOCK (-1)
63#define RETVAL_NOT_BZIP_DATA (-2)
64#define RETVAL_UNEXPECTED_INPUT_EOF (-3)
65#define RETVAL_UNEXPECTED_OUTPUT_EOF (-4)
66#define RETVAL_DATA_ERROR (-5)
67#define RETVAL_OUT_OF_MEMORY (-6)
68#define RETVAL_OBSOLETE_INPUT (-7)
69
70/* Other housekeeping constants */
71#define IOBUF_SIZE 4096
72
73/* This is what we know about each Huffman coding group */
74struct group_data {
75 /* We have an extra slot at the end of limit[] for a sentinal value. */
76 int limit[MAX_HUFCODE_BITS+1],base[MAX_HUFCODE_BITS],permute[MAX_SYMBOLS];
77 int minLen, maxLen;
78};
79
80/* Structure holding all the housekeeping data, including IO buffers and
81 memory that persists between calls to bunzip */
82typedef struct {
83 /* State for interrupting output loop */
84 int writeCopies,writePos,writeRunCountdown,writeCount,writeCurrent;
85 /* I/O tracking data (file handles, buffers, positions, etc.) */
86 int in_fd,out_fd,inbufCount,inbufPos /*,outbufPos*/;
87 unsigned char *inbuf /*,*outbuf*/;
88 unsigned int inbufBitCount, inbufBits;
89 /* The CRC values stored in the block header and calculated from the data */
90 unsigned int crc32Table[256],headerCRC, totalCRC, writeCRC;
91 /* Intermediate buffer and its size (in bytes) */
92 unsigned int *dbuf, dbufSize;
93 /* These things are a bit too big to go on the stack */
94 unsigned char selectors[32768]; /* nSelectors=15 bits */
95 struct group_data groups[MAX_GROUPS]; /* Huffman coding tables */
96 /* For I/O error handling */
97 jmp_buf jmpbuf;
98} bunzip_data;
99
100/* Return the next nnn bits of input. All reads from the compressed input
101 are done through this function. All reads are big endian */
102static unsigned int get_bits(bunzip_data *bd, char bits_wanted)
103{
104 unsigned int bits=0;
105
106 /* If we need to get more data from the byte buffer, do so. (Loop getting
107 one byte at a time to enforce endianness and avoid unaligned access.) */
108 while (bd->inbufBitCount<bits_wanted) {
109 /* If we need to read more data from file into byte buffer, do so */
110 if(bd->inbufPos==bd->inbufCount) {
111 if((bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE)) <= 0)
112 longjmp(bd->jmpbuf,RETVAL_UNEXPECTED_INPUT_EOF);
113 bd->inbufPos=0;
114 }
115 /* Avoid 32-bit overflow (dump bit buffer to top of output) */
116 if(bd->inbufBitCount>=24) {
117 bits=bd->inbufBits&((1<<bd->inbufBitCount)-1);
118 bits_wanted-=bd->inbufBitCount;
119 bits<<=bits_wanted;
120 bd->inbufBitCount=0;
121 }
122 /* Grab next 8 bits of input from buffer. */
123 bd->inbufBits=(bd->inbufBits<<8)|bd->inbuf[bd->inbufPos++];
124 bd->inbufBitCount+=8;
125 }
126 /* Calculate result */
127 bd->inbufBitCount-=bits_wanted;
128 bits|=(bd->inbufBits>>bd->inbufBitCount)&((1<<bits_wanted)-1);
129
130 return bits;
131}
132
133/* Unpacks the next block and sets up for the inverse burrows-wheeler step. */
134
135static int get_next_block(bunzip_data *bd)
136{
137 /* Note: Ignore the warning about hufGroup, base and limit being used uninitialized.
138 * They will be initialized on the fist pass of the loop. */
139 struct group_data *hufGroup;
140 int dbufCount,nextSym,dbufSize,groupCount,*base,*limit,selector,
141 i,j,k,t,runPos,symCount,symTotal,nSelectors,byteCount[256];
142 unsigned char uc, symToByte[256], mtfSymbol[256], *selectors;
143 unsigned int *dbuf,origPtr;
144
145 dbuf=bd->dbuf;
146 dbufSize=bd->dbufSize;
147 selectors=bd->selectors;
148 /* Reset longjmp I/O error handling */
149 i=setjmp(bd->jmpbuf);
150 if(i) return i;
151 /* Read in header signature and CRC, then validate signature.
152 (last block signature means CRC is for whole file, return now) */
153 i = get_bits(bd,24);
154 j = get_bits(bd,24);
155 bd->headerCRC=get_bits(bd,32);
156 if ((i == 0x177245) && (j == 0x385090)) return RETVAL_LAST_BLOCK;
157 if ((i != 0x314159) || (j != 0x265359)) return RETVAL_NOT_BZIP_DATA;
158 /* We can add support for blockRandomised if anybody complains. There was
159 some code for this in busybox 1.0.0-pre3, but nobody ever noticed that
160 it didn't actually work. */
161 if(get_bits(bd,1)) return RETVAL_OBSOLETE_INPUT;
162 if((origPtr=get_bits(bd,24)) > dbufSize) return RETVAL_DATA_ERROR;
163 /* mapping table: if some byte values are never used (encoding things
164 like ascii text), the compression code removes the gaps to have fewer
165 symbols to deal with, and writes a sparse bitfield indicating which
166 values were present. We make a translation table to convert the symbols
167 back to the corresponding bytes. */
168 t=get_bits(bd, 16);
169 symTotal=0;
170 for (i=0;i<16;i++) {
171 if(t&(1<<(15-i))) {
172 k=get_bits(bd,16);
173 for(j=0;j<16;j++)
174 if(k&(1<<(15-j))) symToByte[symTotal++]=(16*i)+j;
175 }
176 }
177 /* How many different Huffman coding groups does this block use? */
178 groupCount=get_bits(bd,3);
179 if (groupCount<2 || groupCount>MAX_GROUPS) return RETVAL_DATA_ERROR;
180 /* nSelectors: Every GROUP_SIZE many symbols we select a new Huffman coding
181 group. Read in the group selector list, which is stored as MTF encoded
182 bit runs. (MTF=Move To Front, as each value is used it's moved to the
183 start of the list.) */
184 if(!(nSelectors=get_bits(bd, 15))) return RETVAL_DATA_ERROR;
185 for(i=0; i<groupCount; i++) mtfSymbol[i] = i;
186 for(i=0; i<nSelectors; i++) {
187 /* Get next value */
188 for(j=0;get_bits(bd,1);j++) if (j>=groupCount) return RETVAL_DATA_ERROR;
189 /* Decode MTF to get the next selector */
190 uc = mtfSymbol[j];
191 for(;j;j--) mtfSymbol[j] = mtfSymbol[j-1];
192 mtfSymbol[0]=selectors[i]=uc;
193 }
194 /* Read the Huffman coding tables for each group, which code for symTotal
195 literal symbols, plus two run symbols (RUNA, RUNB) */
196 symCount=symTotal+2;
197 for (j=0; j<groupCount; j++) {
198 unsigned char length[MAX_SYMBOLS],temp[MAX_HUFCODE_BITS+1];
199 int minLen, maxLen, pp;
200 /* Read Huffman code lengths for each symbol. They're stored in
201 a way similar to mtf; record a starting value for the first symbol,
202 and an offset from the previous value for everys symbol after that.
203 (Subtracting 1 before the loop and then adding it back at the end is
204 an optimization that makes the test inside the loop simpler: symbol
205 length 0 becomes negative, so an unsigned inequality catches it.) */
206 t=get_bits(bd, 5)-1;
207 for (i = 0; i < symCount; i++) {
208 for(;;) {
209 if (((unsigned)t) > (MAX_HUFCODE_BITS-1))
210 return RETVAL_DATA_ERROR;
211 /* If first bit is 0, stop. Else second bit indicates whether
212 to increment or decrement the value. Optimization: grab 2
213 bits and unget the second if the first was 0. */
214 k = get_bits(bd,2);
215 if (k < 2) {
216 bd->inbufBitCount++;
217 break;
218 }
219 /* Add one if second bit 1, else subtract 1. Avoids if/else */
220 t+=(((k+1)&2)-1);
221 }
222 /* Correct for the initial -1, to get the final symbol length */
223 length[i]=t+1;
224 }
225 /* Find largest and smallest lengths in this group */
226 minLen=maxLen=length[0];
227 for(i = 1; i < symCount; i++) {
228 if(length[i] > maxLen) maxLen = length[i];
229 else if(length[i] < minLen) minLen = length[i];
230 }
231 /* Calculate permute[], base[], and limit[] tables from length[].
232 *
233 * permute[] is the lookup table for converting Huffman coded symbols
234 * into decoded symbols. base[] is the amount to subtract from the
235 * value of a Huffman symbol of a given length when using permute[].
236 *
237 * limit[] indicates the largest numerical value a symbol with a given
238 * number of bits can have. This is how the Huffman codes can vary in
239 * length: each code with a value>limit[length] needs another bit.
240 */
241 hufGroup=bd->groups+j;
242 hufGroup->minLen = minLen;
243 hufGroup->maxLen = maxLen;
244 /* Note that minLen can't be smaller than 1, so we adjust the base
245 and limit array pointers so we're not always wasting the first
246 entry. We do this again when using them (during symbol decoding).*/
247 base=hufGroup->base-1;
248 limit=hufGroup->limit-1;
249 /* Calculate permute[]. Concurently, initialize temp[] and limit[]. */
250 pp=0;
251 for(i=minLen;i<=maxLen;i++) {
252 temp[i]=limit[i]=0;
253 for(t=0;t<symCount;t++)
254 if(length[t]==i) hufGroup->permute[pp++] = t;
255 }
256 /* Count symbols coded for at each bit length */
257 for (i=0;i<symCount;i++) temp[length[i]]++;
258 /* Calculate limit[] (the largest symbol-coding value at each bit
259 * length, which is (previous limit<<1)+symbols at this level), and
260 * base[] (number of symbols to ignore at each bit length, which is
261 * limit minus the cumulative count of symbols coded for already). */
262 pp=t=0;
263 for (i=minLen; i<maxLen; i++) {
264 pp+=temp[i];
265 /* We read the largest possible symbol size and then unget bits
266 after determining how many we need, and those extra bits could
267 be set to anything. (They're noise from future symbols.) At
268 each level we're really only interested in the first few bits,
269 so here we set all the trailing to-be-ignored bits to 1 so they
270 don't affect the value>limit[length] comparison. */
271 limit[i]= (pp << (maxLen - i)) - 1;
272 pp<<=1;
273 base[i+1]=pp-(t+=temp[i]);
274 }
275 limit[maxLen+1] = INT_MAX; /* Sentinal value for reading next sym. */
276 limit[maxLen]=pp+temp[maxLen]-1;
277 base[minLen]=0;
278 }
279 /* We've finished reading and digesting the block header. Now read this
280 block's Huffman coded symbols from the file and undo the Huffman coding
281 and run length encoding, saving the result into dbuf[dbufCount++]=uc */
282
283 /* Initialize symbol occurrence counters and symbol Move To Front table */
284 for(i=0;i<256;i++) {
285 byteCount[i] = 0;
286 mtfSymbol[i]=(unsigned char)i;
287 }
288 /* Loop through compressed symbols. */
289 runPos=dbufCount=symCount=selector=0;
290 for(;;) {
291 /* Determine which Huffman coding group to use. */
292 if(!(symCount--)) {
293 symCount=GROUP_SIZE-1;
294 if(selector>=nSelectors) return RETVAL_DATA_ERROR;
295 hufGroup=bd->groups+selectors[selector++];
296 base=hufGroup->base-1;
297 limit=hufGroup->limit-1;
298 }
299 /* Read next Huffman-coded symbol. */
300 /* Note: It is far cheaper to read maxLen bits and back up than it is
301 to read minLen bits and then an additional bit at a time, testing
302 as we go. Because there is a trailing last block (with file CRC),
303 there is no danger of the overread causing an unexpected EOF for a
304 valid compressed file. As a further optimization, we do the read
305 inline (falling back to a call to get_bits if the buffer runs
306 dry). The following (up to got_huff_bits:) is equivalent to
307 j=get_bits(bd,hufGroup->maxLen);
308 */
309 while (bd->inbufBitCount<hufGroup->maxLen) {
310 if(bd->inbufPos==bd->inbufCount) {
311 j = get_bits(bd,hufGroup->maxLen);
312 goto got_huff_bits;
313 }
314 bd->inbufBits=(bd->inbufBits<<8)|bd->inbuf[bd->inbufPos++];
315 bd->inbufBitCount+=8;
316 };
317 bd->inbufBitCount-=hufGroup->maxLen;
318 j = (bd->inbufBits>>bd->inbufBitCount)&((1<<hufGroup->maxLen)-1);
319got_huff_bits:
320 /* Figure how how many bits are in next symbol and unget extras */
321 i=hufGroup->minLen;
322 while(j>limit[i]) ++i;
323 bd->inbufBitCount += (hufGroup->maxLen - i);
324 /* Huffman decode value to get nextSym (with bounds checking) */
325 if ((i > hufGroup->maxLen)
326 || (((unsigned)(j=(j>>(hufGroup->maxLen-i))-base[i]))
327 >= MAX_SYMBOLS))
328 return RETVAL_DATA_ERROR;
329 nextSym = hufGroup->permute[j];
330 /* We have now decoded the symbol, which indicates either a new literal
331 byte, or a repeated run of the most recent literal byte. First,
332 check if nextSym indicates a repeated run, and if so loop collecting
333 how many times to repeat the last literal. */
334 if (((unsigned)nextSym) <= SYMBOL_RUNB) { /* RUNA or RUNB */
335 /* If this is the start of a new run, zero out counter */
336 if(!runPos) {
337 runPos = 1;
338 t = 0;
339 }
340 /* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at
341 each bit position, add 1 or 2 instead. For example,
342 1011 is 1<<0 + 1<<1 + 2<<2. 1010 is 2<<0 + 2<<1 + 1<<2.
343 You can make any bit pattern that way using 1 less symbol than
344 the basic or 0/1 method (except all bits 0, which would use no
345 symbols, but a run of length 0 doesn't mean anything in this
346 context). Thus space is saved. */
347 t += (runPos << nextSym); /* +runPos if RUNA; +2*runPos if RUNB */
348 runPos <<= 1;
349 continue;
350 }
351 /* When we hit the first non-run symbol after a run, we now know
352 how many times to repeat the last literal, so append that many
353 copies to our buffer of decoded symbols (dbuf) now. (The last
354 literal used is the one at the head of the mtfSymbol array.) */
355 if(runPos) {
356 runPos=0;
357 if(dbufCount+t>=dbufSize) return RETVAL_DATA_ERROR;
358
359 uc = symToByte[mtfSymbol[0]];
360 byteCount[uc] += t;
361 while(t--) dbuf[dbufCount++]=uc;
362 }
363 /* Is this the terminating symbol? */
364 if(nextSym>symTotal) break;
365 /* At this point, nextSym indicates a new literal character. Subtract
366 one to get the position in the MTF array at which this literal is
367 currently to be found. (Note that the result can't be -1 or 0,
368 because 0 and 1 are RUNA and RUNB. But another instance of the
369 first symbol in the mtf array, position 0, would have been handled
370 as part of a run above. Therefore 1 unused mtf position minus
371 2 non-literal nextSym values equals -1.) */
372 if(dbufCount>=dbufSize) return RETVAL_DATA_ERROR;
373 i = nextSym - 1;
374 uc = mtfSymbol[i];
375 /* Adjust the MTF array. Since we typically expect to move only a
376 * small number of symbols, and are bound by 256 in any case, using
377 * memmove here would typically be bigger and slower due to function
378 * call overhead and other assorted setup costs. */
379 do {
380 mtfSymbol[i] = mtfSymbol[i-1];
381 } while (--i);
382 mtfSymbol[0] = uc;
383 uc=symToByte[uc];
384 /* We have our literal byte. Save it into dbuf. */
385 byteCount[uc]++;
386 dbuf[dbufCount++] = (unsigned int)uc;
387 }
388 /* At this point, we've read all the Huffman-coded symbols (and repeated
389 runs) for this block from the input stream, and decoded them into the
390 intermediate buffer. There are dbufCount many decoded bytes in dbuf[].
391 Now undo the Burrows-Wheeler transform on dbuf.
392 See http://dogma.net/markn/articles/bwt/bwt.htm
393 */
394 /* Turn byteCount into cumulative occurrence counts of 0 to n-1. */
395 j=0;
396 for(i=0;i<256;i++) {
397 k=j+byteCount[i];
398 byteCount[i] = j;
399 j=k;
400 }
401 /* Figure out what order dbuf would be in if we sorted it. */
402 for (i=0;i<dbufCount;i++) {
403 uc=(unsigned char)(dbuf[i] & 0xff);
404 dbuf[byteCount[uc]] |= (i << 8);
405 byteCount[uc]++;
406 }
407 /* Decode first byte by hand to initialize "previous" byte. Note that it
408 doesn't get output, and if the first three characters are identical
409 it doesn't qualify as a run (hence writeRunCountdown=5). */
410 if(dbufCount) {
411 if(origPtr>=dbufCount) return RETVAL_DATA_ERROR;
412 bd->writePos=dbuf[origPtr];
413 bd->writeCurrent=(unsigned char)(bd->writePos&0xff);
414 bd->writePos>>=8;
415 bd->writeRunCountdown=5;
416 }
417 bd->writeCount=dbufCount;
418
419 return RETVAL_OK;
420}
421
422/* Undo burrows-wheeler transform on intermediate buffer to produce output.
423 If start_bunzip was initialized with out_fd=-1, then up to len bytes of
424 data are written to outbuf. Return value is number of bytes written or
425 error (all errors are negative numbers). If out_fd!=-1, outbuf and len
426 are ignored, data is written to out_fd and return is RETVAL_OK or error.
427*/
428
429static int read_bunzip(bunzip_data *bd, char *outbuf, int len)
430{
431 const unsigned int *dbuf;
432 int pos,current,previous,gotcount;
433
434 /* If last read was short due to end of file, return last block now */
435 if(bd->writeCount<0) return bd->writeCount;
436
437 gotcount = 0;
438 dbuf=bd->dbuf;
439 pos=bd->writePos;
440 current=bd->writeCurrent;
441
442 /* We will always have pending decoded data to write into the output
443 buffer unless this is the very first call (in which case we haven't
444 Huffman-decoded a block into the intermediate buffer yet). */
445
446 if (bd->writeCopies) {
447 /* Inside the loop, writeCopies means extra copies (beyond 1) */
448 --bd->writeCopies;
449 /* Loop outputting bytes */
450 for(;;) {
451 /* If the output buffer is full, snapshot state and return */
452 if(gotcount >= len) {
453 bd->writePos=pos;
454 bd->writeCurrent=current;
455 bd->writeCopies++;
456 return len;
457 }
458 /* Write next byte into output buffer, updating CRC */
459 outbuf[gotcount++] = current;
460 bd->writeCRC=(((bd->writeCRC)<<8)
461 ^bd->crc32Table[((bd->writeCRC)>>24)^current]);
462 /* Loop now if we're outputting multiple copies of this byte */
463 if (bd->writeCopies) {
464 --bd->writeCopies;
465 continue;
466 }
467decode_next_byte:
468 if (!bd->writeCount--) break;
469 /* Follow sequence vector to undo Burrows-Wheeler transform */
470 previous=current;
471 pos=dbuf[pos];
472 current=pos&0xff;
473 pos>>=8;
474 /* After 3 consecutive copies of the same byte, the 4th is a repeat
475 count. We count down from 4 instead
476 * of counting up because testing for non-zero is faster */
477 if(--bd->writeRunCountdown) {
478 if(current!=previous) bd->writeRunCountdown=4;
479 } else {
480 /* We have a repeated run, this byte indicates the count */
481 bd->writeCopies=current;
482 current=previous;
483 bd->writeRunCountdown=5;
484 /* Sometimes there are just 3 bytes (run length 0) */
485 if(!bd->writeCopies) goto decode_next_byte;
486 /* Subtract the 1 copy we'd output anyway to get extras */
487 --bd->writeCopies;
488 }
489 }
490 /* Decompression of this block completed successfully */
491 bd->writeCRC=~bd->writeCRC;
492 bd->totalCRC=((bd->totalCRC<<1) | (bd->totalCRC>>31)) ^ bd->writeCRC;
493 /* If this block had a CRC error, force file level CRC error. */
494 if(bd->writeCRC!=bd->headerCRC) {
495 bd->totalCRC=bd->headerCRC+1;
496 return RETVAL_LAST_BLOCK;
497 }
498 }
499
500 /* Refill the intermediate buffer by Huffman-decoding next block of input */
501 /* (previous is just a convenient unused temp variable here) */
502 previous=get_next_block(bd);
503 if(previous) {
504 bd->writeCount=previous;
505 return (previous!=RETVAL_LAST_BLOCK) ? previous : gotcount;
506 }
507 bd->writeCRC=0xffffffffUL;
508 pos=bd->writePos;
509 current=bd->writeCurrent;
510 goto decode_next_byte;
511}
512
513/* Allocate the structure, read file header. If in_fd==-1, inbuf must contain
514 a complete bunzip file (len bytes long). If in_fd!=-1, inbuf and len are
515 ignored, and data is read from file handle into temporary buffer. */
516static int start_bunzip(bunzip_data **bdp, int in_fd, char *inbuf, int len)
517{
518 bunzip_data *bd;
519 unsigned int i,j,c;
520 const unsigned int BZh0=(((unsigned int)'B')<<24)+(((unsigned int)'Z')<<16)
521 +(((unsigned int)'h')<<8)+(unsigned int)'0';
522
523 /* Figure out how much data to allocate */
524 i=sizeof(bunzip_data);
525 if(in_fd!=-1) i+=IOBUF_SIZE;
526 /* Allocate bunzip_data. Most fields initialize to zero. */
527 bd=*bdp=xmalloc(i);
528 memset(bd,0,sizeof(bunzip_data));
529 /* Setup input buffer */
530 if(-1==(bd->in_fd=in_fd)) {
531 bd->inbuf=inbuf;
532 bd->inbufCount=len;
533 } else bd->inbuf=(unsigned char *)(bd+1);
534 /* Init the CRC32 table (big endian) */
535 for(i=0;i<256;i++) {
536 c=i<<24;
537 for(j=8;j;j--)
538 c=c&0x80000000 ? (c<<1)^0x04c11db7 : (c<<1);
539 bd->crc32Table[i]=c;
540 }
541 /* Setup for I/O error handling via longjmp */
542 i=setjmp(bd->jmpbuf);
543 if(i) return i;
544
545 /* Ensure that file starts with "BZh['1'-'9']." */
546 i = get_bits(bd,32);
547 if (((unsigned int)(i-BZh0-1)) >= 9) return RETVAL_NOT_BZIP_DATA;
548
549 /* Fourth byte (ascii '1'-'9'), indicates block size in units of 100k of
550 uncompressed data. Allocate intermediate buffer for block. */
551 bd->dbufSize=100000*(i-BZh0);
552
553 bd->dbuf=xmalloc(bd->dbufSize * sizeof(int));
554 return RETVAL_OK;
555}
556
557/* Example usage: decompress src_fd to dst_fd. (Stops at end of bzip data,
558 not end of file.) */
559extern int uncompressStream(int src_fd, int dst_fd)
560{
561 char *outbuf;
562 bunzip_data *bd;
563 int i;
564
565 outbuf=xmalloc(IOBUF_SIZE);
566 if(!(i=start_bunzip(&bd,src_fd,0,0))) {
567 for(;;) {
568 if((i=read_bunzip(bd,outbuf,IOBUF_SIZE)) <= 0) break;
569 if(i!=write(dst_fd,outbuf,i)) {
570 i=RETVAL_UNEXPECTED_OUTPUT_EOF;
571 break;
572 }
573 }
574 }
575 /* Check CRC and release memory */
576 if(i==RETVAL_LAST_BLOCK) {
577 if (bd->headerCRC!=bd->totalCRC) {
578 bb_error_msg("Data integrity error when decompressing.");
579 } else {
580 i=RETVAL_OK;
581 }
582 }
583 else if (i==RETVAL_UNEXPECTED_OUTPUT_EOF) {
584 bb_error_msg("Compressed file ends unexpectedly");
585 } else {
586 bb_error_msg("Decompression failed");
587 }
588 if(bd->dbuf) free(bd->dbuf);
589 free(bd);
590 free(outbuf);
591
592 return i;
593}
594
595#ifdef TESTING
596
597static char * const bunzip_errors[]={NULL,"Bad file checksum","Not bzip data",
598 "Unexpected input EOF","Unexpected output EOF","Data error",
599 "Out of memory","Obsolete (pre 0.9.5) bzip format not supported."};
600
601/* Dumb little test thing, decompress stdin to stdout */
602int main(int argc, char *argv[])
603{
604 int i=uncompressStream(0,1);
605 char c;
606
607 if(i) fprintf(stderr,"%s\n", bunzip_errors[-i]);
608 else if(read(0,&c,1)) fprintf(stderr,"Trailing garbage ignored\n");
609 return -i;
610}
611#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 @@
1#include "config.h"
2#include "libbb.h"
3
4/* uncompress for busybox -- (c) 2002 Robert Griebl
5 *
6 * based on the original compress42.c source
7 * (see disclaimer below)
8 */
9
10
11/* (N)compress42.c - File compression ala IEEE Computer, Mar 1992.
12 *
13 * Authors:
14 * Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
15 * Jim McKie (decvax!mcvax!jim)
16 * Steve Davies (decvax!vax135!petsd!peora!srd)
17 * Ken Turkowski (decvax!decwrl!turtlevax!ken)
18 * James A. Woods (decvax!ihnp4!ames!jaw)
19 * Joe Orost (decvax!vax135!petsd!joe)
20 * Dave Mack (csu@alembic.acs.com)
21 * Peter Jannesen, Network Communication Systems
22 * (peter@ncs.nl)
23 *
24 * marc@suse.de : a small security fix for a buffer overflow
25 *
26 * [... History snipped ...]
27 *
28 */
29#include <stdio.h>
30#include <string.h>
31#include <unistd.h>
32
33/* Default input buffer size */
34#define IBUFSIZ 2048
35
36/* Default output buffer size */
37#define OBUFSIZ 2048
38
39/* Defines for third byte of header */
40#define MAGIC_1 (char_type)'\037' /* First byte of compressed file */
41#define MAGIC_2 (char_type)'\235' /* Second byte of compressed file */
42#define BIT_MASK 0x1f /* Mask for 'number of compresssion bits' */
43 /* Masks 0x20 and 0x40 are free. */
44 /* I think 0x20 should mean that there is */
45 /* a fourth header byte (for expansion). */
46#define BLOCK_MODE 0x80 /* Block compresssion if table is full and */
47 /* compression rate is dropping flush tables */
48 /* the next two codes should not be changed lightly, as they must not */
49 /* lie within the contiguous general code space. */
50#define FIRST 257 /* first free entry */
51#define CLEAR 256 /* table clear output code */
52
53#define INIT_BITS 9 /* initial number of bits/code */
54
55
56/* machine variants which require cc -Dmachine: pdp11, z8000, DOS */
57#define FAST
58
59#define HBITS 17 /* 50% occupancy */
60#define HSIZE (1<<HBITS)
61#define HMASK (HSIZE-1)
62#define HPRIME 9941
63#define BITS 16
64#undef MAXSEG_64K
65#define MAXCODE(n) (1L << (n))
66
67/* Block compress mode -C compatible with 2.0 */
68int block_mode = BLOCK_MODE;
69
70/* user settable max # bits/code */
71int maxbits = BITS;
72
73/* Exitcode of compress (-1 no file compressed) */
74int exit_code = -1;
75
76/* Input buffer */
77unsigned char inbuf[IBUFSIZ + 64];
78
79/* Output buffer */
80unsigned char outbuf[OBUFSIZ + 2048];
81
82
83long int htab[HSIZE];
84unsigned short codetab[HSIZE];
85
86#define htabof(i) htab[i]
87#define codetabof(i) codetab[i]
88#define tab_prefixof(i) codetabof(i)
89#define tab_suffixof(i) ((unsigned char *)(htab))[i]
90#define de_stack ((unsigned char *)&(htab[HSIZE-1]))
91#define clear_htab() memset(htab, -1, sizeof(htab))
92#define clear_tab_prefixof() memset(codetab, 0, 256);
93
94
95/*
96 * Decompress stdin to stdout. This routine adapts to the codes in the
97 * file building the "string" table on-the-fly; requiring no table to
98 * be stored in the compressed file. The tables used herein are shared
99 * with those of the compress() routine. See the definitions above.
100 */
101
102extern int uncompress(int fd_in, int fd_out)
103{
104 unsigned char *stackp;
105 long int code;
106 int finchar;
107 long int oldcode;
108 long int incode;
109 int inbits;
110 int posbits;
111 int outpos;
112 int insize;
113 int bitmask;
114 long int free_ent;
115 long int maxcode;
116 long int maxmaxcode;
117 int n_bits;
118 int rsize = 0;
119
120 insize = 0;
121
122 inbuf[0] = bb_xread_char(fd_in);
123
124 maxbits = inbuf[0] & BIT_MASK;
125 block_mode = inbuf[0] & BLOCK_MODE;
126 maxmaxcode = MAXCODE(maxbits);
127
128 if (maxbits > BITS) {
129 bb_error_msg("compressed with %d bits, can only handle %d bits", maxbits,
130 BITS);
131 return -1;
132 }
133
134 maxcode = MAXCODE(n_bits = INIT_BITS) - 1;
135 bitmask = (1 << n_bits) - 1;
136 oldcode = -1;
137 finchar = 0;
138 outpos = 0;
139 posbits = 0 << 3;
140
141 free_ent = ((block_mode) ? FIRST : 256);
142
143 /* As above, initialize the first 256 entries in the table. */
144 clear_tab_prefixof();
145
146 for (code = 255; code >= 0; --code) {
147 tab_suffixof(code) = (unsigned char) code;
148 }
149
150 do {
151 resetbuf:;
152 {
153 int i;
154 int e;
155 int o;
156
157 e = insize - (o = (posbits >> 3));
158
159 for (i = 0; i < e; ++i)
160 inbuf[i] = inbuf[i + o];
161
162 insize = e;
163 posbits = 0;
164 }
165
166 if (insize < (int) sizeof(inbuf) - IBUFSIZ) {
167 rsize = safe_read(fd_in, inbuf + insize, IBUFSIZ);
168 insize += rsize;
169 }
170
171 inbits = ((rsize > 0) ? (insize - insize % n_bits) << 3 :
172 (insize << 3) - (n_bits - 1));
173
174 while (inbits > posbits) {
175 if (free_ent > maxcode) {
176 posbits =
177 ((posbits - 1) +
178 ((n_bits << 3) -
179 (posbits - 1 + (n_bits << 3)) % (n_bits << 3)));
180 ++n_bits;
181 if (n_bits == maxbits) {
182 maxcode = maxmaxcode;
183 } else {
184 maxcode = MAXCODE(n_bits) - 1;
185 }
186 bitmask = (1 << n_bits) - 1;
187 goto resetbuf;
188 }
189 {
190 unsigned char *p = &inbuf[posbits >> 3];
191
192 code =
193 ((((long) (p[0])) | ((long) (p[1]) << 8) |
194 ((long) (p[2]) << 16)) >> (posbits & 0x7)) & bitmask;
195 }
196 posbits += n_bits;
197
198
199 if (oldcode == -1) {
200 outbuf[outpos++] = (unsigned char) (finchar =
201 (int) (oldcode = code));
202 continue;
203 }
204
205 if (code == CLEAR && block_mode) {
206 clear_tab_prefixof();
207 free_ent = FIRST - 1;
208 posbits =
209 ((posbits - 1) +
210 ((n_bits << 3) -
211 (posbits - 1 + (n_bits << 3)) % (n_bits << 3)));
212 maxcode = MAXCODE(n_bits = INIT_BITS) - 1;
213 bitmask = (1 << n_bits) - 1;
214 goto resetbuf;
215 }
216
217 incode = code;
218 stackp = de_stack;
219
220 /* Special case for KwKwK string. */
221 if (code >= free_ent) {
222 if (code > free_ent) {
223 unsigned char *p;
224
225 posbits -= n_bits;
226 p = &inbuf[posbits >> 3];
227
228 bb_error_msg
229 ("insize:%d posbits:%d inbuf:%02X %02X %02X %02X %02X (%d)",
230 insize, posbits, p[-1], p[0], p[1], p[2], p[3],
231 (posbits & 07));
232 bb_error_msg("uncompress: corrupt input");
233 return -1;
234 }
235
236 *--stackp = (unsigned char) finchar;
237 code = oldcode;
238 }
239
240 /* Generate output characters in reverse order */
241 while ((long int) code >= (long int) 256) {
242 *--stackp = tab_suffixof(code);
243 code = tab_prefixof(code);
244 }
245
246 *--stackp = (unsigned char) (finchar = tab_suffixof(code));
247
248 /* And put them out in forward order */
249 {
250 int i;
251
252 if (outpos + (i = (de_stack - stackp)) >= OBUFSIZ) {
253 do {
254 if (i > OBUFSIZ - outpos) {
255 i = OBUFSIZ - outpos;
256 }
257
258 if (i > 0) {
259 memcpy(outbuf + outpos, stackp, i);
260 outpos += i;
261 }
262
263 if (outpos >= OBUFSIZ) {
264 write(fd_out, outbuf, outpos);
265 outpos = 0;
266 }
267 stackp += i;
268 } while ((i = (de_stack - stackp)) > 0);
269 } else {
270 memcpy(outbuf + outpos, stackp, i);
271 outpos += i;
272 }
273 }
274
275 /* Generate the new entry. */
276 if ((code = free_ent) < maxmaxcode) {
277 tab_prefixof(code) = (unsigned short) oldcode;
278 tab_suffixof(code) = (unsigned char) finchar;
279 free_ent = code + 1;
280 }
281
282 /* Remember previous code. */
283 oldcode = incode;
284 }
285
286 } while (rsize > 0);
287
288 if (outpos > 0) {
289 write(fd_out, outbuf, outpos);
290 }
291
292 return 0;
293}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * gunzip implementation for busybox
4 *
5 * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
6 *
7 * Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
8 * based on gzip sources
9 *
10 * Adjusted further by Erik Andersen <andersen@codepoet.org> to support
11 * files as well as stdin/stdout, and to generally behave itself wrt
12 * command line handling.
13 *
14 * General cleanup to better adhere to the style guide and make use of standard
15 * busybox functions by Glenn McGrath <bug1@iinet.net.au>
16 *
17 * read_gz interface + associated hacking by Laurence Anderson
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 *
33 *
34 * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
35 * Copyright (C) 1992-1993 Jean-loup Gailly
36 * The unzip code was written and put in the public domain by Mark Adler.
37 * Portions of the lzw code are derived from the public domain 'compress'
38 * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
39 * Ken Turkowski, Dave Mack and Peter Jannesen.
40 *
41 * See the license_msg below and the file COPYING for the software license.
42 * See the file algorithm.doc for the compression algorithms and file formats.
43 */
44
45#if 0
46static char *license_msg[] = {
47 " Copyright (C) 1992-1993 Jean-loup Gailly",
48 " This program is free software; you can redistribute it and/or modify",
49 " it under the terms of the GNU General Public License as published by",
50 " the Free Software Foundation; either version 2, or (at your option)",
51 " any later version.",
52 "",
53 " This program is distributed in the hope that it will be useful,",
54 " but WITHOUT ANY WARRANTY; without even the implied warranty of",
55 " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the",
56 " GNU General Public License for more details.",
57 "",
58 " You should have received a copy of the GNU General Public License",
59 " along with this program; if not, write to the Free Software",
60 " Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.",
61 0
62};
63#endif
64
65#include <sys/types.h>
66#include <sys/wait.h>
67#include <signal.h>
68#include <stdlib.h>
69#include <string.h>
70#include <unistd.h>
71#include <fcntl.h>
72#include "config.h"
73#include "busybox.h"
74#include "unarchive.h"
75
76typedef struct huft_s {
77 unsigned char e; /* number of extra bits or operation */
78 unsigned char b; /* number of bits in this code or subcode */
79 union {
80 unsigned short n; /* literal, length base, or distance base */
81 struct huft_s *t; /* pointer to next level of table */
82 } v;
83} huft_t;
84
85static int gunzip_src_fd;
86unsigned int gunzip_bytes_out; /* number of output bytes */
87static unsigned int gunzip_outbuf_count; /* bytes in output buffer */
88
89/* gunzip_window size--must be a power of two, and
90 * at least 32K for zip's deflate method */
91static const int gunzip_wsize = 0x8000;
92static unsigned char *gunzip_window;
93
94static unsigned int *gunzip_crc_table;
95unsigned int gunzip_crc;
96
97/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
98#define BMAX 16 /* maximum bit length of any code (16 for explode) */
99#define N_MAX 288 /* maximum number of codes in any set */
100
101/* bitbuffer */
102static unsigned int gunzip_bb; /* bit buffer */
103static unsigned char gunzip_bk; /* bits in bit buffer */
104
105/* These control the size of the bytebuffer */
106static unsigned int bytebuffer_max = 0x8000;
107static unsigned char *bytebuffer = NULL;
108static unsigned int bytebuffer_offset = 0;
109static unsigned int bytebuffer_size = 0;
110
111static const unsigned short mask_bits[] = {
112 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
113 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
114};
115
116/* Copy lengths for literal codes 257..285 */
117static const unsigned short cplens[] = {
118 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
119 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
120};
121
122/* note: see note #13 above about the 258 in this list. */
123/* Extra bits for literal codes 257..285 */
124static const unsigned char cplext[] = {
125 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,
126 5, 5, 5, 0, 99, 99
127}; /* 99==invalid */
128
129/* Copy offsets for distance codes 0..29 */
130static const unsigned short cpdist[] = {
131 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
132 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
133};
134
135/* Extra bits for distance codes */
136static const unsigned char cpdext[] = {
137 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
138 11, 11, 12, 12, 13, 13
139};
140
141/* Tables for deflate from PKZIP's appnote.txt. */
142/* Order of the bit length code lengths */
143static const unsigned char border[] = {
144 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
145};
146
147static unsigned int fill_bitbuffer(unsigned int bitbuffer, unsigned int *current, const unsigned int required)
148{
149 while (*current < required) {
150 if (bytebuffer_offset >= bytebuffer_size) {
151 /* Leave the first 4 bytes empty so we can always unwind the bitbuffer
152 * to the front of the bytebuffer, leave 4 bytes free at end of tail
153 * so we can easily top up buffer in check_trailer_gzip() */
154 bytebuffer_size = 4 + bb_xread(gunzip_src_fd, &bytebuffer[4], bytebuffer_max - 8);
155 bytebuffer_offset = 4;
156 }
157 bitbuffer |= ((unsigned int) bytebuffer[bytebuffer_offset]) << *current;
158 bytebuffer_offset++;
159 *current += 8;
160 }
161 return(bitbuffer);
162}
163
164static void make_gunzip_crc_table(void)
165{
166 const unsigned int poly = 0xedb88320; /* polynomial exclusive-or pattern */
167 unsigned short i; /* counter for all possible eight bit values */
168
169 /* initial shift register value */
170 gunzip_crc = 0xffffffffL;
171 gunzip_crc_table = (unsigned int *) malloc(256 * sizeof(unsigned int));
172
173 /* Compute and print table of CRC's, five per line */
174 for (i = 0; i < 256; i++) {
175 unsigned int table_entry; /* crc shift register */
176 unsigned char k; /* byte being shifted into crc apparatus */
177
178 table_entry = i;
179 /* The idea to initialize the register with the byte instead of
180 * zero was stolen from Haruhiko Okumura's ar002
181 */
182 for (k = 8; k; k--) {
183 if (table_entry & 1) {
184 table_entry = (table_entry >> 1) ^ poly;
185 } else {
186 table_entry >>= 1;
187 }
188 }
189 gunzip_crc_table[i] = table_entry;
190 }
191}
192
193/*
194 * Free the malloc'ed tables built by huft_build(), which makes a linked
195 * list of the tables it made, with the links in a dummy first entry of
196 * each table.
197 * t: table to free
198 */
199static int huft_free(huft_t * t)
200{
201 huft_t *p;
202 huft_t *q;
203
204 /* Go through linked list, freeing from the malloced (t[-1]) address. */
205 p = t;
206 while (p != (huft_t *) NULL) {
207 q = (--p)->v.t;
208 free((char *) p);
209 p = q;
210 }
211 return 0;
212}
213
214/* Given a list of code lengths and a maximum table size, make a set of
215 * tables to decode that set of codes. Return zero on success, one if
216 * the given code set is incomplete (the tables are still built in this
217 * case), two if the input is invalid (all zero length codes or an
218 * oversubscribed set of lengths), and three if not enough memory.
219 *
220 * b: code lengths in bits (all assumed <= BMAX)
221 * n: number of codes (assumed <= N_MAX)
222 * s: number of simple-valued codes (0..s-1)
223 * d: list of base values for non-simple codes
224 * e: list of extra bits for non-simple codes
225 * t: result: starting table
226 * m: maximum lookup bits, returns actual
227 */
228static int huft_build(unsigned int *b, const unsigned int n,
229 const unsigned int s, const unsigned short *d,
230 const unsigned char *e, huft_t ** t, int *m)
231{
232 unsigned a; /* counter for codes of length k */
233 unsigned c[BMAX + 1]; /* bit length count table */
234 unsigned f; /* i repeats in table every f entries */
235 int g; /* maximum code length */
236 int h; /* table level */
237 register unsigned i; /* counter, current code */
238 register unsigned j; /* counter */
239 register int k; /* number of bits in current code */
240 int l; /* bits per table (returned in m) */
241 register unsigned *p; /* pointer into c[], b[], or v[] */
242 register huft_t *q; /* points to current table */
243 huft_t r; /* table entry for structure assignment */
244 huft_t *u[BMAX]; /* table stack */
245 unsigned v[N_MAX]; /* values in order of bit length */
246 register int w; /* bits before this table == (l * h) */
247 unsigned x[BMAX + 1]; /* bit offsets, then code stack */
248 unsigned *xp; /* pointer into x */
249 int y; /* number of dummy codes added */
250 unsigned z; /* number of entries in current table */
251
252 /* Generate counts for each bit length */
253 memset((void *) (c), 0, sizeof(c));
254 p = b;
255 i = n;
256 do {
257 c[*p]++; /* assume all entries <= BMAX */
258 p++; /* Can't combine with above line (Solaris bug) */
259 } while (--i);
260 if (c[0] == n) { /* null input--all zero length codes */
261 *t = (huft_t *) NULL;
262 *m = 0;
263 return 0;
264 }
265
266 /* Find minimum and maximum length, bound *m by those */
267 l = *m;
268 for (j = 1; j <= BMAX; j++) {
269 if (c[j]) {
270 break;
271 }
272 }
273 k = j; /* minimum code length */
274 if ((unsigned) l < j) {
275 l = j;
276 }
277 for (i = BMAX; i; i--) {
278 if (c[i]) {
279 break;
280 }
281 }
282 g = i; /* maximum code length */
283 if ((unsigned) l > i) {
284 l = i;
285 }
286 *m = l;
287
288 /* Adjust last length count to fill out codes, if needed */
289 for (y = 1 << j; j < i; j++, y <<= 1) {
290 if ((y -= c[j]) < 0) {
291 return 2; /* bad input: more codes than bits */
292 }
293 }
294 if ((y -= c[i]) < 0) {
295 return 2;
296 }
297 c[i] += y;
298
299 /* Generate starting offsets into the value table for each length */
300 x[1] = j = 0;
301 p = c + 1;
302 xp = x + 2;
303 while (--i) { /* note that i == g from above */
304 *xp++ = (j += *p++);
305 }
306
307 /* Make a table of values in order of bit lengths */
308 p = b;
309 i = 0;
310 do {
311 if ((j = *p++) != 0) {
312 v[x[j]++] = i;
313 }
314 } while (++i < n);
315
316 /* Generate the Huffman codes and for each, make the table entries */
317 x[0] = i = 0; /* first Huffman code is zero */
318 p = v; /* grab values in bit order */
319 h = -1; /* no tables yet--level -1 */
320 w = -l; /* bits decoded == (l * h) */
321 u[0] = (huft_t *) NULL; /* just to keep compilers happy */
322 q = (huft_t *) NULL; /* ditto */
323 z = 0; /* ditto */
324
325 /* go through the bit lengths (k already is bits in shortest code) */
326 for (; k <= g; k++) {
327 a = c[k];
328 while (a--) {
329 /* here i is the Huffman code of length k bits for value *p */
330 /* make tables up to required level */
331 while (k > w + l) {
332 h++;
333 w += l; /* previous table always l bits */
334
335 /* compute minimum size table less than or equal to l bits */
336 z = (z = g - w) > (unsigned) l ? l : z; /* upper limit on table size */
337 if ((f = 1 << (j = k - w)) > a + 1) { /* try a k-w bit table *//* too few codes for k-w bit table */
338 f -= a + 1; /* deduct codes from patterns left */
339 xp = c + k;
340 while (++j < z) { /* try smaller tables up to z bits */
341 if ((f <<= 1) <= *++xp) {
342 break; /* enough codes to use up j bits */
343 }
344 f -= *xp; /* else deduct codes from patterns */
345 }
346 }
347 z = 1 << j; /* table entries for j-bit table */
348
349 /* allocate and link in new table */
350 q = (huft_t *) xmalloc((z + 1) * sizeof(huft_t));
351
352 *t = q + 1; /* link to list for huft_free() */
353 *(t = &(q->v.t)) = NULL;
354 u[h] = ++q; /* table starts after link */
355
356 /* connect to last table, if there is one */
357 if (h) {
358 x[h] = i; /* save pattern for backing up */
359 r.b = (unsigned char) l; /* bits to dump before this table */
360 r.e = (unsigned char) (16 + j); /* bits in this table */
361 r.v.t = q; /* pointer to this table */
362 j = i >> (w - l); /* (get around Turbo C bug) */
363 u[h - 1][j] = r; /* connect to last table */
364 }
365 }
366
367 /* set up table entry in r */
368 r.b = (unsigned char) (k - w);
369 if (p >= v + n) {
370 r.e = 99; /* out of values--invalid code */
371 } else if (*p < s) {
372 r.e = (unsigned char) (*p < 256 ? 16 : 15); /* 256 is end-of-block code */
373 r.v.n = (unsigned short) (*p); /* simple code is just the value */
374 p++; /* one compiler does not like *p++ */
375 } else {
376 r.e = (unsigned char) e[*p - s]; /* non-simple--look up in lists */
377 r.v.n = d[*p++ - s];
378 }
379
380 /* fill code-like entries with r */
381 f = 1 << (k - w);
382 for (j = i >> w; j < z; j += f) {
383 q[j] = r;
384 }
385
386 /* backwards increment the k-bit code i */
387 for (j = 1 << (k - 1); i & j; j >>= 1) {
388 i ^= j;
389 }
390 i ^= j;
391
392 /* backup over finished tables */
393 while ((i & ((1 << w) - 1)) != x[h]) {
394 h--; /* don't need to update q */
395 w -= l;
396 }
397 }
398 }
399 /* Return true (1) if we were given an incomplete table */
400 return y != 0 && g != 1;
401}
402
403/*
404 * inflate (decompress) the codes in a deflated (compressed) block.
405 * Return an error code or zero if it all goes ok.
406 *
407 * tl, td: literal/length and distance decoder tables
408 * bl, bd: number of bits decoded by tl[] and td[]
409 */
410static int inflate_codes(huft_t * my_tl, huft_t * my_td, const unsigned int my_bl, const unsigned int my_bd, int setup)
411{
412 static unsigned int e; /* table entry flag/number of extra bits */
413 static unsigned int n, d; /* length and index for copy */
414 static unsigned int w; /* current gunzip_window position */
415 static huft_t *t; /* pointer to table entry */
416 static unsigned int ml, md; /* masks for bl and bd bits */
417 static unsigned int b; /* bit buffer */
418 static unsigned int k; /* number of bits in bit buffer */
419 static huft_t *tl, *td;
420 static unsigned int bl, bd;
421 static int resumeCopy = 0;
422
423 if (setup) { // 1st time we are called, copy in variables
424 tl = my_tl;
425 td = my_td;
426 bl = my_bl;
427 bd = my_bd;
428 /* make local copies of globals */
429 b = gunzip_bb; /* initialize bit buffer */
430 k = gunzip_bk;
431 w = gunzip_outbuf_count; /* initialize gunzip_window position */
432
433 /* inflate the coded data */
434 ml = mask_bits[bl]; /* precompute masks for speed */
435 md = mask_bits[bd];
436 return 0; // Don't actually do anything the first time
437 }
438
439 if (resumeCopy) goto do_copy;
440
441 while (1) { /* do until end of block */
442 b = fill_bitbuffer(b, &k, bl);
443 if ((e = (t = tl + ((unsigned) b & ml))->e) > 16)
444 do {
445 if (e == 99) {
446 bb_error_msg_and_die("inflate_codes error 1");;
447 }
448 b >>= t->b;
449 k -= t->b;
450 e -= 16;
451 b = fill_bitbuffer(b, &k, e);
452 } while ((e =
453 (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
454 b >>= t->b;
455 k -= t->b;
456 if (e == 16) { /* then it's a literal */
457 gunzip_window[w++] = (unsigned char) t->v.n;
458 if (w == gunzip_wsize) {
459 gunzip_outbuf_count = (w);
460 //flush_gunzip_window();
461 w = 0;
462 return 1; // We have a block to read
463 }
464 } else { /* it's an EOB or a length */
465
466 /* exit if end of block */
467 if (e == 15) {
468 break;
469 }
470
471 /* get length of block to copy */
472 b = fill_bitbuffer(b, &k, e);
473 n = t->v.n + ((unsigned) b & mask_bits[e]);
474 b >>= e;
475 k -= e;
476
477 /* decode distance of block to copy */
478 b = fill_bitbuffer(b, &k, bd);
479 if ((e = (t = td + ((unsigned) b & md))->e) > 16)
480 do {
481 if (e == 99)
482 bb_error_msg_and_die("inflate_codes error 2");;
483 b >>= t->b;
484 k -= t->b;
485 e -= 16;
486 b = fill_bitbuffer(b, &k, e);
487 } while ((e =
488 (t =
489 t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
490 b >>= t->b;
491 k -= t->b;
492 b = fill_bitbuffer(b, &k, e);
493 d = w - t->v.n - ((unsigned) b & mask_bits[e]);
494 b >>= e;
495 k -= e;
496
497 /* do the copy */
498do_copy: do {
499 n -= (e =
500 (e =
501 gunzip_wsize - ((d &= gunzip_wsize - 1) > w ? d : w)) > n ? n : e);
502 /* copy to new buffer to prevent possible overwrite */
503 if (w - d >= e) { /* (this test assumes unsigned comparison) */
504 memcpy(gunzip_window + w, gunzip_window + d, e);
505 w += e;
506 d += e;
507 } else {
508 /* do it slow to avoid memcpy() overlap */
509 /* !NOMEMCPY */
510 do {
511 gunzip_window[w++] = gunzip_window[d++];
512 } while (--e);
513 }
514 if (w == gunzip_wsize) {
515 gunzip_outbuf_count = (w);
516 if (n) resumeCopy = 1;
517 else resumeCopy = 0;
518 //flush_gunzip_window();
519 w = 0;
520 return 1;
521 }
522 } while (n);
523 resumeCopy = 0;
524 }
525 }
526
527 /* restore the globals from the locals */
528 gunzip_outbuf_count = w; /* restore global gunzip_window pointer */
529 gunzip_bb = b; /* restore global bit buffer */
530 gunzip_bk = k;
531
532 /* normally just after call to inflate_codes, but save code by putting it here */
533 /* free the decoding tables, return */
534 huft_free(tl);
535 huft_free(td);
536
537 /* done */
538 return 0;
539}
540
541static int inflate_stored(int my_n, int my_b_stored, int my_k_stored, int setup)
542{
543 static int n, b_stored, k_stored, w;
544 if (setup) {
545 n = my_n;
546 b_stored = my_b_stored;
547 k_stored = my_k_stored;
548 w = gunzip_outbuf_count; /* initialize gunzip_window position */
549 return 0; // Don't do anything first time
550 }
551
552 /* read and output the compressed data */
553 while (n--) {
554 b_stored = fill_bitbuffer(b_stored, &k_stored, 8);
555 gunzip_window[w++] = (unsigned char) b_stored;
556 if (w == (unsigned int) gunzip_wsize) {
557 gunzip_outbuf_count = (w);
558 //flush_gunzip_window();
559 w = 0;
560 b_stored >>= 8;
561 k_stored -= 8;
562 return 1; // We have a block
563 }
564 b_stored >>= 8;
565 k_stored -= 8;
566 }
567
568 /* restore the globals from the locals */
569 gunzip_outbuf_count = w; /* restore global gunzip_window pointer */
570 gunzip_bb = b_stored; /* restore global bit buffer */
571 gunzip_bk = k_stored;
572 return 0; // Finished
573}
574
575/*
576 * decompress an inflated block
577 * e: last block flag
578 *
579 * GLOBAL VARIABLES: bb, kk,
580 */
581 // Return values: -1 = inflate_stored, -2 = inflate_codes
582static int inflate_block(int *e)
583{
584 unsigned t; /* block type */
585 register unsigned int b; /* bit buffer */
586 unsigned int k; /* number of bits in bit buffer */
587
588 /* make local bit buffer */
589
590 b = gunzip_bb;
591 k = gunzip_bk;
592
593 /* read in last block bit */
594 b = fill_bitbuffer(b, &k, 1);
595 *e = (int) b & 1;
596 b >>= 1;
597 k -= 1;
598
599 /* read in block type */
600 b = fill_bitbuffer(b, &k, 2);
601 t = (unsigned) b & 3;
602 b >>= 2;
603 k -= 2;
604
605 /* restore the global bit buffer */
606 gunzip_bb = b;
607 gunzip_bk = k;
608
609 /* inflate that block type */
610 switch (t) {
611 case 0: /* Inflate stored */
612 {
613 unsigned int n; /* number of bytes in block */
614 unsigned int b_stored; /* bit buffer */
615 unsigned int k_stored; /* number of bits in bit buffer */
616
617 /* make local copies of globals */
618 b_stored = gunzip_bb; /* initialize bit buffer */
619 k_stored = gunzip_bk;
620
621 /* go to byte boundary */
622 n = k_stored & 7;
623 b_stored >>= n;
624 k_stored -= n;
625
626 /* get the length and its complement */
627 b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
628 n = ((unsigned) b_stored & 0xffff);
629 b_stored >>= 16;
630 k_stored -= 16;
631
632 b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
633 if (n != (unsigned) ((~b_stored) & 0xffff)) {
634 return 1; /* error in compressed data */
635 }
636 b_stored >>= 16;
637 k_stored -= 16;
638
639 inflate_stored(n, b_stored, k_stored, 1); // Setup inflate_stored
640 return -1;
641 }
642 case 1: /* Inflate fixed
643 * decompress an inflated type 1 (fixed Huffman codes) block. We should
644 * either replace this with a custom decoder, or at least precompute the
645 * Huffman tables.
646 */
647 {
648 int i; /* temporary variable */
649 huft_t *tl; /* literal/length code table */
650 huft_t *td; /* distance code table */
651 unsigned int bl; /* lookup bits for tl */
652 unsigned int bd; /* lookup bits for td */
653 unsigned int l[288]; /* length list for huft_build */
654
655 /* set up literal table */
656 for (i = 0; i < 144; i++) {
657 l[i] = 8;
658 }
659 for (; i < 256; i++) {
660 l[i] = 9;
661 }
662 for (; i < 280; i++) {
663 l[i] = 7;
664 }
665 for (; i < 288; i++) { /* make a complete, but wrong code set */
666 l[i] = 8;
667 }
668 bl = 7;
669 if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0) {
670 return i;
671 }
672
673 /* set up distance table */
674 for (i = 0; i < 30; i++) { /* make an incomplete code set */
675 l[i] = 5;
676 }
677 bd = 5;
678 if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1) {
679 huft_free(tl);
680 return i;
681 }
682
683 /* decompress until an end-of-block code */
684 inflate_codes(tl, td, bl, bd, 1); // Setup inflate_codes
685
686 /* huft_free code moved into inflate_codes */
687
688 return -2;
689 }
690 case 2: /* Inflate dynamic */
691 {
692 const int dbits = 6; /* bits in base distance lookup table */
693 const int lbits = 9; /* bits in base literal/length lookup table */
694
695 huft_t *tl; /* literal/length code table */
696 huft_t *td; /* distance code table */
697 unsigned int i; /* temporary variables */
698 unsigned int j;
699 unsigned int l; /* last length */
700 unsigned int m; /* mask for bit lengths table */
701 unsigned int n; /* number of lengths to get */
702 unsigned int bl; /* lookup bits for tl */
703 unsigned int bd; /* lookup bits for td */
704 unsigned int nb; /* number of bit length codes */
705 unsigned int nl; /* number of literal/length codes */
706 unsigned int nd; /* number of distance codes */
707
708 unsigned int ll[286 + 30]; /* literal/length and distance code lengths */
709 unsigned int b_dynamic; /* bit buffer */
710 unsigned int k_dynamic; /* number of bits in bit buffer */
711
712 /* make local bit buffer */
713 b_dynamic = gunzip_bb;
714 k_dynamic = gunzip_bk;
715
716 /* read in table lengths */
717 b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
718 nl = 257 + ((unsigned int) b_dynamic & 0x1f); /* number of literal/length codes */
719
720 b_dynamic >>= 5;
721 k_dynamic -= 5;
722 b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
723 nd = 1 + ((unsigned int) b_dynamic & 0x1f); /* number of distance codes */
724
725 b_dynamic >>= 5;
726 k_dynamic -= 5;
727 b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 4);
728 nb = 4 + ((unsigned int) b_dynamic & 0xf); /* number of bit length codes */
729
730 b_dynamic >>= 4;
731 k_dynamic -= 4;
732 if (nl > 286 || nd > 30) {
733 return 1; /* bad lengths */
734 }
735
736 /* read in bit-length-code lengths */
737 for (j = 0; j < nb; j++) {
738 b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
739 ll[border[j]] = (unsigned int) b_dynamic & 7;
740 b_dynamic >>= 3;
741 k_dynamic -= 3;
742 }
743 for (; j < 19; j++) {
744 ll[border[j]] = 0;
745 }
746
747 /* build decoding table for trees--single level, 7 bit lookup */
748 bl = 7;
749 i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl);
750 if (i != 0) {
751 if (i == 1) {
752 huft_free(tl);
753 }
754 return i; /* incomplete code set */
755 }
756
757 /* read in literal and distance code lengths */
758 n = nl + nd;
759 m = mask_bits[bl];
760 i = l = 0;
761 while ((unsigned int) i < n) {
762 b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, (unsigned int)bl);
763 j = (td = tl + ((unsigned int) b_dynamic & m))->b;
764 b_dynamic >>= j;
765 k_dynamic -= j;
766 j = td->v.n;
767 if (j < 16) { /* length of code in bits (0..15) */
768 ll[i++] = l = j; /* save last length in l */
769 } else if (j == 16) { /* repeat last length 3 to 6 times */
770 b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 2);
771 j = 3 + ((unsigned int) b_dynamic & 3);
772 b_dynamic >>= 2;
773 k_dynamic -= 2;
774 if ((unsigned int) i + j > n) {
775 return 1;
776 }
777 while (j--) {
778 ll[i++] = l;
779 }
780 } else if (j == 17) { /* 3 to 10 zero length codes */
781 b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
782 j = 3 + ((unsigned int) b_dynamic & 7);
783 b_dynamic >>= 3;
784 k_dynamic -= 3;
785 if ((unsigned int) i + j > n) {
786 return 1;
787 }
788 while (j--) {
789 ll[i++] = 0;
790 }
791 l = 0;
792 } else { /* j == 18: 11 to 138 zero length codes */
793 b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 7);
794 j = 11 + ((unsigned int) b_dynamic & 0x7f);
795 b_dynamic >>= 7;
796 k_dynamic -= 7;
797 if ((unsigned int) i + j > n) {
798 return 1;
799 }
800 while (j--) {
801 ll[i++] = 0;
802 }
803 l = 0;
804 }
805 }
806
807 /* free decoding table for trees */
808 huft_free(tl);
809
810 /* restore the global bit buffer */
811 gunzip_bb = b_dynamic;
812 gunzip_bk = k_dynamic;
813
814 /* build the decoding tables for literal/length and distance codes */
815 bl = lbits;
816
817 if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0) {
818 if (i == 1) {
819 bb_error_msg_and_die("Incomplete literal tree");
820 huft_free(tl);
821 }
822 return i; /* incomplete code set */
823 }
824
825 bd = dbits;
826 if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0) {
827 if (i == 1) {
828 bb_error_msg_and_die("incomplete distance tree");
829 huft_free(td);
830 }
831 huft_free(tl);
832 return i; /* incomplete code set */
833 }
834
835 /* decompress until an end-of-block code */
836 inflate_codes(tl, td, bl, bd, 1); // Setup inflate_codes
837
838 /* huft_free code moved into inflate_codes */
839
840 return -2;
841 }
842 default:
843 /* bad block type */
844 bb_error_msg_and_die("bad block type %d\n", t);
845 }
846}
847
848static void calculate_gunzip_crc(void)
849{
850 int n;
851 for (n = 0; n < gunzip_outbuf_count; n++) {
852 gunzip_crc = gunzip_crc_table[((int) gunzip_crc ^ (gunzip_window[n])) & 0xff] ^ (gunzip_crc >> 8);
853 }
854 gunzip_bytes_out += gunzip_outbuf_count;
855}
856
857static int inflate_get_next_window(void)
858{
859 static int method = -1; // Method == -1 for stored, -2 for codes
860 static int e = 0;
861 static int needAnotherBlock = 1;
862
863 gunzip_outbuf_count = 0;
864
865 while(1) {
866 int ret;
867
868 if (needAnotherBlock) {
869 if(e) {
870 calculate_gunzip_crc();
871 e = 0;
872 needAnotherBlock = 1;
873 return 0;
874 } // Last block
875 method = inflate_block(&e);
876 needAnotherBlock = 0;
877 }
878
879 switch (method) {
880 case -1: ret = inflate_stored(0,0,0,0);
881 break;
882 case -2: ret = inflate_codes(0,0,0,0,0);
883 break;
884 default: bb_error_msg_and_die("inflate error %d", method);
885 }
886
887 if (ret == 1) {
888 calculate_gunzip_crc();
889 return 1; // More data left
890 } else needAnotherBlock = 1; // End of that block
891 }
892 /* Doesnt get here */
893}
894
895/* Initialise bytebuffer, be careful not to overfill the buffer */
896extern void inflate_init(unsigned int bufsize)
897{
898 /* Set the bytebuffer size, default is same as gunzip_wsize */
899 bytebuffer_max = bufsize + 8;
900 bytebuffer_offset = 4;
901 bytebuffer_size = 0;
902}
903
904extern int inflate_unzip(int in, int out)
905{
906 ssize_t nwrote;
907 typedef void (*sig_type) (int);
908
909 /* Allocate all global buffers (for DYN_ALLOC option) */
910 gunzip_window = xmalloc(gunzip_wsize);
911 gunzip_outbuf_count = 0;
912 gunzip_bytes_out = 0;
913 gunzip_src_fd = in;
914
915 /* initialize gunzip_window, bit buffer */
916 gunzip_bk = 0;
917 gunzip_bb = 0;
918
919 /* Create the crc table */
920 make_gunzip_crc_table();
921
922 /* Allocate space for buffer */
923 bytebuffer = xmalloc(bytebuffer_max);
924
925 while(1) {
926 int ret = inflate_get_next_window();
927 nwrote = bb_full_write(out, gunzip_window, gunzip_outbuf_count);
928 if (nwrote == -1) {
929 bb_perror_msg("write");
930 return -1;
931 }
932 if (ret == 0) break;
933 }
934
935 /* Cleanup */
936 free(gunzip_window);
937 free(gunzip_crc_table);
938
939 /* Store unused bytes in a global buffer so calling applets can access it */
940 if (gunzip_bk >= 8) {
941 /* Undo too much lookahead. The next read will be byte aligned
942 * so we can discard unused bits in the last meaningful byte. */
943 bytebuffer_offset--;
944 bytebuffer[bytebuffer_offset] = gunzip_bb & 0xff;
945 gunzip_bb >>= 8;
946 gunzip_bk -= 8;
947 }
948 return 0;
949}
950
951extern int inflate_gunzip(int in, int out)
952{
953 unsigned int stored_crc = 0;
954 unsigned char count;
955
956 inflate_unzip(in, out);
957
958 /* top up the input buffer with the rest of the trailer */
959 count = bytebuffer_size - bytebuffer_offset;
960 if (count < 8) {
961 bb_xread_all(in, &bytebuffer[bytebuffer_size], 8 - count);
962 bytebuffer_size += 8 - count;
963 }
964 for (count = 0; count != 4; count++) {
965 stored_crc |= (bytebuffer[bytebuffer_offset] << (count * 8));
966 bytebuffer_offset++;
967 }
968
969 /* Validate decompression - crc */
970 if (stored_crc != (gunzip_crc ^ 0xffffffffL)) {
971 bb_error_msg("crc error");
972 }
973
974 /* Validate decompression - size */
975 if (gunzip_bytes_out !=
976 (bytebuffer[bytebuffer_offset] | (bytebuffer[bytebuffer_offset+1] << 8) |
977 (bytebuffer[bytebuffer_offset+2] << 16) | (bytebuffer[bytebuffer_offset+3] << 24))) {
978 bb_error_msg("Incorrect length");
979 }
980
981 return 0;
982}
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 @@
1/*
2 * Copyright (C) 2002 by Glenn McGrath
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19#include <fnmatch.h>
20#include <stdlib.h>
21
22#include "unarchive.h"
23
24/* Accept any non-null name, its not really a filter at all */
25extern char filter_accept_all(archive_handle_t *archive_handle)
26{
27 if (archive_handle->file_header->name) {
28 return(EXIT_SUCCESS);
29 } else {
30 return(EXIT_FAILURE);
31 }
32}
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 @@
1/*
2 * Copyright (C) 2002 by Glenn McGrath
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19#include <fnmatch.h>
20#include <stdlib.h>
21
22#include "unarchive.h"
23
24/*
25 * Accept names that are in the accept list, ignoring reject list.
26 */
27extern char filter_accept_list(archive_handle_t *archive_handle)
28{
29 if (find_list_entry(archive_handle->accept, archive_handle->file_header->name)) {
30 return(EXIT_SUCCESS);
31 } else {
32 return(EXIT_FAILURE);
33 }
34}
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 @@
1/*
2 * Copyright (C) 2002 by Glenn McGrath
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19#include <stdlib.h>
20#include <string.h>
21#include <unistd.h>
22
23#include "libbb.h"
24#include "unarchive.h"
25
26/*
27 * Reassign the subarchive metadata parser based on the filename extension
28 * e.g. if its a .tar.gz modify archive_handle->sub_archive to process a .tar.gz
29 * or if its a .tar.bz2 make archive_handle->sub_archive handle that
30 */
31extern char filter_accept_list_reassign(archive_handle_t *archive_handle)
32{
33 /* Check the file entry is in the accept list */
34 if (find_list_entry(archive_handle->accept, archive_handle->file_header->name)) {
35 const char *name_ptr;
36
37 /* Extract the last 2 extensions */
38 name_ptr = strrchr(archive_handle->file_header->name, '.');
39
40 /* Modify the subarchive handler based on the extension */
41#ifdef CONFIG_FEATURE_DEB_TAR_GZ
42 if (strcmp(name_ptr, ".gz") == 0) {
43 archive_handle->action_data_subarchive = get_header_tar_gz;
44 return(EXIT_SUCCESS);
45 }
46#endif
47#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
48 if (strcmp(name_ptr, ".bz2") == 0) {
49 archive_handle->action_data_subarchive = get_header_tar_bz2;
50 return(EXIT_SUCCESS);
51 }
52#endif
53 }
54 return(EXIT_FAILURE);
55}
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 @@
1/*
2 * Copyright (C) 2002 by Glenn McGrath
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19#include <fnmatch.h>
20#include <stdlib.h>
21
22#include "unarchive.h"
23
24/*
25 * Accept names that are in the accept list and not in the reject list
26 */
27extern char filter_accept_reject_list(archive_handle_t *archive_handle)
28{
29 const char *key = archive_handle->file_header->name;
30 const llist_t *accept_entry = find_list_entry(archive_handle->accept, key);
31 const llist_t *reject_entry = find_list_entry(archive_handle->reject, key);
32
33 /* If the key is in a reject list fail */
34 if (reject_entry) {
35 return(EXIT_FAILURE);
36 }
37
38 /* Fail if an accept list was specified and the key wasnt in there */
39 if (archive_handle->accept && (accept_entry == NULL)) {
40 return(EXIT_FAILURE);
41 }
42
43 /* Accepted */
44 return(EXIT_SUCCESS);
45}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <fnmatch.h>
18#include <stdlib.h>
19#include "unarchive.h"
20
21extern const llist_t *find_list_entry(const llist_t *list, const char *filename)
22{
23 while (list) {
24 if (fnmatch(list->data, filename, 0) == 0) {
25 return(list);
26 }
27 list = list->link;
28 }
29 return(NULL);
30}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Library General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <unistd.h>
21#include "unarchive.h"
22#include "libbb.h"
23
24extern char get_header_ar(archive_handle_t *archive_handle)
25{
26 file_header_t *typed = archive_handle->file_header;
27 union {
28 char raw[60];
29 struct {
30 char name[16];
31 char date[12];
32 char uid[6];
33 char gid[6];
34 char mode[8];
35 char size[10];
36 char magic[2];
37 } formated;
38 } ar;
39#ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
40 static char *ar_long_names;
41 static unsigned int ar_long_name_size;
42#endif
43
44 /* dont use bb_xread as we want to handle the error ourself */
45 if (read(archive_handle->src_fd, ar.raw, 60) != 60) {
46 /* End Of File */
47 return(EXIT_FAILURE);
48 }
49
50 /* ar header starts on an even byte (2 byte aligned)
51 * '\n' is used for padding
52 */
53 if (ar.raw[0] == '\n') {
54 /* fix up the header, we started reading 1 byte too early */
55 memmove(ar.raw, &ar.raw[1], 59);
56 ar.raw[59] = bb_xread_char(archive_handle->src_fd);
57 archive_handle->offset++;
58 }
59 archive_handle->offset += 60;
60
61 /* align the headers based on the header magic */
62 if ((ar.formated.magic[0] != '`') || (ar.formated.magic[1] != '\n')) {
63 bb_error_msg_and_die("Invalid ar header");
64 }
65
66 typed->mode = strtol(ar.formated.mode, NULL, 8);
67 typed->mtime = atoi(ar.formated.date);
68 typed->uid = atoi(ar.formated.uid);
69 typed->gid = atoi(ar.formated.gid);
70 typed->size = atoi(ar.formated.size);
71
72 /* long filenames have '/' as the first character */
73 if (ar.formated.name[0] == '/') {
74#ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
75 if (ar.formated.name[1] == '/') {
76 /* If the second char is a '/' then this entries data section
77 * stores long filename for multiple entries, they are stored
78 * in static variable long_names for use in future entries */
79 ar_long_name_size = typed->size;
80 ar_long_names = xmalloc(ar_long_name_size);
81 bb_xread_all(archive_handle->src_fd, ar_long_names, ar_long_name_size);
82 archive_handle->offset += ar_long_name_size;
83 /* This ar entries data section only contained filenames for other records
84 * they are stored in the static ar_long_names for future reference */
85 return (get_header_ar(archive_handle)); /* Return next header */
86 } else if (ar.formated.name[1] == ' ') {
87 /* This is the index of symbols in the file for compilers */
88 data_skip(archive_handle);
89 archive_handle->offset += typed->size;
90 return (get_header_ar(archive_handle)); /* Return next header */
91 } else {
92 /* The number after the '/' indicates the offset in the ar data section
93 (saved in variable long_name) that conatains the real filename */
94 const unsigned int long_offset = atoi(&ar.formated.name[1]);
95 if (long_offset >= ar_long_name_size) {
96 bb_error_msg_and_die("Cant resolve long filename");
97 }
98 typed->name = bb_xstrdup(ar_long_names + long_offset);
99 }
100#else
101 bb_error_msg_and_die("long filenames not supported");
102#endif
103 } else {
104 /* short filenames */
105 typed->name = bb_xstrndup(ar.formated.name, 16);
106 }
107
108 typed->name[strcspn(typed->name, " /")] = '\0';
109
110 if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
111 archive_handle->action_header(typed);
112 if (archive_handle->sub_archive) {
113 while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS);
114 } else {
115 archive_handle->action_data(archive_handle);
116 }
117 } else {
118 data_skip(archive_handle);
119 }
120
121 archive_handle->offset += typed->size;
122 /* Set the file pointer to the correct spot, we may have been reading a compressed file */
123 lseek(archive_handle->src_fd, archive_handle->offset, SEEK_SET);
124
125 return(EXIT_SUCCESS);
126}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Library General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <unistd.h>
21#include <sys/sysmacros.h> /* major() and minor() */
22#include "unarchive.h"
23#include "libbb.h"
24
25typedef struct hardlinks_s {
26 file_header_t *entry;
27 int inode;
28 struct hardlinks_s *next;
29} hardlinks_t;
30
31extern char get_header_cpio(archive_handle_t *archive_handle)
32{
33 static hardlinks_t *saved_hardlinks = NULL;
34 static unsigned short pending_hardlinks = 0;
35 file_header_t *file_header = archive_handle->file_header;
36 char cpio_header[110];
37 int namesize;
38 char dummy[16];
39 int major, minor, nlink, inode;
40
41 if (pending_hardlinks) { /* Deal with any pending hardlinks */
42 hardlinks_t *tmp;
43 hardlinks_t *oldtmp;
44
45 tmp = saved_hardlinks;
46 oldtmp = NULL;
47
48 while (tmp) {
49 bb_error_msg_and_die("need to fix this\n");
50 if (tmp->entry->link_name) { /* Found a hardlink ready to be extracted */
51 file_header = tmp->entry;
52 if (oldtmp) {
53 oldtmp->next = tmp->next; /* Remove item from linked list */
54 } else {
55 saved_hardlinks = tmp->next;
56 }
57 free(tmp);
58 continue;
59 }
60 oldtmp = tmp;
61 tmp = tmp->next;
62 }
63 pending_hardlinks = 0; /* No more pending hardlinks, read next file entry */
64 }
65
66 /* There can be padding before archive header */
67 data_align(archive_handle, 4);
68
69 if (archive_xread_all_eof(archive_handle, cpio_header, 110) == 0) {
70 return(EXIT_FAILURE);
71 }
72 archive_handle->offset += 110;
73
74 if ((strncmp(&cpio_header[0], "07070", 5) != 0) || ((cpio_header[5] != '1') && (cpio_header[5] != '2'))) {
75 bb_error_msg_and_die("Unsupported cpio format, use newc or crc");
76 }
77
78 {
79 unsigned long tmpsize;
80 sscanf(cpio_header, "%6c%8x%8x%8x%8x%8x%8lx%8lx%16c%8x%8x%8x%8c",
81 dummy, &inode, (unsigned int*)&file_header->mode,
82 (unsigned int*)&file_header->uid, (unsigned int*)&file_header->gid,
83 &nlink, &file_header->mtime, &tmpsize,
84 dummy, &major, &minor, &namesize, dummy);
85 file_header->size = tmpsize;
86 }
87
88 file_header->name = (char *) xmalloc(namesize + 1);
89 archive_xread_all(archive_handle, file_header->name, namesize); /* Read in filename */
90 file_header->name[namesize] = '\0';
91 archive_handle->offset += namesize;
92
93 /* Update offset amount and skip padding before file contents */
94 data_align(archive_handle, 4);
95
96 if (strcmp(file_header->name, "TRAILER!!!") == 0) {
97 printf("%d blocks\n", (int) (archive_handle->offset % 512 ? (archive_handle->offset / 512) + 1 : archive_handle->offset / 512)); /* Always round up */
98 if (saved_hardlinks) { /* Bummer - we still have unresolved hardlinks */
99 hardlinks_t *tmp = saved_hardlinks;
100 hardlinks_t *oldtmp = NULL;
101 while (tmp) {
102 bb_error_msg("%s not created: cannot resolve hardlink", tmp->entry->name);
103 oldtmp = tmp;
104 tmp = tmp->next;
105 free (oldtmp->entry->name);
106 free (oldtmp->entry);
107 free (oldtmp);
108 }
109 saved_hardlinks = NULL;
110 pending_hardlinks = 0;
111 }
112 return(EXIT_FAILURE);
113 }
114
115 if (S_ISLNK(file_header->mode)) {
116 file_header->link_name = (char *) xmalloc(file_header->size + 1);
117 archive_xread_all(archive_handle, file_header->link_name, file_header->size);
118 file_header->link_name[file_header->size] = '\0';
119 archive_handle->offset += file_header->size;
120 file_header->size = 0; /* Stop possible seeks in future */
121 } else {
122 file_header->link_name = NULL;
123 }
124 if (nlink > 1 && !S_ISDIR(file_header->mode)) {
125 if (file_header->size == 0) { /* Put file on a linked list for later */
126 hardlinks_t *new = xmalloc(sizeof(hardlinks_t));
127 new->next = saved_hardlinks;
128 new->inode = inode;
129 new->entry = file_header;
130 saved_hardlinks = new;
131 return(EXIT_SUCCESS); // Skip this one
132 } else { /* Found the file with data in */
133 hardlinks_t *tmp = saved_hardlinks;
134 pending_hardlinks = 1;
135 while (tmp) {
136 if (tmp->inode == inode) {
137 tmp->entry->link_name = bb_xstrdup(file_header->name);
138 nlink--;
139 }
140 tmp = tmp->next;
141 }
142 if (nlink > 1) {
143 bb_error_msg("error resolving hardlink: did you create the archive with GNU cpio 2.0-2.2?");
144 }
145 }
146 }
147 file_header->device = makedev(major, minor);
148
149 if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
150 archive_handle->action_data(archive_handle);
151 archive_handle->action_header(archive_handle->file_header);
152 } else {
153 data_skip(archive_handle);
154 }
155
156 archive_handle->offset += file_header->size;
157
158 free(file_header->link_name);
159
160 return (EXIT_SUCCESS);
161}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Library General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 *
16 * FIXME:
17 * In privileged mode if uname and gname map to a uid and gid then use the
18 * mapped value instead of the uid/gid values in tar header
19 *
20 * References:
21 * GNU tar and star man pages,
22 * Opengroup's ustar interchange format,
23 * http://www.opengroup.org/onlinepubs/007904975/utilities/pax.html
24 */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include "unarchive.h"
30#include "libbb.h"
31
32#ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS
33static char *longname = NULL;
34static char *linkname = NULL;
35#endif
36
37extern char get_header_tar(archive_handle_t *archive_handle)
38{
39 file_header_t *file_header = archive_handle->file_header;
40 union {
41 /* ustar header, Posix 1003.1 */
42 unsigned char raw[512];
43 struct {
44 char name[100]; /* 0-99 */
45 char mode[8]; /* 100-107 */
46 char uid[8]; /* 108-115 */
47 char gid[8]; /* 116-123 */
48 char size[12]; /* 124-135 */
49 char mtime[12]; /* 136-147 */
50 char chksum[8]; /* 148-155 */
51 char typeflag; /* 156-156 */
52 char linkname[100]; /* 157-256 */
53 char magic[6]; /* 257-262 */
54 char version[2]; /* 263-264 */
55 char uname[32]; /* 265-296 */
56 char gname[32]; /* 297-328 */
57 char devmajor[8]; /* 329-336 */
58 char devminor[8]; /* 337-344 */
59 char prefix[155]; /* 345-499 */
60 char padding[12]; /* 500-512 */
61 } formated;
62 } tar;
63 long sum = 0;
64 long i;
65
66 /* Align header */
67 data_align(archive_handle, 512);
68
69 if (bb_full_read(archive_handle->src_fd, tar.raw, 512) != 512) {
70 /* Assume end of file */
71 return(EXIT_FAILURE);
72 }
73 archive_handle->offset += 512;
74
75 /* If there is no filename its an empty header */
76 if (tar.formated.name[0] == 0) {
77 return(EXIT_SUCCESS);
78 }
79
80 /* Check header has valid magic, "ustar" is for the proper tar
81 * 0's are for the old tar format
82 */
83 if (strncmp(tar.formated.magic, "ustar", 5) != 0) {
84#ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY
85 if (strncmp(tar.formated.magic, "\0\0\0\0\0", 5) != 0)
86#endif
87 bb_error_msg_and_die("Invalid tar magic");
88 }
89 /* Do checksum on headers */
90 for (i = 0; i < 148 ; i++) {
91 sum += tar.raw[i];
92 }
93 sum += ' ' * 8;
94 for (i = 156; i < 512 ; i++) {
95 sum += tar.raw[i];
96 }
97 if (sum != strtol(tar.formated.chksum, NULL, 8)) {
98 bb_error_msg("Invalid tar header checksum");
99 return(EXIT_FAILURE);
100 }
101
102#ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS
103 if (longname) {
104 file_header->name = longname;
105 longname = NULL;
106 }
107 else if (linkname) {
108 file_header->name = linkname;
109 linkname = NULL;
110 } else
111#endif
112 if (tar.formated.prefix[0] == 0) {
113 file_header->name = strdup(tar.formated.name);
114 } else {
115 file_header->name = concat_path_file(tar.formated.prefix, tar.formated.name);
116 }
117
118 file_header->uid = strtol(tar.formated.uid, NULL, 8);
119 file_header->gid = strtol(tar.formated.gid, NULL, 8);
120 file_header->size = strtol(tar.formated.size, NULL, 8);
121 file_header->mtime = strtol(tar.formated.mtime, NULL, 8);
122 file_header->link_name = (tar.formated.linkname[0] != '\0') ?
123 bb_xstrdup(tar.formated.linkname) : NULL;
124 file_header->device = makedev(strtol(tar.formated.devmajor, NULL, 8),
125 strtol(tar.formated.devminor, NULL, 8));
126
127 /* Set bits 0-11 of the files mode */
128 file_header->mode = 07777 & strtol(tar.formated.mode, NULL, 8);
129
130 /* Set bits 12-15 of the files mode */
131 switch (tar.formated.typeflag) {
132 /* busybox identifies hard links as being regular files with 0 size and a link name */
133 case '1':
134 file_header->mode |= S_IFREG;
135 break;
136 case 'x':
137 case 'g':
138 bb_error_msg_and_die("pax is not tar");
139 break;
140 case '7':
141 /* Reserved for high performance files, treat as normal file */
142 case 0:
143 case '0':
144#ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY
145 if (last_char_is(file_header->name, '/')) {
146 file_header->mode |= S_IFDIR;
147 } else
148#endif
149 file_header->mode |= S_IFREG;
150 break;
151 case '2':
152 file_header->mode |= S_IFLNK;
153 break;
154 case '3':
155 file_header->mode |= S_IFCHR;
156 break;
157 case '4':
158 file_header->mode |= S_IFBLK;
159 break;
160 case '5':
161 file_header->mode |= S_IFDIR;
162 break;
163 case '6':
164 file_header->mode |= S_IFIFO;
165 break;
166#ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS
167 case 'L': {
168 longname = xmalloc(file_header->size + 1);
169 archive_xread_all(archive_handle, longname, file_header->size);
170 longname[file_header->size] = '\0';
171 archive_handle->offset += file_header->size;
172
173 return(get_header_tar(archive_handle));
174 }
175 case 'K': {
176 linkname = xmalloc(file_header->size + 1);
177 archive_xread_all(archive_handle, linkname, file_header->size);
178 linkname[file_header->size] = '\0';
179 archive_handle->offset += file_header->size;
180
181 file_header->name = linkname;
182 return(get_header_tar(archive_handle));
183 }
184 case 'D': /* GNU dump dir */
185 case 'M': /* Continuation of multi volume archive*/
186 case 'N': /* Old GNU for names > 100 characters */
187 case 'S': /* Sparse file */
188 case 'V': /* Volume header */
189 bb_error_msg("Ignoring GNU extension type %c", tar.formated.typeflag);
190#endif
191 default:
192 bb_error_msg("Unknown typeflag: 0x%x", tar.formated.typeflag);
193 }
194 { /* Strip trailing '/' in directories */
195 /* Must be done after mode is set as '/' is used to check if its a directory */
196 char *tmp = last_char_is(file_header->name, '/');
197 if (tmp) {
198 *tmp = '\0';
199 }
200 }
201
202 if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
203 archive_handle->action_header(archive_handle->file_header);
204 archive_handle->flags |= ARCHIVE_EXTRACT_QUIET;
205 archive_handle->action_data(archive_handle);
206 archive_handle->passed = llist_add_to(archive_handle->passed, file_header->name);
207 } else {
208 data_skip(archive_handle);
209 }
210 archive_handle->offset += file_header->size;
211
212 free(file_header->link_name);
213
214 return(EXIT_SUCCESS);
215}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Library General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <sys/types.h>
18#include <sys/wait.h>
19#include <signal.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
24#include "libbb.h"
25#include "unarchive.h"
26
27extern char get_header_tar_bz2(archive_handle_t *archive_handle)
28{
29 /* Cant lseek over pipe's */
30 archive_handle->seek = seek_by_char;
31
32 archive_handle->src_fd = open_transformer(archive_handle->src_fd, uncompressStream);
33 archive_handle->offset = 0;
34 while (get_header_tar(archive_handle) == EXIT_SUCCESS);
35
36 /* Can only do one file at a time */
37 return(EXIT_FAILURE);
38}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Library General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <stdlib.h>
18
19#include "libbb.h"
20#include "unarchive.h"
21
22extern char get_header_tar_gz(archive_handle_t *archive_handle)
23{
24 unsigned char magic[2];
25
26 /* Cant lseek over pipe's */
27 archive_handle->seek = seek_by_char;
28
29 archive_xread_all(archive_handle, &magic, 2);
30 if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
31 bb_error_msg_and_die("Invalid gzip magic");
32 }
33
34 check_header_gzip(archive_handle->src_fd);
35
36 archive_handle->src_fd = open_transformer(archive_handle->src_fd, inflate_gunzip);
37 archive_handle->offset = 0;
38 while (get_header_tar(archive_handle) == EXIT_SUCCESS);
39
40 /* Can only do one file at a time */
41 return(EXIT_FAILURE);
42}
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 @@
1#include <stdio.h>
2#include "unarchive.h"
3
4extern void header_list(const file_header_t *file_header)
5{
6 puts(file_header->name);
7}
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 @@
1#include <stdio.h>
2#include "unarchive.h"
3
4extern void header_skip(const file_header_t *file_header)
5{
6 return;
7}
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 @@
1#include <stdio.h>
2#include <string.h>
3#include <time.h>
4#include "libbb.h"
5#include "unarchive.h"
6
7extern void header_verbose_list(const file_header_t *file_header)
8{
9 struct tm *mtime = localtime(&file_header->mtime);
10
11 printf("%s %d/%d%10u %4u-%02u-%02u %02u:%02u:%02u %s",
12 bb_mode_string(file_header->mode),
13 file_header->uid,
14 file_header->gid,
15 (unsigned int) file_header->size,
16 1900 + mtime->tm_year,
17 1 + mtime->tm_mon,
18 mtime->tm_mday,
19 mtime->tm_hour,
20 mtime->tm_min,
21 mtime->tm_sec,
22 file_header->name);
23
24 if (file_header->link_name) {
25 printf(" -> %s", file_header->link_name);
26 }
27 /* putchar isnt used anywhere else i dont think */
28 puts("");
29}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <unistd.h>
18#include <string.h>
19#include "libbb.h"
20#include "unarchive.h"
21
22archive_handle_t *init_handle(void)
23{
24 archive_handle_t *archive_handle;
25
26 /* Initialise default values */
27 archive_handle = xmalloc(sizeof(archive_handle_t));
28 memset(archive_handle, 0, sizeof(archive_handle_t));
29 archive_handle->file_header = xmalloc(sizeof(file_header_t));
30 archive_handle->action_header = header_skip;
31 archive_handle->action_data = data_skip;
32 archive_handle->filter = filter_accept_all;
33 archive_handle->seek = seek_by_jump;
34
35 return(archive_handle);
36}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Library General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <stdlib.h>
18#include <unistd.h>
19
20#include "libbb.h"
21
22/* transformer(), more than meets the eye */
23extern int open_transformer(int src_fd, int (*transformer)(int src_fd, int dst_fd))
24{
25 int fd_pipe[2];
26 int pid;
27
28 if (pipe(fd_pipe) != 0) {
29 bb_perror_msg_and_die("Can't create pipe");
30 }
31
32 pid = fork();
33 if (pid == -1) {
34 bb_perror_msg_and_die("Fork failed");
35 }
36
37 if (pid == 0) {
38 /* child process */
39 close(fd_pipe[0]); /* We don't wan't to read from the parent */
40 transformer(src_fd, fd_pipe[1]);
41 close(fd_pipe[1]); /* Send EOF */
42 close(src_fd);
43 exit(0);
44 /* notreached */
45 }
46
47 /* parent process */
48 close(fd_pipe[1]); /* Don't want to write to the child */
49
50 return(fd_pipe[0]);
51}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Library General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <stdlib.h>
18
19#include "unarchive.h"
20#include "busybox.h"
21
22/* If we are reading through a pipe(), or from stdin then we cant lseek,
23 * we must read and discard the data to skip over it.
24 *
25 * TODO: rename to seek_by_read
26 */
27extern void seek_by_char(const archive_handle_t *archive_handle, const unsigned int jump_size)
28{
29 if (jump_size) {
30 bb_copyfd_size(archive_handle->src_fd, -1, jump_size);
31 }
32}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Library General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <sys/types.h>
18#include <errno.h>
19#include <unistd.h>
20#include <stdlib.h>
21
22#include "libbb.h"
23#include "unarchive.h"
24
25extern void seek_by_jump(const archive_handle_t *archive_handle, const unsigned int amount)
26{
27 if (lseek(archive_handle->src_fd, (off_t) amount, SEEK_CUR) == (off_t) -1) {
28#ifdef CONFIG_FEATURE_UNARCHIVE_TAPE
29 if (errno == ESPIPE) {
30 seek_by_char(archive_handle, amount);
31 } else
32#endif
33 bb_perror_msg_and_die("Seek failure");
34 }
35}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Library General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16#include <fcntl.h>
17#include <stdlib.h>
18#include <string.h>
19#include <getopt.h>
20#include "unarchive.h"
21#include "busybox.h"
22
23extern void unpack_ar_archive(archive_handle_t *ar_archive)
24{
25 char magic[7];
26
27 archive_xread_all(ar_archive, magic, 7);
28 if (strncmp(magic, "!<arch>", 7) != 0) {
29 bb_error_msg_and_die("Invalid ar magic");
30 }
31 ar_archive->offset += 7;
32
33 while (get_header_ar(ar_archive) == EXIT_SUCCESS);
34}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini rpm applet for busybox
4 *
5 * Copyright (C) 2001,2002 by Laurence Anderson
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <unistd.h>
24#include <signal.h>
25#include <stdlib.h>
26#include <fcntl.h>
27#include <netinet/in.h> /* For ntohl & htonl function */
28#include <string.h> /* For strncmp */
29#include <sys/mman.h> /* For mmap */
30#include <time.h> /* For ctime */
31
32#include "busybox.h"
33#include "unarchive.h"
34
35#define RPM_HEADER_MAGIC "\216\255\350"
36#define RPM_CHAR_TYPE 1
37#define RPM_INT8_TYPE 2
38#define RPM_INT16_TYPE 3
39#define RPM_INT32_TYPE 4
40/* #define RPM_INT64_TYPE 5 ---- These aren't supported (yet) */
41#define RPM_STRING_TYPE 6
42#define RPM_BIN_TYPE 7
43#define RPM_STRING_ARRAY_TYPE 8
44#define RPM_I18NSTRING_TYPE 9
45
46#define RPMTAG_NAME 1000
47#define RPMTAG_VERSION 1001
48#define RPMTAG_RELEASE 1002
49#define RPMTAG_SUMMARY 1004
50#define RPMTAG_DESCRIPTION 1005
51#define RPMTAG_BUILDTIME 1006
52#define RPMTAG_BUILDHOST 1007
53#define RPMTAG_SIZE 1009
54#define RPMTAG_VENDOR 1011
55#define RPMTAG_LICENSE 1014
56#define RPMTAG_PACKAGER 1015
57#define RPMTAG_GROUP 1016
58#define RPMTAG_URL 1020
59#define RPMTAG_PREIN 1023
60#define RPMTAG_POSTIN 1024
61#define RPMTAG_FILEFLAGS 1037
62#define RPMTAG_FILEUSERNAME 1039
63#define RPMTAG_FILEGROUPNAME 1040
64#define RPMTAG_SOURCERPM 1044
65#define RPMTAG_PREINPROG 1085
66#define RPMTAG_POSTINPROG 1086
67#define RPMTAG_PREFIXS 1098
68#define RPMTAG_DIRINDEXES 1116
69#define RPMTAG_BASENAMES 1117
70#define RPMTAG_DIRNAMES 1118
71#define RPMFILE_CONFIG (1 << 0)
72#define RPMFILE_DOC (1 << 1)
73
74enum rpm_functions_e {
75 rpm_query = 1,
76 rpm_install = 2,
77 rpm_query_info = 4,
78 rpm_query_package = 8,
79 rpm_query_list = 16,
80 rpm_query_list_doc = 32,
81 rpm_query_list_config = 64
82};
83
84typedef struct {
85 uint32_t tag; /* 4 byte tag */
86 uint32_t type; /* 4 byte type */
87 uint32_t offset; /* 4 byte offset */
88 uint32_t count; /* 4 byte count */
89} rpm_index;
90
91static void *map;
92static rpm_index **mytags;
93static int tagcount;
94
95void extract_cpio_gz(int fd);
96rpm_index **rpm_gettags(int fd, int *num_tags);
97int bsearch_rpmtag(const void *key, const void *item);
98char *rpm_getstring(int tag, int itemindex);
99int rpm_getint(int tag, int itemindex);
100int rpm_getcount(int tag);
101void exec_script(int progtag, int datatag, char *prefix);
102void fileaction_dobackup(char *filename, int fileref);
103void fileaction_setowngrp(char *filename, int fileref);
104void fileaction_list(char *filename, int itemno);
105void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref));
106
107int rpm_main(int argc, char **argv)
108{
109 int opt = 0, func = 0, rpm_fd, offset;
110
111 while ((opt = getopt(argc, argv, "iqpldc")) != -1) {
112 switch (opt) {
113 case 'i': // First arg: Install mode, with q: Information
114 if (!func) func |= rpm_install;
115 else func |= rpm_query_info;
116 break;
117 case 'q': // First arg: Query mode
118 if (!func) func |= rpm_query;
119 else bb_show_usage();
120 break;
121 case 'p': // Query a package
122 func |= rpm_query_package;
123 break;
124 case 'l': // List files in a package
125 func |= rpm_query_list;
126 break;
127 case 'd': // List doc files in a package (implies list)
128 func |= rpm_query_list;
129 func |= rpm_query_list_doc;
130 break;
131 case 'c': // List config files in a package (implies list)
132 func |= rpm_query_list;
133 func |= rpm_query_list_config;
134 break;
135 default:
136 bb_show_usage();
137 }
138 }
139
140 if (optind == argc) bb_show_usage();
141 while (optind < argc) {
142 rpm_fd = bb_xopen(argv[optind], O_RDONLY);
143 mytags = rpm_gettags(rpm_fd, (int *) &tagcount);
144 offset = lseek(rpm_fd, 0, SEEK_CUR);
145 if (!mytags) { printf("Error reading rpm header\n"); exit(-1); }
146 map = mmap(0, offset > getpagesize() ? (offset + offset % getpagesize()) : getpagesize(), PROT_READ, MAP_SHARED, rpm_fd, 0); // Mimimum is one page
147 if (func & rpm_install) {
148 loop_through_files(RPMTAG_BASENAMES, fileaction_dobackup); /* Backup any config files */
149 extract_cpio_gz(rpm_fd); // Extact the archive
150 loop_through_files(RPMTAG_BASENAMES, fileaction_setowngrp); /* Set the correct file uid/gid's */
151 } else if (func & rpm_query && func & rpm_query_package) {
152 if (!((func & rpm_query_info) || (func & rpm_query_list))) { // If just a straight query, just give package name
153 printf("%s-%s-%s\n", rpm_getstring(RPMTAG_NAME, 0), rpm_getstring(RPMTAG_VERSION, 0), rpm_getstring(RPMTAG_RELEASE, 0));
154 }
155 if (func & rpm_query_info) {
156 /* Do the nice printout */
157 time_t bdate_time;
158 struct tm *bdate;
159 char bdatestring[50];
160 printf("Name : %-29sRelocations: %s\n", rpm_getstring(RPMTAG_NAME, 0), rpm_getstring(RPMTAG_PREFIXS, 0) ? rpm_getstring(RPMTAG_PREFIXS, 0) : "(not relocateable)");
161 printf("Version : %-34sVendor: %s\n", rpm_getstring(RPMTAG_VERSION, 0), rpm_getstring(RPMTAG_VENDOR, 0) ? rpm_getstring(RPMTAG_VENDOR, 0) : "(none)");
162 bdate_time = rpm_getint(RPMTAG_BUILDTIME, 0);
163 bdate = localtime((time_t *) &bdate_time);
164 strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate);
165 printf("Release : %-30sBuild Date: %s\n", rpm_getstring(RPMTAG_RELEASE, 0), bdatestring);
166 printf("Install date: %-30sBuild Host: %s\n", "(not installed)", rpm_getstring(RPMTAG_BUILDHOST, 0));
167 printf("Group : %-30sSource RPM: %s\n", rpm_getstring(RPMTAG_GROUP, 0), rpm_getstring(RPMTAG_SOURCERPM, 0));
168 printf("Size : %-33dLicense: %s\n", rpm_getint(RPMTAG_SIZE, 0), rpm_getstring(RPMTAG_LICENSE, 0));
169 printf("URL : %s\n", rpm_getstring(RPMTAG_URL, 0));
170 printf("Summary : %s\n", rpm_getstring(RPMTAG_SUMMARY, 0));
171 printf("Description :\n%s\n", rpm_getstring(RPMTAG_DESCRIPTION, 0));
172 }
173 if (func & rpm_query_list) {
174 int count, it, flags;
175 count = rpm_getcount(RPMTAG_BASENAMES);
176 for (it = 0; it < count; it++) {
177 flags = rpm_getint(RPMTAG_FILEFLAGS, it);
178 switch ((func & rpm_query_list_doc) + (func & rpm_query_list_config))
179 {
180 case rpm_query_list_doc: if (!(flags & RPMFILE_DOC)) continue; break;
181 case rpm_query_list_config: if (!(flags & RPMFILE_CONFIG)) continue; break;
182 case rpm_query_list_doc + rpm_query_list_config: if (!((flags & RPMFILE_CONFIG) || (flags & RPMFILE_DOC))) continue; break;
183 }
184 printf("%s%s\n", rpm_getstring(RPMTAG_DIRNAMES, rpm_getint(RPMTAG_DIRINDEXES, it)), rpm_getstring(RPMTAG_BASENAMES, it));
185 }
186 }
187 }
188 optind++;
189 free (mytags);
190 }
191 return 0;
192}
193
194void extract_cpio_gz(int fd) {
195 archive_handle_t *archive_handle;
196 unsigned char magic[2];
197
198 /* Initialise */
199 archive_handle = init_handle();
200 archive_handle->seek = seek_by_char;
201 //archive_handle->action_header = header_list;
202 archive_handle->action_data = data_extract_all;
203 archive_handle->flags |= ARCHIVE_PRESERVE_DATE;
204 archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS;
205 archive_handle->src_fd = fd;
206 archive_handle->offset = 0;
207
208 bb_xread_all(archive_handle->src_fd, &magic, 2);
209 if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
210 bb_error_msg_and_die("Invalid gzip magic");
211 }
212 check_header_gzip(archive_handle->src_fd);
213 chdir("/"); // Install RPM's to root
214
215 archive_handle->src_fd = open_transformer(archive_handle->src_fd, inflate_gunzip);
216 archive_handle->offset = 0;
217 while (get_header_cpio(archive_handle) == EXIT_SUCCESS);
218}
219
220
221rpm_index **rpm_gettags(int fd, int *num_tags)
222{
223 rpm_index **tags = calloc(200, sizeof(struct rpmtag *)); /* We should never need mode than 200, and realloc later */
224 int pass, tagindex = 0;
225 lseek(fd, 96, SEEK_CUR); /* Seek past the unused lead */
226
227 for (pass = 0; pass < 2; pass++) { /* 1st pass is the signature headers, 2nd is the main stuff */
228 struct {
229 char magic[3]; /* 3 byte magic: 0x8e 0xad 0xe8 */
230 uint8_t version; /* 1 byte version number */
231 uint32_t reserved; /* 4 bytes reserved */
232 uint32_t entries; /* Number of entries in header (4 bytes) */
233 uint32_t size; /* Size of store (4 bytes) */
234 } header;
235 rpm_index *tmpindex;
236 int storepos;
237
238 read(fd, &header, sizeof(header));
239 if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) return NULL; /* Invalid magic */
240 if (header.version != 1) return NULL; /* This program only supports v1 headers */
241 header.size = ntohl(header.size);
242 header.entries = ntohl(header.entries);
243 storepos = lseek(fd,0,SEEK_CUR) + header.entries * 16;
244
245 while (header.entries--) {
246 tmpindex = tags[tagindex++] = malloc(sizeof(rpm_index));
247 read(fd, tmpindex, sizeof(rpm_index));
248 tmpindex->tag = ntohl(tmpindex->tag); tmpindex->type = ntohl(tmpindex->type); tmpindex->count = ntohl(tmpindex->count);
249 tmpindex->offset = storepos + ntohl(tmpindex->offset);
250 if (pass==0) tmpindex->tag -= 743;
251 }
252 lseek(fd, header.size, SEEK_CUR); /* Seek past store */
253 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 */
254 }
255 tags = realloc(tags, tagindex * sizeof(struct rpmtag *)); /* realloc tags to save space */
256 *num_tags = tagindex;
257 return tags; /* All done, leave the file at the start of the gzipped cpio archive */
258}
259
260int bsearch_rpmtag(const void *key, const void *item)
261{
262 rpm_index **tmp = (rpm_index **) item;
263 return ((int) key - tmp[0]->tag);
264}
265
266int rpm_getcount(int tag)
267{
268 rpm_index **found;
269 found = bsearch((void *) tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
270 if (!found) return 0;
271 else return found[0]->count;
272}
273
274char *rpm_getstring(int tag, int itemindex)
275{
276 rpm_index **found;
277 found = bsearch((void *) tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
278 if (!found || itemindex >= found[0]->count) return NULL;
279 if (found[0]->type == RPM_STRING_TYPE || found[0]->type == RPM_I18NSTRING_TYPE || found[0]->type == RPM_STRING_ARRAY_TYPE) {
280 int n;
281 char *tmpstr = (char *) (map + found[0]->offset);
282 for (n=0; n < itemindex; n++) tmpstr = tmpstr + strlen(tmpstr) + 1;
283 return tmpstr;
284 } else return NULL;
285}
286
287int rpm_getint(int tag, int itemindex)
288{
289 rpm_index **found;
290 int n, *tmpint;
291 found = bsearch((void *) tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
292 if (!found || itemindex >= found[0]->count) return -1;
293 tmpint = (int *) (map + found[0]->offset);
294 if (found[0]->type == RPM_INT32_TYPE) {
295 for (n=0; n<itemindex; n++) tmpint = (int *) ((void *) tmpint + 4);
296 return ntohl(*tmpint);
297 } else if (found[0]->type == RPM_INT16_TYPE) {
298 for (n=0; n<itemindex; n++) tmpint = (int *) ((void *) tmpint + 2);
299 return ntohs(*tmpint);
300 } else if (found[0]->type == RPM_INT8_TYPE) {
301 for (n=0; n<itemindex; n++) tmpint = (int *) ((void *) tmpint + 1);
302 return ntohs(*tmpint);
303 } else return -1;
304}
305
306void fileaction_dobackup(char *filename, int fileref)
307{
308 struct stat oldfile;
309 int stat_res;
310 char *newname;
311 if (rpm_getint(RPMTAG_FILEFLAGS, fileref) & RPMFILE_CONFIG) { /* Only need to backup config files */
312 stat_res = lstat (filename, &oldfile);
313 if (stat_res == 0 && S_ISREG(oldfile.st_mode)) { /* File already exists - really should check MD5's etc to see if different */
314 newname = bb_xstrdup(filename);
315 newname = strcat(newname, ".rpmorig");
316 copy_file(filename, newname, FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS);
317 remove_file(filename, FILEUTILS_RECUR | FILEUTILS_FORCE);
318 free(newname);
319 }
320 }
321}
322
323void fileaction_setowngrp(char *filename, int fileref)
324{
325 int uid, gid;
326 uid = my_getpwnam(rpm_getstring(RPMTAG_FILEUSERNAME, fileref));
327 gid = my_getgrnam(rpm_getstring(RPMTAG_FILEGROUPNAME, fileref));
328 chown (filename, uid, gid);
329}
330
331void fileaction_list(char *filename, int fileref)
332{
333 printf("%s\n", filename);
334}
335
336void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref))
337{
338 int count = 0;
339 char *filename, *tmp_dirname, *tmp_basename;
340 while (rpm_getstring(filetag, count)) {
341 tmp_dirname = rpm_getstring(RPMTAG_DIRNAMES, rpm_getint(RPMTAG_DIRINDEXES, count)); /* 1st put on the directory */
342 tmp_basename = rpm_getstring(RPMTAG_BASENAMES, count);
343 filename = xmalloc(strlen(tmp_basename) + strlen(tmp_dirname) + 1);
344 strcpy(filename, tmp_dirname); /* First the directory name */
345 strcat(filename, tmp_basename); /* then the filename */
346 fileaction(filename, count++);
347 free(filename);
348 }
349}
diff --git a/busybox/archival/rpm2cpio.c b/busybox/archival/rpm2cpio.c
new file mode 100644
index 000000000..5314e5300
--- /dev/null
+++ b/busybox/archival/rpm2cpio.c
@@ -0,0 +1,106 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini rpm2cpio implementation for busybox
4 *
5 * Copyright (C) 2001 by Laurence Anderson
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21#include <sys/types.h>
22#include <netinet/in.h> /* For ntohl & htonl function */
23#include <fcntl.h>
24#include <unistd.h>
25#include <string.h>
26#include "busybox.h"
27#include "unarchive.h"
28
29#define RPM_MAGIC "\355\253\356\333"
30#define RPM_HEADER_MAGIC "\216\255\350"
31
32struct rpm_lead {
33 unsigned char magic[4];
34 uint8_t major, minor;
35 uint16_t type;
36 uint16_t archnum;
37 char name[66];
38 uint16_t osnum;
39 uint16_t signature_type;
40 char reserved[16];
41};
42
43struct rpm_header {
44 char magic[3]; /* 3 byte magic: 0x8e 0xad 0xe8 */
45 uint8_t version; /* 1 byte version number */
46 uint32_t reserved; /* 4 bytes reserved */
47 uint32_t entries; /* Number of entries in header (4 bytes) */
48 uint32_t size; /* Size of store (4 bytes) */
49};
50
51void skip_header(int rpm_fd)
52{
53 struct rpm_header header;
54
55 bb_xread_all(rpm_fd, &header, sizeof(struct rpm_header));
56 if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) {
57 bb_error_msg_and_die("Invalid RPM header magic"); /* Invalid magic */
58 }
59 if (header.version != 1) {
60 bb_error_msg_and_die("Unsupported RPM header version"); /* This program only supports v1 headers */
61 }
62 header.entries = ntohl(header.entries);
63 header.size = ntohl(header.size);
64 lseek (rpm_fd, 16 * header.entries, SEEK_CUR); /* Seek past index entries */
65 lseek (rpm_fd, header.size, SEEK_CUR); /* Seek past store */
66}
67
68/* No getopt required */
69extern int rpm2cpio_main(int argc, char **argv)
70{
71 struct rpm_lead lead;
72 int rpm_fd;
73 unsigned char magic[2];
74
75 if (argc == 1) {
76 rpm_fd = STDIN_FILENO;
77 } else {
78 rpm_fd = bb_xopen(argv[1], O_RDONLY);
79 }
80
81 bb_xread_all(rpm_fd, &lead, sizeof(struct rpm_lead));
82 if (strncmp((char *) &lead.magic, RPM_MAGIC, 4) != 0) {
83 bb_error_msg_and_die("Invalid RPM magic"); /* Just check the magic, the rest is irrelevant */
84 }
85
86 /* Skip the signature header */
87 skip_header(rpm_fd);
88 lseek(rpm_fd, (8 - (lseek(rpm_fd, 0, SEEK_CUR) % 8)) % 8, SEEK_CUR);
89
90 /* Skip the main header */
91 skip_header(rpm_fd);
92
93 bb_xread_all(rpm_fd, &magic, 2);
94 if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
95 bb_error_msg_and_die("Invalid gzip magic");
96 }
97
98 check_header_gzip(rpm_fd);
99 if (inflate_gunzip(rpm_fd, STDOUT_FILENO) != 0) {
100 bb_error_msg("Error inflating");
101 }
102
103 close(rpm_fd);
104
105 return 0;
106}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini tar implementation for busybox
4 *
5 * Modified to use common extraction code used by ar, cpio, dpkg-deb, dpkg
6 * Glenn McGrath <bug1@iinet.net.au>
7 *
8 * Note, that as of BusyBox-0.43, tar has been completely rewritten from the
9 * ground up. It still has remnants of the old code lying about, but it is
10 * very different now (i.e., cleaner, less global variables, etc.)
11 *
12 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
13 *
14 * Based in part in the tar implementation in sash
15 * Copyright (c) 1999 by David I. Bell
16 * Permission is granted to use, distribute, or modify this source,
17 * provided that this copyright notice remains intact.
18 * Permission to distribute sash derived code under the GPL has been granted.
19 *
20 * Based in part on the tar implementation from busybox-0.28
21 * Copyright (C) 1995 Bruce Perens
22 * This is free software under the GNU General Public License.
23 *
24 * This program is free software; you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published by
26 * the Free Software Foundation; either version 2 of the License, or
27 * (at your option) any later version.
28 *
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
32 * General Public License for more details.
33 *
34 * You should have received a copy of the GNU General Public License
35 * along with this program; if not, write to the Free Software
36 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
37 *
38 */
39
40#include <fcntl.h>
41#include <getopt.h>
42#include <search.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <unistd.h>
46#include <fnmatch.h>
47#include <string.h>
48#include <errno.h>
49#include <signal.h>
50#include <sys/wait.h>
51#include <sys/socket.h>
52#include <sys/sysmacros.h> /* major() and minor() */
53#include "unarchive.h"
54#include "busybox.h"
55
56#ifdef CONFIG_FEATURE_TAR_CREATE
57
58/* Tar file constants */
59# define TAR_MAGIC "ustar" /* ustar and a null */
60# define TAR_VERSION " " /* Be compatable with GNU tar format */
61
62static const int TAR_BLOCK_SIZE = 512;
63static const int TAR_MAGIC_LEN = 6;
64static const int TAR_VERSION_LEN = 2;
65
66/* POSIX tar Header Block, from POSIX 1003.1-1990 */
67enum { NAME_SIZE = 100 }; /* because gcc won't let me use 'static const int' */
68struct TarHeader { /* byte offset */
69 char name[NAME_SIZE]; /* 0-99 */
70 char mode[8]; /* 100-107 */
71 char uid[8]; /* 108-115 */
72 char gid[8]; /* 116-123 */
73 char size[12]; /* 124-135 */
74 char mtime[12]; /* 136-147 */
75 char chksum[8]; /* 148-155 */
76 char typeflag; /* 156-156 */
77 char linkname[NAME_SIZE]; /* 157-256 */
78 char magic[6]; /* 257-262 */
79 char version[2]; /* 263-264 */
80 char uname[32]; /* 265-296 */
81 char gname[32]; /* 297-328 */
82 char devmajor[8]; /* 329-336 */
83 char devminor[8]; /* 337-344 */
84 char prefix[155]; /* 345-499 */
85 char padding[12]; /* 500-512 (pad to exactly the TAR_BLOCK_SIZE) */
86};
87typedef struct TarHeader TarHeader;
88
89/*
90** writeTarFile(), writeFileToTarball(), and writeTarHeader() are
91** the only functions that deal with the HardLinkInfo structure.
92** Even these functions use the xxxHardLinkInfo() functions.
93*/
94typedef struct HardLinkInfo HardLinkInfo;
95struct HardLinkInfo {
96 HardLinkInfo *next; /* Next entry in list */
97 dev_t dev; /* Device number */
98 ino_t ino; /* Inode number */
99 short linkCount; /* (Hard) Link Count */
100 char name[1]; /* Start of filename (must be last) */
101};
102
103/* Some info to be carried along when creating a new tarball */
104struct TarBallInfo {
105 char *fileName; /* File name of the tarball */
106 int tarFd; /* Open-for-write file descriptor
107 for the tarball */
108 struct stat statBuf; /* Stat info for the tarball, letting
109 us know the inode and device that the
110 tarball lives, so we can avoid trying
111 to include the tarball into itself */
112 int verboseFlag; /* Whether to print extra stuff or not */
113 const llist_t *excludeList; /* List of files to not include */
114 HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */
115 HardLinkInfo *hlInfo; /* Hard Link Info for the current file */
116};
117typedef struct TarBallInfo TarBallInfo;
118
119/* A nice enum with all the possible tar file content types */
120enum TarFileType {
121 REGTYPE = '0', /* regular file */
122 REGTYPE0 = '\0', /* regular file (ancient bug compat) */
123 LNKTYPE = '1', /* hard link */
124 SYMTYPE = '2', /* symbolic link */
125 CHRTYPE = '3', /* character special */
126 BLKTYPE = '4', /* block special */
127 DIRTYPE = '5', /* directory */
128 FIFOTYPE = '6', /* FIFO special */
129 CONTTYPE = '7', /* reserved */
130 GNULONGLINK = 'K', /* GNU long (>100 chars) link name */
131 GNULONGNAME = 'L', /* GNU long (>100 chars) file name */
132};
133typedef enum TarFileType TarFileType;
134
135/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
136static inline void addHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr,
137 struct stat *statbuf,
138 const char *name)
139{
140 /* Note: hlInfoHeadPtr can never be NULL! */
141 HardLinkInfo *hlInfo;
142
143 hlInfo = (HardLinkInfo *) xmalloc(sizeof(HardLinkInfo) + strlen(name));
144 hlInfo->next = *hlInfoHeadPtr;
145 *hlInfoHeadPtr = hlInfo;
146 hlInfo->dev = statbuf->st_dev;
147 hlInfo->ino = statbuf->st_ino;
148 hlInfo->linkCount = statbuf->st_nlink;
149 strcpy(hlInfo->name, name);
150}
151
152static void freeHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr)
153{
154 HardLinkInfo *hlInfo = NULL;
155 HardLinkInfo *hlInfoNext = NULL;
156
157 if (hlInfoHeadPtr) {
158 hlInfo = *hlInfoHeadPtr;
159 while (hlInfo) {
160 hlInfoNext = hlInfo->next;
161 free(hlInfo);
162 hlInfo = hlInfoNext;
163 }
164 *hlInfoHeadPtr = NULL;
165 }
166 return;
167}
168
169/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
170static inline HardLinkInfo *findHardLinkInfo(HardLinkInfo * hlInfo, struct stat *statbuf)
171{
172 while (hlInfo) {
173 if ((statbuf->st_ino == hlInfo->ino) && (statbuf->st_dev == hlInfo->dev))
174 break;
175 hlInfo = hlInfo->next;
176 }
177 return (hlInfo);
178}
179
180/* Put an octal string into the specified buffer.
181 * The number is zero and space padded and possibly null padded.
182 * Returns TRUE if successful. */
183static int putOctal(char *cp, int len, long value)
184{
185 int tempLength;
186 char tempBuffer[32];
187 char *tempString = tempBuffer;
188
189 /* Create a string of the specified length with an initial space,
190 * leading zeroes and the octal number, and a trailing null. */
191 sprintf(tempString, "%0*lo", len - 1, value);
192
193 /* If the string is too large, suppress the leading space. */
194 tempLength = strlen(tempString) + 1;
195 if (tempLength > len) {
196 tempLength--;
197 tempString++;
198 }
199
200 /* If the string is still too large, suppress the trailing null. */
201 if (tempLength > len)
202 tempLength--;
203
204 /* If the string is still too large, fail. */
205 if (tempLength > len)
206 return FALSE;
207
208 /* Copy the string to the field. */
209 memcpy(cp, tempString, len);
210
211 return TRUE;
212}
213
214/* Write out a tar header for the specified file/directory/whatever */
215static inline int writeTarHeader(struct TarBallInfo *tbInfo,
216 const char *header_name,
217 const char *real_name, struct stat *statbuf)
218{
219 long chksum = 0;
220 struct TarHeader header;
221 const unsigned char *cp = (const unsigned char *) &header;
222 ssize_t size = sizeof(struct TarHeader);
223
224 memset(&header, 0, size);
225
226 strncpy(header.name, header_name, sizeof(header.name));
227
228 putOctal(header.mode, sizeof(header.mode), statbuf->st_mode);
229 putOctal(header.uid, sizeof(header.uid), statbuf->st_uid);
230 putOctal(header.gid, sizeof(header.gid), statbuf->st_gid);
231 putOctal(header.size, sizeof(header.size), 0); /* Regular file size is handled later */
232 putOctal(header.mtime, sizeof(header.mtime), statbuf->st_mtime);
233 strncpy(header.magic, TAR_MAGIC TAR_VERSION,
234 TAR_MAGIC_LEN + TAR_VERSION_LEN);
235
236 /* Enter the user and group names (default to root if it fails) */
237 if (my_getpwuid(header.uname, statbuf->st_uid, sizeof(header.uname)) == NULL)
238 strcpy(header.uname, "root");
239 if (my_getgrgid(header.gname, statbuf->st_gid, sizeof(header.gname)) == NULL)
240 strcpy(header.gname, "root");
241
242 if (tbInfo->hlInfo) {
243 /* This is a hard link */
244 header.typeflag = LNKTYPE;
245 strncpy(header.linkname, tbInfo->hlInfo->name,
246 sizeof(header.linkname));
247 } else if (S_ISLNK(statbuf->st_mode)) {
248 char *lpath = xreadlink(real_name);
249
250 if (!lpath) /* Already printed err msg inside xreadlink() */
251 return (FALSE);
252 header.typeflag = SYMTYPE;
253 strncpy(header.linkname, lpath, sizeof(header.linkname));
254 free(lpath);
255 } else if (S_ISDIR(statbuf->st_mode)) {
256 header.typeflag = DIRTYPE;
257 strncat(header.name, "/", sizeof(header.name));
258 } else if (S_ISCHR(statbuf->st_mode)) {
259 header.typeflag = CHRTYPE;
260 putOctal(header.devmajor, sizeof(header.devmajor),
261 major(statbuf->st_rdev));
262 putOctal(header.devminor, sizeof(header.devminor),
263 minor(statbuf->st_rdev));
264 } else if (S_ISBLK(statbuf->st_mode)) {
265 header.typeflag = BLKTYPE;
266 putOctal(header.devmajor, sizeof(header.devmajor),
267 major(statbuf->st_rdev));
268 putOctal(header.devminor, sizeof(header.devminor),
269 minor(statbuf->st_rdev));
270 } else if (S_ISFIFO(statbuf->st_mode)) {
271 header.typeflag = FIFOTYPE;
272 } else if (S_ISREG(statbuf->st_mode)) {
273 header.typeflag = REGTYPE;
274 putOctal(header.size, sizeof(header.size), statbuf->st_size);
275 } else {
276 bb_error_msg("%s: Unknown file type", real_name);
277 return (FALSE);
278 }
279
280 /* Calculate and store the checksum (i.e., the sum of all of the bytes of
281 * the header). The checksum field must be filled with blanks for the
282 * calculation. The checksum field is formatted differently from the
283 * other fields: it has [6] digits, a null, then a space -- rather than
284 * digits, followed by a null like the other fields... */
285 memset(header.chksum, ' ', sizeof(header.chksum));
286 cp = (const unsigned char *) &header;
287 while (size-- > 0)
288 chksum += *cp++;
289 putOctal(header.chksum, 7, chksum);
290
291 /* Now write the header out to disk */
292 if ((size =
293 bb_full_write(tbInfo->tarFd, (char *) &header,
294 sizeof(struct TarHeader))) < 0) {
295 bb_error_msg(bb_msg_io_error, real_name);
296 return (FALSE);
297 }
298 /* Pad the header up to the tar block size */
299 for (; size < TAR_BLOCK_SIZE; size++) {
300 write(tbInfo->tarFd, "\0", 1);
301 }
302 /* Now do the verbose thing (or not) */
303
304 if (tbInfo->verboseFlag) {
305 FILE *vbFd = stdout;
306
307 if (tbInfo->tarFd == STDOUT_FILENO) /* If the archive goes to stdout, verbose to stderr */
308 vbFd = stderr;
309
310 fprintf(vbFd, "%s\n", header.name);
311 }
312
313 return (TRUE);
314}
315
316# ifdef CONFIG_FEATURE_TAR_FROM
317static inline int exclude_file(const llist_t *excluded_files, const char *file)
318{
319 while (excluded_files) {
320 if (excluded_files->data[0] == '/') {
321 if (fnmatch(excluded_files->data, file,
322 FNM_PATHNAME | FNM_LEADING_DIR) == 0)
323 return 1;
324 } else {
325 const char *p;
326
327 for (p = file; p[0] != '\0'; p++) {
328 if ((p == file || p[-1] == '/') && p[0] != '/' &&
329 fnmatch(excluded_files->data, p,
330 FNM_PATHNAME | FNM_LEADING_DIR) == 0)
331 return 1;
332 }
333 }
334 excluded_files = excluded_files->link;
335 }
336
337 return 0;
338}
339# endif
340
341static int writeFileToTarball(const char *fileName, struct stat *statbuf,
342 void *userData)
343{
344 struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData;
345 const char *header_name;
346
347 /*
348 ** Check to see if we are dealing with a hard link.
349 ** If so -
350 ** Treat the first occurance of a given dev/inode as a file while
351 ** treating any additional occurances as hard links. This is done
352 ** by adding the file information to the HardLinkInfo linked list.
353 */
354 tbInfo->hlInfo = NULL;
355 if (statbuf->st_nlink > 1) {
356 tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf);
357 if (tbInfo->hlInfo == NULL)
358 addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, fileName);
359 }
360
361 /* It is against the rules to archive a socket */
362 if (S_ISSOCK(statbuf->st_mode)) {
363 bb_error_msg("%s: socket ignored", fileName);
364 return (TRUE);
365 }
366
367 /* It is a bad idea to store the archive we are in the process of creating,
368 * so check the device and inode to be sure that this particular file isn't
369 * the new tarball */
370 if (tbInfo->statBuf.st_dev == statbuf->st_dev &&
371 tbInfo->statBuf.st_ino == statbuf->st_ino) {
372 bb_error_msg("%s: file is the archive; skipping", fileName);
373 return (TRUE);
374 }
375
376 header_name = fileName;
377 while (header_name[0] == '/') {
378 static int alreadyWarned = FALSE;
379
380 if (alreadyWarned == FALSE) {
381 bb_error_msg("Removing leading '/' from member names");
382 alreadyWarned = TRUE;
383 }
384 header_name++;
385 }
386
387 if (strlen(fileName) >= NAME_SIZE) {
388 bb_error_msg(bb_msg_name_longer_than_foo, NAME_SIZE);
389 return (TRUE);
390 }
391
392 if (header_name[0] == '\0')
393 return TRUE;
394
395# ifdef CONFIG_FEATURE_TAR_FROM
396 if (exclude_file(tbInfo->excludeList, header_name)) {
397 return SKIP;
398 }
399# endif /* CONFIG_FEATURE_TAR_FROM */
400
401 if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) {
402 return (FALSE);
403 }
404
405 /* Now, if the file is a regular file, copy it out to the tarball */
406 if ((tbInfo->hlInfo == NULL)
407 && (S_ISREG(statbuf->st_mode))) {
408 int inputFileFd;
409 ssize_t readSize = 0;
410
411 /* open the file we want to archive, and make sure all is well */
412 if ((inputFileFd = open(fileName, O_RDONLY)) < 0) {
413 bb_perror_msg("%s: Cannot open", fileName);
414 return (FALSE);
415 }
416
417 /* write the file to the archive */
418 readSize = bb_copyfd_eof(inputFileFd, tbInfo->tarFd);
419
420 /* Pad the file up to the tar block size */
421 for (; (readSize % TAR_BLOCK_SIZE) != 0; readSize++) {
422 write(tbInfo->tarFd, "\0", 1);
423 }
424 close(inputFileFd);
425 }
426
427 return (TRUE);
428}
429
430static inline int writeTarFile(const int tar_fd, const int verboseFlag,
431 const unsigned long dereferenceFlag, const llist_t *include,
432 const llist_t *exclude, const int gzip)
433{
434#ifdef CONFIG_FEATURE_TAR_GZIP
435 int gzipDataPipe[2] = { -1, -1 };
436 int gzipStatusPipe[2] = { -1, -1 };
437 pid_t gzipPid = 0;
438 volatile int vfork_exec_errno = 0;
439#endif
440
441 int errorFlag = FALSE;
442 ssize_t size;
443 struct TarBallInfo tbInfo;
444
445 tbInfo.hlInfoHead = NULL;
446
447 fchmod(tar_fd, 0644);
448 tbInfo.tarFd = tar_fd;
449 tbInfo.verboseFlag = verboseFlag;
450
451 /* Store the stat info for the tarball's file, so
452 * can avoid including the tarball into itself.... */
453 if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0)
454 bb_perror_msg_and_die("Couldnt stat tar file");
455
456#ifdef CONFIG_FEATURE_TAR_GZIP
457 if (gzip) {
458 if (pipe(gzipDataPipe) < 0 || pipe(gzipStatusPipe) < 0) {
459 bb_perror_msg_and_die("Failed to create gzip pipe");
460 }
461
462 signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */
463
464# if __GNUC__
465 /* Avoid vfork clobbering */
466 (void) &include;
467 (void) &errorFlag;
468# endif
469
470 gzipPid = vfork();
471
472 if (gzipPid == 0) {
473 dup2(gzipDataPipe[0], 0);
474 close(gzipDataPipe[1]);
475
476 if (tbInfo.tarFd != 1)
477 dup2(tbInfo.tarFd, 1);
478
479 close(gzipStatusPipe[0]);
480 fcntl(gzipStatusPipe[1], F_SETFD, FD_CLOEXEC); /* close on exec shows sucess */
481
482 execl("/bin/gzip", "gzip", "-f", 0);
483 vfork_exec_errno = errno;
484
485 close(gzipStatusPipe[1]);
486 exit(-1);
487 } else if (gzipPid > 0) {
488 close(gzipDataPipe[0]);
489 close(gzipStatusPipe[1]);
490
491 while (1) {
492 char buf;
493
494 int n = bb_full_read(gzipStatusPipe[0], &buf, 1);
495
496 if (n == 0 && vfork_exec_errno != 0) {
497 errno = vfork_exec_errno;
498 bb_perror_msg_and_die("Could not exec gzip process");
499 } else if ((n < 0) && (errno == EAGAIN || errno == EINTR))
500 continue; /* try it again */
501 break;
502 }
503 close(gzipStatusPipe[0]);
504
505 tbInfo.tarFd = gzipDataPipe[1];
506 } else {
507 bb_perror_msg_and_die("Failed to vfork gzip process");
508 }
509 }
510#endif
511
512 tbInfo.excludeList = exclude;
513
514 /* Read the directory/files and iterate over them one at a time */
515 while (include) {
516 if (!recursive_action(include->data, TRUE, dereferenceFlag, FALSE,
517 writeFileToTarball, writeFileToTarball,
518 (void *) &tbInfo)) {
519 errorFlag = TRUE;
520 }
521 include = include->link;
522 }
523 /* Write two empty blocks to the end of the archive */
524 for (size = 0; size < (2 * TAR_BLOCK_SIZE); size++) {
525 write(tbInfo.tarFd, "\0", 1);
526 }
527
528 /* To be pedantically correct, we would check if the tarball
529 * is smaller than 20 tar blocks, and pad it if it was smaller,
530 * but that isn't necessary for GNU tar interoperability, and
531 * so is considered a waste of space */
532
533 /* Hang up the tools, close up shop, head home */
534 close(tbInfo.tarFd);
535 if (errorFlag)
536 bb_error_msg("Error exit delayed from previous errors");
537
538 freeHardLinkInfo(&tbInfo.hlInfoHead);
539
540#ifdef CONFIG_FEATURE_TAR_GZIP
541 if (gzip && gzipPid) {
542 if (waitpid(gzipPid, NULL, 0) == -1)
543 printf("Couldnt wait ?");
544 }
545#endif
546
547 return !errorFlag;
548}
549#endif /* tar_create */
550
551#ifdef CONFIG_FEATURE_TAR_FROM
552static llist_t *append_file_list_to_list(llist_t *list)
553{
554 FILE *src_stream;
555 llist_t *cur = list;
556 llist_t *tmp;
557 char *line;
558 llist_t *newlist = NULL;
559
560 while(cur) {
561 src_stream = bb_xfopen(cur->data, "r");
562 tmp = cur;
563 cur = cur->link;
564 free(tmp);
565 while((line = bb_get_chomped_line_from_file(src_stream)) != NULL) {
566 newlist = llist_add_to(newlist, line);
567 }
568 fclose(src_stream);
569 }
570 return newlist;
571}
572#endif
573
574#ifdef CONFIG_FEATURE_TAR_COMPRESS
575static char get_header_tar_Z(archive_handle_t *archive_handle)
576{
577 /* Cant lseek over pipe's */
578 archive_handle->seek = seek_by_char;
579
580 /* do the decompression, and cleanup */
581 if ((bb_xread_char(archive_handle->src_fd) != 0x1f) || (bb_xread_char(archive_handle->src_fd) != 0x9d)) {
582 bb_error_msg_and_die("Invalid magic");
583 }
584
585 archive_handle->src_fd = open_transformer(archive_handle->src_fd, uncompress);
586 archive_handle->offset = 0;
587 while (get_header_tar(archive_handle) == EXIT_SUCCESS);
588
589 /* Can only do one file at a time */
590 return(EXIT_FAILURE);
591}
592#endif
593
594#define CTX_TEST (1 << 0)
595#define CTX_EXTRACT (1 << 1)
596#define TAR_OPT_BASEDIR (1 << 2)
597#define TAR_OPT_TARNAME (1 << 3)
598#define TAR_OPT_2STDOUT (1 << 4)
599#define TAR_OPT_P (1 << 5)
600#define TAR_OPT_VERBOSE (1 << 6)
601#define TAR_OPT_KEEP_OLD (1 << 7)
602
603#ifdef CONFIG_FEATURE_TAR_CREATE
604# define CTX_CREATE (1 << 8)
605# define TAR_OPT_DEREFERNCE (1 << 9)
606# define TAR_OPT_STR_CREATE "ch"
607# define TAR_OPT_FLAG_CREATE 2
608#else
609//# define CTX_CREATE 0
610# define TAR_OPT_STR_CREATE ""
611# define TAR_OPT_FLAG_CREATE 0
612#endif
613
614#ifdef CONFIG_FEATURE_TAR_BZIP2
615# define TAR_OPT_BZIP2 (1 << (8 + TAR_OPT_FLAG_CREATE))
616# define TAR_OPT_STR_BZIP2 "j"
617# define TAR_OPT_FLAG_BZIP2 1
618#else
619# define TAR_OPT_STR_BZIP2 ""
620# define TAR_OPT_FLAG_BZIP2 0
621#endif
622
623#ifdef CONFIG_FEATURE_TAR_FROM
624# define TAR_OPT_INCLUDE_FROM (1 << (8 + TAR_OPT_FLAG_CREATE + TAR_OPT_FLAG_BZIP2))
625# define TAR_OPT_EXCLUDE_FROM (1 << (8 + TAR_OPT_FLAG_CREATE + TAR_OPT_FLAG_BZIP2 + 1))
626# define TAR_OPT_STR_FROM "T:X:"
627# define TAR_OPT_FLAG_FROM 2
628#else
629# define TAR_OPT_STR_FROM ""
630# define TAR_OPT_FLAG_FROM 0
631#endif
632
633#ifdef CONFIG_FEATURE_TAR_GZIP
634# define TAR_OPT_GZIP (1 << (8 + TAR_OPT_FLAG_CREATE + TAR_OPT_FLAG_BZIP2 + TAR_OPT_FLAG_FROM))
635# define TAR_OPT_STR_GZIP "z"
636# define TAR_OPT_FLAG_GZIP 1
637#else
638# define TAR_OPT_STR_GZIP ""
639# define TAR_OPT_FLAG_GZIP 0
640#endif
641
642#ifdef CONFIG_FEATURE_TAR_COMPRESS
643# define TAR_OPT_UNCOMPRESS (1 << (8 + TAR_OPT_FLAG_CREATE + TAR_OPT_FLAG_BZIP2 + TAR_OPT_FLAG_FROM + TAR_OPT_FLAG_GZIP))
644# define TAR_OPT_STR_COMPRESS "Z"
645#else
646# define TAR_OPT_STR_COMPRESS ""
647#endif
648
649static const char tar_options[]="txC:f:Opvk" \
650 TAR_OPT_STR_CREATE \
651 TAR_OPT_STR_BZIP2 \
652 TAR_OPT_STR_FROM \
653 TAR_OPT_STR_GZIP \
654 TAR_OPT_STR_COMPRESS;
655
656#ifdef CONFIG_FEATURE_TAR_LONG_OPTIONS
657static const struct option tar_long_options[] = {
658 { "list", 0, NULL, 't' },
659 { "extract", 0, NULL, 'x' },
660 { "directory", 1, NULL, 'C' },
661 { "file", 1, NULL, 'f' },
662 { "to-stdout", 0, NULL, 'O' },
663 { "same-permissions", 0, NULL, 'p' },
664 { "verbose", 0, NULL, 'v' },
665 { "keep-old", 0, NULL, 'k' },
666# ifdef CONFIG_FEATURE_TAR_CREATE
667 { "create", 0, NULL, 'c' },
668 { "dereference", 0, NULL, 'h' },
669# endif
670# ifdef CONFIG_FEATURE_TAR_BZIP2
671 { "bzip2", 0, NULL, 'j' },
672# endif
673# ifdef CONFIG_FEATURE_TAR_FROM
674 { "files-from", 1, NULL, 'T' },
675 { "exclude-from", 1, NULL, 'X' },
676# endif
677# ifdef CONFIG_FEATURE_TAR_GZIP
678 { "gzip", 0, NULL, 'z' },
679# endif
680# ifdef CONFIG_FEATURE_TAR_COMPRESS
681 { "compress", 0, NULL, 'Z' },
682# endif
683 { 0, 0, 0, 0 }
684};
685#endif
686
687int tar_main(int argc, char **argv)
688{
689 char (*get_header_ptr)(archive_handle_t *) = get_header_tar;
690 archive_handle_t *tar_handle;
691 char *base_dir = NULL;
692 const char *tar_filename = "-";
693 unsigned long opt;
694 unsigned long ctx_flag = 0;
695
696 if (argc < 2) {
697 bb_show_usage();
698 }
699
700 /* Prepend '-' to the first argument if required */
701 if (argv[1][0] != '-') {
702 char *tmp;
703
704 bb_xasprintf(&tmp, "-%s", argv[1]);
705 argv[1] = tmp;
706 }
707
708 /* Initialise default values */
709 tar_handle = init_handle();
710 tar_handle->flags = ARCHIVE_CREATE_LEADING_DIRS | ARCHIVE_PRESERVE_DATE | ARCHIVE_EXTRACT_UNCONDITIONAL;
711
712 bb_opt_complementaly = "c~tx:t~cx:x~ct:X*:T*";
713#ifdef CONFIG_FEATURE_TAR_LONG_OPTIONS
714 bb_applet_long_options = tar_long_options;
715#endif
716
717 opt = bb_getopt_ulflags(argc, argv, tar_options,
718 &base_dir, /* Change to dir <optarg> */
719 &tar_filename /* archive filename */
720#ifdef CONFIG_FEATURE_TAR_FROM
721 , &(tar_handle->accept),
722 &(tar_handle->reject)
723#endif
724 );
725
726 /* Check one and only one context option was given */
727 if(opt & 0x80000000UL) {
728 bb_show_usage();
729 }
730#ifdef CONFIG_FEATURE_TAR_CREATE
731 ctx_flag = opt & (CTX_CREATE | CTX_TEST | CTX_EXTRACT);
732#else
733 ctx_flag = opt & (CTX_TEST | CTX_EXTRACT);
734#endif
735 if (ctx_flag == 0) {
736 bb_show_usage();
737 }
738 if(ctx_flag & CTX_TEST) {
739 if ((tar_handle->action_header == header_list) ||
740 (tar_handle->action_header == header_verbose_list)) {
741 tar_handle->action_header = header_verbose_list;
742 } else {
743 tar_handle->action_header = header_list;
744 }
745 }
746 if(ctx_flag & CTX_EXTRACT) {
747 if (tar_handle->action_data != data_extract_to_stdout)
748 tar_handle->action_data = data_extract_all;
749 }
750 if(opt & TAR_OPT_2STDOUT) {
751 /* To stdout */
752 tar_handle->action_data = data_extract_to_stdout;
753 }
754 if(opt & TAR_OPT_VERBOSE) {
755 if ((tar_handle->action_header == header_list) ||
756 (tar_handle->action_header == header_verbose_list))
757 {
758 tar_handle->action_header = header_verbose_list;
759 } else {
760 tar_handle->action_header = header_list;
761 }
762 }
763 if (opt & TAR_OPT_KEEP_OLD) {
764 tar_handle->flags &= ~ARCHIVE_EXTRACT_UNCONDITIONAL;
765 }
766
767#ifdef CONFIG_FEATURE_TAR_GZIP
768 if(opt & TAR_OPT_GZIP) {
769 get_header_ptr = get_header_tar_gz;
770 }
771#endif
772#ifdef CONFIG_FEATURE_TAR_BZIP2
773 if(opt & TAR_OPT_BZIP2) {
774 get_header_ptr = get_header_tar_bz2;
775 }
776#endif
777#ifdef CONFIG_FEATURE_TAR_COMPRESS
778 if(opt & TAR_OPT_UNCOMPRESS) {
779 get_header_ptr = get_header_tar_Z;
780 }
781#endif
782#ifdef CONFIG_FEATURE_TAR_FROM
783 if(opt & TAR_OPT_EXCLUDE_FROM) {
784 tar_handle->reject = append_file_list_to_list(tar_handle->reject);
785 }
786 if(opt & TAR_OPT_INCLUDE_FROM) {
787 tar_handle->accept = append_file_list_to_list(tar_handle->accept);
788 }
789#endif
790
791 /* Check if we are reading from stdin */
792 if ((argv[optind]) && (*argv[optind] == '-')) {
793 /* Default is to read from stdin, so just skip to next arg */
794 optind++;
795 }
796
797 /* Setup an array of filenames to work with */
798 /* TODO: This is the same as in ar, separate function ? */
799 while (optind < argc) {
800 char *filename_ptr = last_char_is(argv[optind], '/');
801 if (filename_ptr) {
802 *filename_ptr = '\0';
803 }
804 tar_handle->accept = llist_add_to(tar_handle->accept, argv[optind]);
805 optind++;
806 }
807
808 if ((tar_handle->accept) || (tar_handle->reject)) {
809 tar_handle->filter = filter_accept_reject_list;
810 }
811
812 /* Open the tar file */
813 {
814 FILE *tar_stream;
815 int flags;
816
817#ifdef CONFIG_FEATURE_TAR_CREATE
818 if (opt & CTX_CREATE) {
819 /* Make sure there is at least one file to tar up. */
820 if (tar_handle->accept == NULL) {
821 bb_error_msg_and_die("Cowardly refusing to create an empty archive");
822 }
823 tar_stream = stdout;
824 flags = O_WRONLY | O_CREAT | O_EXCL;
825 unlink(tar_filename);
826 } else
827#endif
828 {
829 tar_stream = stdin;
830 flags = O_RDONLY;
831 }
832
833 if ((tar_filename[0] == '-') && (tar_filename[1] == '\0')) {
834 tar_handle->src_fd = fileno(tar_stream);
835 tar_handle->seek = seek_by_char;
836 } else {
837 tar_handle->src_fd = bb_xopen(tar_filename, flags);
838 }
839 }
840
841 if ((base_dir) && (chdir(base_dir))) {
842 bb_perror_msg_and_die("Couldnt chdir to %s", base_dir);
843 }
844
845#ifdef CONFIG_FEATURE_TAR_CREATE
846 /* create an archive */
847 if (opt & CTX_CREATE) {
848 int verboseFlag = FALSE;
849 int gzipFlag = FALSE;
850
851# ifdef CONFIG_FEATURE_TAR_GZIP
852 if (get_header_ptr == get_header_tar_gz) {
853 gzipFlag = TRUE;
854 }
855# endif /* CONFIG_FEATURE_TAR_GZIP */
856# ifdef CONFIG_FEATURE_TAR_BZIP2
857 if (get_header_ptr == get_header_tar_bz2) {
858 bb_error_msg_and_die("Creating bzip2 compressed archives is not currently supported.");
859 }
860# endif /* CONFIG_FEATURE_TAR_BZIP2 */
861
862 if ((tar_handle->action_header == header_list) ||
863 (tar_handle->action_header == header_verbose_list)) {
864 verboseFlag = TRUE;
865 }
866 writeTarFile(tar_handle->src_fd, verboseFlag, opt & TAR_OPT_DEREFERNCE, tar_handle->accept,
867 tar_handle->reject, gzipFlag);
868 } else
869#endif /* CONFIG_FEATURE_TAR_CREATE */
870 {
871 while (get_header_ptr(tar_handle) == EXIT_SUCCESS);
872
873 /* Ckeck that every file that should have been extracted was */
874 while (tar_handle->accept) {
875 if (find_list_entry(tar_handle->reject, tar_handle->accept->data) == NULL) {
876 if (find_list_entry(tar_handle->passed, tar_handle->accept->data) == NULL) {
877 bb_error_msg_and_die("%s: Not found in archive\n", tar_handle->accept->data);
878 }
879 }
880 tar_handle->accept = tar_handle->accept->link;
881 }
882 }
883
884#ifdef CONFIG_FEATURE_CLEAN_UP
885 if (tar_handle->src_fd != STDIN_FILENO) {
886 close(tar_handle->src_fd);
887 }
888#endif /* CONFIG_FEATURE_CLEAN_UP */
889
890 return(EXIT_SUCCESS);
891}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Uncompress applet for busybox (c) 2002 Glenn McGrath
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20#include <stdlib.h>
21#include <string.h>
22#include <unistd.h>
23#include <getopt.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <fcntl.h>
27
28#include "libbb.h"
29#include "unarchive.h"
30
31#define GUNZIP_TO_STDOUT 1
32#define GUNZIP_FORCE 2
33
34extern int uncompress_main(int argc, char **argv)
35{
36 int status = EXIT_SUCCESS;
37 unsigned long flags;
38
39 flags = bb_getopt_ulflags(argc, argv, "cf");
40
41 while (optind < argc) {
42 const char *compressed_file = argv[optind++];
43 const char *delete_path = NULL;
44 char *uncompressed_file = NULL;
45 int src_fd;
46 int dst_fd;
47
48 if (strcmp(compressed_file, "-") == 0) {
49 src_fd = STDIN_FILENO;
50 flags |= GUNZIP_TO_STDOUT;
51 } else {
52 src_fd = bb_xopen(compressed_file, O_RDONLY);
53 }
54
55 /* Check that the input is sane. */
56 if (isatty(src_fd) && ((flags & GUNZIP_FORCE) == 0)) {
57 bb_error_msg_and_die
58 ("compressed data not read from terminal. Use -f to force it.");
59 }
60
61 /* Set output filename and number */
62 if (flags & GUNZIP_TO_STDOUT) {
63 dst_fd = STDOUT_FILENO;
64 } else {
65 struct stat stat_buf;
66 char *extension;
67
68 uncompressed_file = bb_xstrdup(compressed_file);
69
70 extension = strrchr(uncompressed_file, '.');
71 if (!extension || (strcmp(extension, ".Z") != 0)) {
72 bb_error_msg_and_die("Invalid extension");
73 }
74 *extension = '\0';
75
76 /* Open output file */
77 dst_fd = bb_xopen(uncompressed_file, O_WRONLY | O_CREAT);
78
79 /* Set permissions on the file */
80 stat(compressed_file, &stat_buf);
81 chmod(uncompressed_file, stat_buf.st_mode);
82
83 /* If unzip succeeds remove the old file */
84 delete_path = compressed_file;
85 }
86
87 /* do the decompression, and cleanup */
88 if ((bb_xread_char(src_fd) != 0x1f) || (bb_xread_char(src_fd) != 0x9d)) {
89 bb_error_msg_and_die("Invalid magic");
90 }
91
92 status = uncompress(src_fd, dst_fd);
93
94 if ((status != EXIT_SUCCESS) && (uncompressed_file)) {
95 /* Unzip failed, remove the uncomressed file instead of compressed file */
96 delete_path = uncompressed_file;
97 }
98
99 if (dst_fd != STDOUT_FILENO) {
100 close(dst_fd);
101 }
102 if (src_fd != STDIN_FILENO) {
103 close(src_fd);
104 }
105
106 /* delete_path will be NULL if in test mode or from stdin */
107 if (delete_path && (unlink(delete_path) == -1)) {
108 bb_error_msg_and_die("Couldn't remove %s", delete_path);
109 }
110
111 free(uncompressed_file);
112 }
113
114 return status;
115}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini unzip implementation for busybox
4 *
5 * Copyright (C) 2001 by Laurence Anderson
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* For reference see
24 * http://www.pkware.com/products/enterprise/white_papers/appnote.txt
25 * http://www.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip
26 */
27
28/* TODO Endian issues, exclude, should we accept input from stdin ? */
29
30#include <fcntl.h>
31#include <getopt.h>
32#include <stdlib.h>
33#include <string.h>
34#include <unistd.h>
35#include "unarchive.h"
36#include "busybox.h"
37
38#define ZIP_FILEHEADER_MAGIC 0x04034b50
39#define ZIP_CDS_MAGIC 0x02014b50
40#define ZIP_CDS_END_MAGIC 0x06054b50
41#define ZIP_DD_MAGIC 0x08074b50
42
43extern unsigned int gunzip_crc;
44extern unsigned int gunzip_bytes_out;
45
46static void header_list_unzip(const file_header_t *file_header)
47{
48 printf(" inflating: %s\n", file_header->name);
49}
50
51static void header_verbose_list_unzip(const file_header_t *file_header)
52{
53 unsigned int dostime = (unsigned int) file_header->mtime;
54
55 /* can printf arguments cut of the decade component ? */
56 unsigned short year = 1980 + ((dostime & 0xfe000000) >> 25);
57 while (year >= 100) {
58 year -= 100;
59 }
60
61 printf("%9u %02u-%02u-%02u %02u:%02u %s\n",
62 (unsigned int) file_header->size,
63 (dostime & 0x01e00000) >> 21,
64 (dostime & 0x001f0000) >> 16,
65 year,
66 (dostime & 0x0000f800) >> 11,
67 (dostime & 0x000007e0) >> 5,
68 file_header->name);
69}
70
71extern int unzip_main(int argc, char **argv)
72{
73 union {
74 unsigned char raw[26];
75 struct {
76 unsigned short version; /* 0-1 */
77 unsigned short flags; /* 2-3 */
78 unsigned short method; /* 4-5 */
79 unsigned short modtime; /* 6-7 */
80 unsigned short moddate; /* 8-9 */
81 unsigned int crc32 __attribute__ ((packed)); /* 10-13 */
82 unsigned int cmpsize __attribute__ ((packed));; /* 14-17 */
83 unsigned int ucmpsize __attribute__ ((packed));; /* 18-21 */
84 unsigned short filename_len; /* 22-23 */
85 unsigned short extra_len; /* 24-25 */
86 } formated __attribute__ ((packed));
87 } zip_header;
88
89 archive_handle_t *archive_handle;
90 unsigned int total_size = 0;
91 unsigned int total_entries = 0;
92 char *base_dir = NULL;
93 int opt = 0;
94
95 /* Initialise */
96 archive_handle = init_handle();
97 archive_handle->action_data = NULL;
98 archive_handle->action_header = header_list_unzip;
99
100 while ((opt = getopt(argc, argv, "lnopqd:")) != -1) {
101 switch (opt) {
102 case 'l': /* list */
103 archive_handle->action_header = header_verbose_list_unzip;
104 archive_handle->action_data = data_skip;
105 break;
106 case 'n': /* never overwright existing files */
107 break;
108 case 'o':
109 archive_handle->flags = ARCHIVE_EXTRACT_UNCONDITIONAL;
110 break;
111 case 'p': /* extract files to stdout */
112 archive_handle->action_data = data_extract_to_stdout;
113 break;
114 case 'q': /* Extract files quietly */
115 archive_handle->action_header = header_skip;
116 break;
117 case 'd': /* Extract files to specified base directory*/
118 base_dir = optarg;
119 break;
120#if 0
121 case 'x': /* Exclude the specified files */
122 archive_handle->filter = filter_accept_reject_list;
123 break;
124#endif
125 default:
126 bb_show_usage();
127 }
128 }
129
130 if (argc == optind) {
131 bb_show_usage();
132 }
133
134 printf("Archive: %s\n", argv[optind]);
135 if (archive_handle->action_header == header_verbose_list_unzip) {
136 printf(" Length Date Time Name\n");
137 printf(" -------- ---- ---- ----\n");
138 }
139
140 if (*argv[optind] == '-') {
141 archive_handle->src_fd = STDIN_FILENO;
142 archive_handle->seek = seek_by_char;
143 } else {
144 archive_handle->src_fd = bb_xopen(argv[optind++], O_RDONLY);
145 }
146
147 if ((base_dir) && (chdir(base_dir))) {
148 bb_perror_msg_and_die("Couldnt chdir");
149 }
150
151 while (optind < argc) {
152 archive_handle->filter = filter_accept_list;
153 archive_handle->accept = llist_add_to(archive_handle->accept, argv[optind]);
154 optind++;
155 }
156
157 while (1) {
158 unsigned int magic;
159 int dst_fd;
160
161 /* TODO Endian issues */
162 archive_xread_all(archive_handle, &magic, 4);
163 archive_handle->offset += 4;
164
165 if (magic == ZIP_CDS_MAGIC) {
166 break;
167 }
168 else if (magic != ZIP_FILEHEADER_MAGIC) {
169 bb_error_msg_and_die("Invlaide zip magic");
170 }
171
172 /* Read the file header */
173 archive_xread_all(archive_handle, zip_header.raw, 26);
174 archive_handle->offset += 26;
175 archive_handle->file_header->mode = S_IFREG | 0777;
176
177 if (zip_header.formated.method != 8) {
178 bb_error_msg_and_die("Unsupported compression method %d\n", zip_header.formated.method);
179 }
180
181 /* Read filename */
182 archive_handle->file_header->name = xmalloc(zip_header.formated.filename_len + 1);
183 archive_xread_all(archive_handle, archive_handle->file_header->name, zip_header.formated.filename_len);
184 archive_handle->offset += zip_header.formated.filename_len;
185 archive_handle->file_header->name[zip_header.formated.filename_len] = '\0';
186
187 /* Skip extra header bits */
188 archive_handle->file_header->size = zip_header.formated.extra_len;
189 data_skip(archive_handle);
190 archive_handle->offset += zip_header.formated.extra_len;
191
192 /* Handle directories */
193 archive_handle->file_header->mode = S_IFREG | 0777;
194 if (last_char_is(archive_handle->file_header->name, '/')) {
195 archive_handle->file_header->mode ^= S_IFREG;
196 archive_handle->file_header->mode |= S_IFDIR;
197 }
198
199 /* Data section */
200 archive_handle->file_header->size = zip_header.formated.cmpsize;
201 if (archive_handle->action_data) {
202 archive_handle->action_data(archive_handle);
203 } else {
204 dst_fd = bb_xopen(archive_handle->file_header->name, O_WRONLY | O_CREAT);
205 inflate_init(zip_header.formated.cmpsize);
206 inflate_unzip(archive_handle->src_fd, dst_fd);
207 close(dst_fd);
208 chmod(archive_handle->file_header->name, archive_handle->file_header->mode);
209
210 /* Validate decompression - crc */
211 if (zip_header.formated.crc32 != (gunzip_crc ^ 0xffffffffL)) {
212 bb_error_msg("Invalid compressed data--crc error");
213 }
214
215 /* Validate decompression - size */
216 if (gunzip_bytes_out != zip_header.formated.ucmpsize) {
217 bb_error_msg("Invalid compressed data--length error");
218 }
219 }
220
221 /* local file descriptor section */
222 archive_handle->offset += zip_header.formated.cmpsize;
223 /* This ISNT unix time */
224 archive_handle->file_header->mtime = zip_header.formated.modtime | (zip_header.formated.moddate << 16);
225 archive_handle->file_header->size = zip_header.formated.ucmpsize;
226 total_size += archive_handle->file_header->size;
227 total_entries++;
228
229 archive_handle->action_header(archive_handle->file_header);
230
231 /* Data descriptor section */
232 if (zip_header.formated.flags & 4) {
233 /* skip over duplicate crc, compressed size and uncompressed size */
234 unsigned char data_description[12];
235 archive_xread_all(archive_handle, data_description, 12);
236 archive_handle->offset += 12;
237 }
238 }
239 /* Central directory section */
240
241 if (archive_handle->action_header == header_verbose_list_unzip) {
242 printf(" -------- -------\n");
243 printf("%9d %d files\n", total_size, total_entries);
244 }
245
246 return(EXIT_SUCCESS);
247}
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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Console Utilities"
7
8config CONFIG_CHVT
9 bool "chvt"
10 default n
11 help
12 This program is used to change to another terminal.
13 Example: chvt 4 (change to terminal /dev/tty4)
14
15config CONFIG_CLEAR
16 bool "clear"
17 default n
18 help
19 This program clears the terminal screen.
20
21config CONFIG_DEALLOCVT
22 bool "deallocvt"
23 default n
24 help
25 This program deallocates unused virtual consoles.
26
27config CONFIG_DUMPKMAP
28 bool "dumpkmap"
29 default n
30 help
31 This program dumps the kernel's keyboard translation table to
32 stdout, in binary format. You can then use loadkmap to load it.
33
34config CONFIG_LOADFONT
35 bool "loadfont"
36 default n
37 help
38 This program loads a console font from standard input.
39
40config CONFIG_LOADKMAP
41 bool "loadkmap"
42 default n
43 help
44 This program loads a keyboard translation table from
45 standard input.
46
47config CONFIG_OPENVT
48 bool "openvt"
49 default n
50 help
51 This program is used to start a command on an unused
52 virtual terminal.
53
54config CONFIG_RESET
55 bool "reset"
56 default n
57 help
58 This program is used to reset the terminal screen, if it
59 gets messed up.
60
61config CONFIG_SETKEYCODES
62 bool "setkeycodes"
63 default n
64 help
65 This program loads entries into the kernel's scancode-to-keycode
66 map, allowing unusual keyboards to generate usable keycodes.
67
68endmenu
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/console/tools
23CONSOLETOOLS_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include $(srcdir)/Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20CONSOLETOOLS_AR:=console-tools.a
21ifndef $(CONSOLETOOLS_DIR)
22CONSOLETOOLS_DIR:=$(top_builddir)/console-tools/
23endif
24srcdir=$(top_srcdir)/console-tools
25
26CONSOLETOOLS_DIR-y:=
27CONSOLETOOLS_DIR-$(CONFIG_CHVT) += chvt.o
28CONSOLETOOLS_DIR-$(CONFIG_CLEAR) += clear.o
29CONSOLETOOLS_DIR-$(CONFIG_DEALLOCVT) += deallocvt.o
30CONSOLETOOLS_DIR-$(CONFIG_DUMPKMAP) += dumpkmap.o
31CONSOLETOOLS_DIR-$(CONFIG_LOADFONT) += loadfont.o
32CONSOLETOOLS_DIR-$(CONFIG_LOADKMAP) += loadkmap.o
33CONSOLETOOLS_DIR-$(CONFIG_OPENVT) += openvt.o
34CONSOLETOOLS_DIR-$(CONFIG_RESET) += reset.o
35CONSOLETOOLS_DIR-$(CONFIG_SETKEYCODES) += setkeycodes.o
36
37libraries-y+=$(CONSOLETOOLS_DIR)$(CONSOLETOOLS_AR)
38
39$(CONSOLETOOLS_DIR)$(CONSOLETOOLS_AR): $(patsubst %,$(CONSOLETOOLS_DIR)%, $(CONSOLETOOLS_DIR-y))
40 $(AR) -ro $@ $(patsubst %,$(CONSOLETOOLS_DIR)%, $(CONSOLETOOLS_DIR-y))
41
42$(CONSOLETOOLS_DIR)%.o: $(srcdir)/%.c
43 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
44
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini chvt implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22/* no options, no getopt */
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <fcntl.h>
27#include <sys/types.h>
28#include <sys/ioctl.h>
29#include "busybox.h"
30
31/* From <linux/vt.h> */
32static const int VT_ACTIVATE = 0x5606; /* make vt active */
33static const int VT_WAITACTIVE = 0x5607; /* wait for vt active */
34
35int chvt_main(int argc, char **argv)
36{
37 int fd, num;
38
39 if (argc != 2) {
40 bb_show_usage();
41 }
42
43 fd = get_console_fd();
44 num = bb_xgetlarg(argv[1], 10, 0, INT_MAX);
45 if (ioctl(fd, VT_ACTIVATE, num)) {
46 bb_perror_msg_and_die("VT_ACTIVATE");
47 }
48 if (ioctl(fd, VT_WAITACTIVE, num)) {
49 bb_perror_msg_and_die("VT_WAITACTIVE");
50 }
51 return EXIT_SUCCESS;
52}
53
54
55/*
56Local Variables:
57c-file-style: "linux"
58c-basic-offset: 4
59tab-width: 4
60End:
61*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini clear implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* no options, no getopt */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include "busybox.h"
28
29
30extern int clear_main(int argc, char **argv)
31{
32 printf("\033[H\033[J");
33 return EXIT_SUCCESS;
34}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Disallocate virtual terminal(s)
4 *
5 * Copyright (C) 2003 by Tito Ragusa <farmatito@tiscali.it>
6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23/* no options, no getopt */
24
25#include <stdlib.h>
26#include <stdio.h>
27#include <fcntl.h>
28#include <sys/types.h>
29#include <sys/ioctl.h>
30#include "busybox.h"
31
32/* From <linux/vt.h> */
33static const int VT_DISALLOCATE = 0x5608; /* free memory associated to vt */
34
35int deallocvt_main(int argc, char *argv[])
36{
37 /* num = 0 deallocate all unused consoles */
38 int num = 0;
39
40 switch(argc)
41 {
42 case 2:
43 if((num = bb_xgetlarg(argv[1], 10, 0, INT_MAX)) == 0)
44 bb_error_msg_and_die("0: illegal VT number");
45 /* Falltrough */
46 case 1:
47 break;
48 default:
49 bb_show_usage();
50 }
51
52 if (ioctl( get_console_fd(), VT_DISALLOCATE, num )) {
53 bb_perror_msg_and_die("VT_DISALLOCATE");
54 }
55 return EXIT_SUCCESS;
56}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini dumpkmap implementation for busybox
4 *
5 * Copyright (C) Arne Bernin <arne@matrix.loopback.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <errno.h>
24#include <fcntl.h>
25#include <stdio.h>
26#include <unistd.h>
27#include <string.h>
28#include <stdlib.h>
29#include <sys/ioctl.h>
30#include "busybox.h"
31
32/* From <linux/kd.h> */
33struct kbentry {
34 unsigned char kb_table;
35 unsigned char kb_index;
36 unsigned short kb_value;
37};
38static const int KDGKBENT = 0x4B46; /* gets one entry in translation table */
39
40/* From <linux/keyboard.h> */
41static const int NR_KEYS = 128;
42static const int MAX_NR_KEYMAPS = 256;
43
44int dumpkmap_main(int argc, char **argv)
45{
46 struct kbentry ke;
47 int i, j, fd;
48 char flags[MAX_NR_KEYMAPS], magic[] = "bkeymap";
49
50 if (argc>=2 && *argv[1]=='-') {
51 bb_show_usage();
52 }
53
54 fd=bb_xopen(CURRENT_VC, O_RDWR);
55
56 write(1, magic, 7);
57
58 for (i=0; i < MAX_NR_KEYMAPS; i++) flags[i]=0;
59 flags[0]=1;
60 flags[1]=1;
61 flags[2]=1;
62 flags[4]=1;
63 flags[5]=1;
64 flags[6]=1;
65 flags[8]=1;
66 flags[9]=1;
67 flags[10]=1;
68 flags[12]=1;
69
70 /* dump flags */
71 for (i=0; i < MAX_NR_KEYMAPS; i++) write(1,&flags[i],1);
72
73 for (i = 0; i < MAX_NR_KEYMAPS; i++) {
74 if (flags[i] == 1) {
75 for (j = 0; j < NR_KEYS; j++) {
76 ke.kb_index = j;
77 ke.kb_table = i;
78 if (ioctl(fd, KDGKBENT, &ke) < 0) {
79
80 bb_error_msg("ioctl returned: %m, %s, %s, %xqq", (char *)&ke.kb_index,(char *)&ke.kb_table,(int)&ke.kb_value);
81 }
82 else {
83 write(1,(void*)&ke.kb_value,2);
84 }
85
86 }
87 }
88 }
89 close(fd);
90 return EXIT_SUCCESS;
91}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * loadfont.c - Eugene Crosser & Andries Brouwer
4 *
5 * Version 0.96bb
6 *
7 * Loads the console font, and possibly the corresponding screen map(s).
8 * (Adapted for busybox by Matej Vela.)
9 */
10#include <stdio.h>
11#include <string.h>
12#include <fcntl.h>
13#include <memory.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <sys/types.h>
17#include <dirent.h>
18#include <errno.h>
19#include <sys/ioctl.h>
20#include <sys/kd.h>
21#include <endian.h>
22#include "busybox.h"
23
24static const int PSF_MAGIC1 = 0x36;
25static const int PSF_MAGIC2 = 0x04;
26
27static const int PSF_MODE512 = 0x01;
28static const int PSF_MODEHASTAB = 0x02;
29static const int PSF_MAXMODE = 0x03;
30static const int PSF_SEPARATOR = 0xFFFF;
31
32struct psf_header {
33 unsigned char magic1, magic2; /* Magic number */
34 unsigned char mode; /* PSF font mode */
35 unsigned char charsize; /* Character size */
36};
37
38#define PSF_MAGIC_OK(x) ((x).magic1 == PSF_MAGIC1 && (x).magic2 == PSF_MAGIC2)
39
40static void loadnewfont(int fd);
41
42extern int loadfont_main(int argc, char **argv)
43{
44 int fd;
45
46 if (argc != 1)
47 bb_show_usage();
48
49 fd = bb_xopen(CURRENT_VC, O_RDWR);
50 loadnewfont(fd);
51
52 return EXIT_SUCCESS;
53}
54
55static void do_loadfont(int fd, char *inbuf, int unit, int fontsize)
56{
57 char buf[16384];
58 int i;
59
60 memset(buf, 0, sizeof(buf));
61
62 if (unit < 1 || unit > 32)
63 bb_error_msg_and_die("Bad character size %d", unit);
64
65 for (i = 0; i < fontsize; i++)
66 memcpy(buf + (32 * i), inbuf + (unit * i), unit);
67
68#if defined( PIO_FONTX ) && !defined( __sparc__ )
69 {
70 struct consolefontdesc cfd;
71
72 cfd.charcount = fontsize;
73 cfd.charheight = unit;
74 cfd.chardata = buf;
75
76 if (ioctl(fd, PIO_FONTX, &cfd) == 0)
77 return; /* success */
78 bb_perror_msg("PIO_FONTX ioctl error (trying PIO_FONT)");
79 }
80#endif
81 if (ioctl(fd, PIO_FONT, buf))
82 bb_perror_msg_and_die("PIO_FONT ioctl error");
83}
84
85static void
86do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize)
87{
88 struct unimapinit advice;
89 struct unimapdesc ud;
90 struct unipair *up;
91 int ct = 0, maxct;
92 int glyph;
93 u_short unicode;
94
95 maxct = tailsz; /* more than enough */
96 up = (struct unipair *) xmalloc(maxct * sizeof(struct unipair));
97
98 for (glyph = 0; glyph < fontsize; glyph++) {
99 while (tailsz >= 2) {
100 unicode = (((u_short) inbuf[1]) << 8) + inbuf[0];
101 tailsz -= 2;
102 inbuf += 2;
103 if (unicode == PSF_SEPARATOR)
104 break;
105 up[ct].unicode = unicode;
106 up[ct].fontpos = glyph;
107 ct++;
108 }
109 }
110
111 /* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP
112 this printf did not work on many kernels */
113
114 advice.advised_hashsize = 0;
115 advice.advised_hashstep = 0;
116 advice.advised_hashlevel = 0;
117 if (ioctl(fd, PIO_UNIMAPCLR, &advice)) {
118#ifdef ENOIOCTLCMD
119 if (errno == ENOIOCTLCMD) {
120 bb_error_msg("It seems this kernel is older than 1.1.92");
121 bb_error_msg_and_die("No Unicode mapping table loaded.");
122 } else
123#endif
124 bb_perror_msg_and_die("PIO_UNIMAPCLR");
125 }
126 ud.entry_ct = ct;
127 ud.entries = up;
128 if (ioctl(fd, PIO_UNIMAP, &ud)) {
129#if 0
130 if (errno == ENOMEM) {
131 /* change advice parameters */
132 }
133#endif
134 bb_perror_msg_and_die("PIO_UNIMAP");
135 }
136}
137
138static void loadnewfont(int fd)
139{
140 int unit;
141 char inbuf[32768]; /* primitive */
142 unsigned int inputlth, offset;
143
144 /*
145 * We used to look at the length of the input file
146 * with stat(); now that we accept compressed files,
147 * just read the entire file.
148 */
149 inputlth = fread(inbuf, 1, sizeof(inbuf), stdin);
150 if (ferror(stdin))
151 bb_perror_msg_and_die("Error reading input font");
152 /* use malloc/realloc in case of giant files;
153 maybe these do not occur: 16kB for the font,
154 and 16kB for the map leaves 32 unicode values
155 for each font position */
156 if (!feof(stdin))
157 bb_perror_msg_and_die("Font too large");
158
159 /* test for psf first */
160 {
161 struct psf_header psfhdr;
162 int fontsize;
163 int hastable;
164 unsigned int head0, head;
165
166 if (inputlth < sizeof(struct psf_header))
167 goto no_psf;
168
169 psfhdr = *(struct psf_header *) &inbuf[0];
170
171 if (!PSF_MAGIC_OK(psfhdr))
172 goto no_psf;
173
174 if (psfhdr.mode > PSF_MAXMODE)
175 bb_error_msg_and_die("Unsupported psf file mode");
176 fontsize = ((psfhdr.mode & PSF_MODE512) ? 512 : 256);
177#if !defined( PIO_FONTX ) || defined( __sparc__ )
178 if (fontsize != 256)
179 bb_error_msg_and_die("Only fontsize 256 supported");
180#endif
181 hastable = (psfhdr.mode & PSF_MODEHASTAB);
182 unit = psfhdr.charsize;
183 head0 = sizeof(struct psf_header);
184
185 head = head0 + fontsize * unit;
186 if (head > inputlth || (!hastable && head != inputlth))
187 bb_error_msg_and_die("Input file: bad length");
188 do_loadfont(fd, inbuf + head0, unit, fontsize);
189 if (hastable)
190 do_loadtable(fd, inbuf + head, inputlth - head, fontsize);
191 return;
192 }
193 no_psf:
194
195 /* file with three code pages? */
196 if (inputlth == 9780) {
197 offset = 40;
198 unit = 16;
199 } else {
200 /* bare font */
201 if (inputlth & 0377)
202 bb_error_msg_and_die("Bad input file size");
203 offset = 0;
204 unit = inputlth / 256;
205 }
206 do_loadfont(fd, inbuf + offset, unit, 256);
207}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini loadkmap implementation for busybox
4 *
5 * Copyright (C) 1998 Enrique Zanardi <ezanardi@ull.es>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <errno.h>
24#include <fcntl.h>
25#include <stdio.h>
26#include <string.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include <sys/ioctl.h>
30#include "busybox.h"
31
32#define BINARY_KEYMAP_MAGIC "bkeymap"
33
34/* From <linux/kd.h> */
35struct kbentry {
36 unsigned char kb_table;
37 unsigned char kb_index;
38 unsigned short kb_value;
39};
40/* sets one entry in translation table */
41#define KDSKBENT 0x4B47
42
43/* From <linux/keyboard.h> */
44#define NR_KEYS 128
45#define MAX_NR_KEYMAPS 256
46
47int loadkmap_main(int argc, char **argv)
48{
49 struct kbentry ke;
50 int i, j, fd;
51 u_short ibuff[NR_KEYS];
52 char flags[MAX_NR_KEYMAPS];
53 char buff[7];
54
55 if (argc != 1)
56 bb_show_usage();
57
58 fd = bb_xopen(CURRENT_VC, O_RDWR);
59
60 if ((bb_full_read(0, buff, 7) != 7) || (strncmp(buff, BINARY_KEYMAP_MAGIC, 7) != 0))
61 bb_error_msg_and_die("This is not a valid binary keymap.");
62
63 if (bb_full_read(0, flags, MAX_NR_KEYMAPS) != MAX_NR_KEYMAPS)
64 bb_perror_msg_and_die("Error reading keymap flags");
65
66 for (i = 0; i < MAX_NR_KEYMAPS; i++) {
67 if (flags[i] == 1) {
68 bb_full_read(0, ibuff, NR_KEYS * sizeof(u_short));
69 for (j = 0; j < NR_KEYS; j++) {
70 ke.kb_index = j;
71 ke.kb_table = i;
72 ke.kb_value = ibuff[j];
73 ioctl(fd, KDSKBENT, &ke);
74 }
75 }
76 }
77
78 /* Don't bother to close files. Exit does that
79 * automagically, so we can save a few bytes */
80 /* close(fd); */
81 return EXIT_SUCCESS;
82}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * openvt.c - open a vt to run a command.
4 *
5 * busyboxed by Quy Tonthat <quy@signal3.com>
6 * hacked by Tito <farmatito@tiscali.it>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 */
22
23/* getopt not needed */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <fcntl.h>
29#include <string.h>
30#include <sys/types.h>
31#include <ctype.h>
32
33#include "busybox.h"
34
35int openvt_main(int argc, char **argv)
36{
37 int fd;
38 char vtname[sizeof VC_FORMAT + 2];
39
40
41 if (argc < 3)
42 bb_show_usage();
43
44 /* check for Illegal vt number: < 1 or > 12 */
45 sprintf(vtname, VC_FORMAT,(int)bb_xgetlarg(argv[1], 10, 1, 12));
46
47 argv+=2;
48 argc-=2;
49
50 if(fork() == 0) {
51 /* leave current vt */
52
53#ifdef ESIX_5_3_2_D
54 if (setpgrp() < 0) {
55#else
56 if (setsid() < 0) {
57#endif
58
59 bb_perror_msg_and_die("Unable to set new session");
60 }
61 close(0); /* so that new vt becomes stdin */
62
63 /* and grab new one */
64 fd = bb_xopen(vtname, O_RDWR);
65
66 /* Reassign stdout and sterr */
67 close(1);
68 close(2);
69 dup(fd);
70 dup(fd);
71
72 execvp(argv[0], argv);
73 _exit(1);
74 }
75 return EXIT_SUCCESS;
76}
77
78/*
79Local Variables:
80c-file-style: "linux"
81c-basic-offset: 4
82tab-width: 4
83End:
84*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini reset implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 * Written by Erik Andersen and Kent Robotti <robotti@metconnect.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24/* no options, no getopt */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include "busybox.h"
30
31extern int reset_main(int argc, char **argv)
32{
33 if (isatty(0) || isatty(0) || isatty(0)) {
34 /* See 'man 4 console_codes' for details:
35 * "ESC c" -- Reset
36 * "ESC ( K" -- Select user mapping
37 * "ESC [ J" -- Erase display
38 * "ESC [ 0 m" -- Reset all Graphics Rendition display attributes
39 * "ESC [ ? 25 h" -- Make cursor visible.
40 */
41 printf("\033c\033(K\033[J\033[0m\033[?25h");
42 }
43 return EXIT_SUCCESS;
44}
45
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * setkeycodes
4 *
5 * Copyright (C) 1994-1998 Andries E. Brouwer <aeb@cwi.nl>
6 *
7 * Adjusted for BusyBox by Erik Andersen <andersen@codepoet.org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <fcntl.h>
28#include <sys/ioctl.h>
29#include "busybox.h"
30
31
32/* From <linux/kd.h> */
33struct kbkeycode {
34 unsigned int scancode, keycode;
35};
36static const int KDSETKEYCODE = 0x4B4D; /* write kernel keycode table entry */
37
38extern int
39setkeycodes_main(int argc, char** argv)
40{
41 char *ep;
42 int fd, sc;
43 struct kbkeycode a;
44
45 if (argc % 2 != 1 || argc < 2) {
46 bb_show_usage();
47 }
48
49 fd = get_console_fd();
50
51 while (argc > 2) {
52 a.keycode = atoi(argv[2]);
53 a.scancode = sc = strtol(argv[1], &ep, 16);
54 if (*ep) {
55 bb_error_msg_and_die("error reading SCANCODE: '%s'", argv[1]);
56 }
57 if (a.scancode > 127) {
58 a.scancode -= 0xe000;
59 a.scancode += 128;
60 }
61 if (a.scancode > 255 || a.keycode > 127) {
62 bb_error_msg_and_die("SCANCODE or KEYCODE outside bounds");
63 }
64 if (ioctl(fd,KDSETKEYCODE,&a)) {
65 perror("KDSETKEYCODE");
66 bb_error_msg_and_die("failed to set SCANCODE %x to KEYCODE %d", sc, a.keycode);
67 }
68 argc -= 2;
69 argv += 2;
70 }
71 return EXIT_SUCCESS;
72}
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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Coreutils"
7
8config CONFIG_BASENAME
9 bool "basename"
10 default n
11 help
12 basename is used to strip the directory and suffix from filenames,
13 leaving just the filename itself. Enable this option if you wish
14 to enable the 'basename' utility.
15
16config CONFIG_CAL
17 bool "cal"
18 default n
19 help
20 cal is used to display a monthly calender.
21
22config CONFIG_CAT
23 bool "cat"
24 default n
25 help
26 cat is used to concatenate files and print them to the standard
27 output. Enable this option if you wish to enable the 'cat' utility.
28
29config CONFIG_CHGRP
30 bool "chgrp"
31 default n
32 help
33 chgrp is used to change the group ownership of files.
34
35config CONFIG_CHMOD
36 bool "chmod"
37 default n
38 help
39 chmod is used to change the access permission of files.
40
41config CONFIG_CHOWN
42 bool "chown"
43 default n
44 help
45 chown is used to change the user and/or group ownership
46 of files.
47
48config CONFIG_CHROOT
49 bool "chroot"
50 default n
51 help
52 chroot is used to change the root directory and run a command.
53 The default command is `/bin/sh'.
54
55config CONFIG_CMP
56 bool "cmp"
57 default n
58 help
59 cmp is used to compare two files and returns the result
60 to standard output.
61
62config CONFIG_CP
63 bool "cp"
64 default n
65 help
66 cp is used to copy files and directories.
67
68config CONFIG_CUT
69 bool "cut"
70 default n
71 help
72 cut is used to print selected parts of lines from
73 each file to stdout.
74
75if CONFIG_WATCH
76 config CONFIG_DATE
77 default y
78 comment "date (forced enabled for use with watch)"
79endif
80
81if !CONFIG_WATCH
82 config CONFIG_DATE
83 bool "date"
84 default n
85 help
86 date is used to set the system date or display the
87 current time in the given format.
88endif
89
90config CONFIG_FEATURE_DATE_ISOFMT
91 bool " Enable ISO date format output (-I)"
92 default y
93 depends on CONFIG_DATE
94 help
95 Enable option (-I) to output an ISO-8601 compliant
96 date/time string.
97
98config CONFIG_DD
99 bool "dd"
100 default n
101 help
102 dd copies a file (from standard input to standard output,
103 by default) using specific input and output blocksizes,
104 while optionally performing conversions on it.
105
106config CONFIG_DF
107 bool "df"
108 default n
109 help
110 df reports the amount of disk space used and available
111 on filesystems.
112
113config CONFIG_DIRNAME
114 bool "dirname"
115 default n
116 help
117 dirname is used to strip a non-directory suffix from
118 a file name.
119
120config CONFIG_DOS2UNIX
121 bool "dos2unix/unix2dos"
122 default n
123 help
124 dos2unix is used to convert a text file from DOS format to
125 UNIX format, and vice versa.
126
127config CONFIG_UNIX2DOS
128 bool
129 default y
130 depends on CONFIG_DOS2UNIX
131
132config CONFIG_DU
133 bool "du (default blocksize of 512 bytes)"
134 default n
135 help
136 du is used to report the amount of disk space used
137 for specified files.
138
139config CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
140 bool " Use a default blocksize of 1024 bytes (1K)"
141 default y
142 depends on CONFIG_DU
143 help
144 Use a blocksize of (1K) instead of the default 512b.
145
146config CONFIG_ECHO
147 bool "echo (basic SUSv3 version taking no options)"
148 default n
149 help
150 echo is used to print a specified string to stdout.
151
152config CONFIG_FEATURE_FANCY_ECHO
153 bool " Enable echo options (-n and -e)"
154 default y
155 depends on CONFIG_ECHO
156 help
157 This adds options (-n and -e) to echo.
158
159config CONFIG_ENV
160 bool "env"
161 default n
162 help
163 env is used to set an environment variable and run
164 a command; without options it displays the current
165 environment.
166
167config CONFIG_EXPR
168 bool "expr"
169 default n
170 help
171 expr is used to calculate numbers and print the result
172 to standard output.
173
174if CONFIG_HUSH || CONFIG_LASH || CONFIG_MSH
175 config CONFIG_FALSE
176 default y
177 comment "false (forced enabled for use with shell)"
178endif
179
180if !CONFIG_HUSH && !CONFIG_LASH && !CONFIG_MSH
181 config CONFIG_FALSE
182 bool "false"
183 default n
184 help
185 false returns an exit code of FALSE (1).
186endif
187
188config CONFIG_FOLD
189 bool "fold"
190 default n
191 help
192 Wrap text to fit a specific width.
193
194config CONFIG_HEAD
195 bool "head"
196 default n
197 help
198 head is used to print the first specified number of lines
199 from files.
200
201config CONFIG_FEATURE_FANCY_HEAD
202 bool " Enable head options (-c, -q, and -v)"
203 default n
204 depends on CONFIG_HEAD
205 help
206 This enables the head options (-c, -q, and -v).
207
208config CONFIG_HOSTID
209 bool "hostid"
210 default n
211 help
212 hostid prints the numeric identifier (in hexadecimal) for
213 the current host.
214
215config CONFIG_ID
216 bool "id"
217 default n
218 help
219 id displays the current user and group ID names.
220
221config CONFIG_INSTALL
222 bool "install"
223 default n
224 help
225 Copy files and set attributes.
226
227config CONFIG_LENGTH
228 bool "length"
229 default n
230 help
231 length is used to print out the length of a specified string.
232
233config CONFIG_LN
234 bool "ln"
235 default n
236 help
237 ln is used to create hard or soft links between files.
238
239config CONFIG_LOGNAME
240 bool "logname"
241 default n
242 help
243 logname is used to print the current user's login name.
244
245config CONFIG_LS
246 bool "ls"
247 default n
248 help
249 ls is used to list the contents of directories.
250
251config CONFIG_FEATURE_LS_FILETYPES
252 bool " Enable filetyping options (-p and -F)"
253 default y
254 depends on CONFIG_LS
255 help
256 Enable the ls options (-p and -F).
257
258config CONFIG_FEATURE_LS_FOLLOWLINKS
259 bool " Enable symlinks dereferencing (-L)"
260 default y
261 depends on CONFIG_LS
262 help
263 Enable the ls option (-L).
264
265config CONFIG_FEATURE_LS_RECURSIVE
266 bool " Enable recursion (-R)"
267 default y
268 depends on CONFIG_LS
269 help
270 Enable the ls option (-R).
271
272config CONFIG_FEATURE_LS_SORTFILES
273 bool " Sort the file names"
274 default y
275 depends on CONFIG_LS
276 help
277 Allow ls to sort file names alphabetically.
278
279config CONFIG_FEATURE_LS_TIMESTAMPS
280 bool " Show file timestamps"
281 default y
282 depends on CONFIG_LS
283 help
284 Allow ls to display timestamps for files.
285
286config CONFIG_FEATURE_LS_USERNAME
287 bool " Show username/groupnames"
288 default y
289 depends on CONFIG_LS
290 help
291 Allow ls to display username/groupname for files.
292
293config CONFIG_FEATURE_LS_COLOR
294 bool " Use color to identify file types"
295 default y
296 depends on CONFIG_LS
297 help
298 Allow ls to use color when displaying files.
299
300config CONFIG_MD5SUM
301 bool "md5sum"
302 default n
303 help
304 md5sum is used to print or check MD5 checksums.
305
306config CONFIG_MKDIR
307 bool "mkdir"
308 default n
309 help
310 mkdir is used to create directories with the specified names.
311
312config CONFIG_MKFIFO
313 bool "mkfifo"
314 default n
315 help
316 mkfifo is used to create FIFOs (named pipes).
317 The `mknod' program can also create FIFOs.
318
319config CONFIG_MKNOD
320 bool "mknod"
321 default n
322 help
323 mknod is used to create FIFOs or block/character special
324 files with the specified names.
325
326config CONFIG_MV
327 bool "mv"
328 default n
329 help
330 mv is used to move or rename files or directories.
331
332config CONFIG_OD
333 bool "od"
334 default n
335 help
336 od is used to dump binary files in octal and other formats.
337
338config CONFIG_PRINTF
339 bool "printf"
340 default n
341 help
342 printf is used to format and print specified strings.
343 It's similar to `echo' except it has more options.
344
345config CONFIG_PWD
346 bool "pwd"
347 default n
348 help
349 pwd is used to print the current directory.
350
351config CONFIG_REALPATH
352 bool "realpath"
353 default n
354 help
355 Return the canonicalized absolute pathname.
356 This isn't provided by GNU shellutils, but where else does it belong.
357
358config CONFIG_RM
359 bool "rm"
360 default n
361 help
362 rm is used to remove files or directories.
363
364config CONFIG_RMDIR
365 bool "rmdir"
366 default n
367 help
368 rmdir is used to remove empty directories.
369
370config CONFIG_SEQ
371 bool "seq"
372 default n
373 help
374 print a sequence of numbers
375
376config CONFIG_SHA1SUM
377 bool "sha1sum"
378 default n
379 help
380 Compute and check SHA1 message digest
381
382config CONFIG_SLEEP
383 bool "sleep (single integer arg with no suffix)"
384 default n
385 help
386 sleep is used to pause for a specified number of seconds,
387
388config CONFIG_FEATURE_FANCY_SLEEP
389 bool " Enable multiple integer args and optional time suffixes"
390 default n
391 depends on CONFIG_SLEEP
392 help
393 Allow sleep to pause for specified minutes, hours, and days.
394
395config CONFIG_SORT
396 bool "sort"
397 default n
398 help
399 sort is used to sort lines of text in specified files.
400
401config CONFIG_STTY
402 bool "stty"
403 default n
404 help
405 stty is used to change and print terminal line settings.
406
407config CONFIG_SYNC
408 bool "sync"
409 default n
410 help
411 sync is used to flush filesystem buffers.
412
413config CONFIG_TAIL
414 bool "tail"
415 default n
416 help
417 tail is used to print the last specified number of lines
418 from files.
419
420config CONFIG_FEATURE_FANCY_TAIL
421 bool " Enable extra tail options (-q, -s, and -v)"
422 default y
423 depends on CONFIG_TAIL
424 help
425 The options (-q, -s, and -v) are provided by GNU tail, but
426 are not specific in the SUSv3 standard.
427
428config CONFIG_TEE
429 bool "tee"
430 default n
431 help
432 tee is used to read from standard input and write
433 to standard output and files.
434
435config CONFIG_FEATURE_TEE_USE_BLOCK_IO
436 bool " Enable block i/o (larger/faster) instead of byte i/o."
437 default n
438 depends on CONFIG_TEE
439 help
440 Enable this option for a faster tee, at expense of size.
441
442if CONFIG_ASH || CONFIG_HUSH || CONFIG_LASH || CONFIG_MSH
443 config CONFIG_TEST
444 default y
445 comment "test (forced enabled for use with shell)"
446endif
447
448if !CONFIG_ASH && !CONFIG_HUSH && !CONFIG_LASH && !CONFIG_MSH
449 config CONFIG_TEST
450 bool "test"
451 default n
452 help
453 test is used to check file types and compare values,
454 returning an appropriate exit code. The shells (ash
455 and bash) have test builtin.
456endif
457
458config CONFIG_FEATURE_TEST_64
459 bool " Extend test to 64 bit"
460 default n
461 depends on CONFIG_TEST
462 help
463 Enable 64-bit support in test.
464
465config CONFIG_TOUCH
466 bool "touch"
467 default n
468 help
469 touch is used to create or change the access and/or
470 modification timestamp of specified files.
471
472config CONFIG_TR
473 bool "tr"
474 default n
475 help
476 tr is used to squeeze, and/or delete characters from standard
477 input, writing to standard output.
478
479if CONFIG_HUSH || CONFIG_LASH || CONFIG_MSH
480 config CONFIG_TRUE
481 default y
482 comment "true (forced enabled for use with shell)"
483endif
484
485if !CONFIG_HUSH && !CONFIG_LASH && !CONFIG_MSH
486 config CONFIG_TRUE
487 bool "true"
488 default n
489 help
490 true returns an exit code of TRUE (0).
491
492endif
493
494config CONFIG_TTY
495 bool "tty"
496 default n
497 help
498 tty is used to print the name of the current terminal to
499 standard output.
500
501config CONFIG_UNAME
502 bool "uname"
503 default n
504 help
505 uname is used to print system information.
506
507config CONFIG_UNIQ
508 bool "uniq"
509 default n
510 help
511 uniq is used to remove duplicate lines from a sorted file.
512
513config CONFIG_USLEEP
514 bool "usleep"
515 default n
516 help
517 usleep is used to pause for a specified number of microseconds.
518
519config CONFIG_UUDECODE
520 bool "uudecode"
521 default n
522 help
523 uudecode is used to decode a uuencoded file.
524
525config CONFIG_UUENCODE
526 bool "uuencode"
527 default n
528 help
529 uuencode is used to uuencode a file.
530
531config CONFIG_WATCH
532 bool "watch"
533 default n
534 help
535 watch is used to execute a program periodically, showing
536 output to the screen.
537
538config CONFIG_WC
539 bool "wc"
540 default n
541 help
542 wc is used to print the number of bytes, words, and lines,
543 in specified files.
544
545config CONFIG_WHO
546 bool "who"
547 default n
548 select CONFIG_FEATURE_U_W_TMP
549 help
550 who is used to show who is logged on.
551
552config CONFIG_WHOAMI
553 bool "whoami"
554 default n
555 help
556 whoami is used to print the username of the current
557 user id (same as id -un).
558
559config CONFIG_YES
560 bool "yes"
561 default n
562 help
563 yes is used to repeatedly output a specific string, or
564 the default string `y'.
565
566comment "Common options for cp and mv"
567 depends on CONFIG_CP || CONFIG_MV
568
569config CONFIG_FEATURE_PRESERVE_HARDLINKS
570 bool " Preserve hard links"
571 default n
572 depends on CONFIG_CP || CONFIG_MV
573 help
574 Allow cp and mv to preserve hard links.
575
576comment "Common options for ls and more"
577 depends on CONFIG_LS || CONFIG_MORE
578
579config CONFIG_FEATURE_AUTOWIDTH
580 bool " Calculate terminal & column widths"
581 default y
582 depends on CONFIG_LS || CONFIG_MORE
583 help
584 This option allows utilities such as 'ls' and 'more' to determine the
585 width of the screen, which can allow them to display additional text
586 or avoid wrapping text onto the next line. If you leave this
587 disabled, your utilities will be especially primitive and will be
588 unable to determine the current screen width.
589
590comment "Common options for df, du, ls"
591 depends on CONFIG_DF || CONFIG_DU || CONFIG_LS
592
593config CONFIG_FEATURE_HUMAN_READABLE
594 bool " Support for human readable output (example 13k, 23M, 235G)"
595 default n
596 depends on CONFIG_DF || CONFIG_DU || CONFIG_LS
597 help
598 Allow df, du, and ls to have human readable output.
599
600comment "Common options for md5sum, sha1sum"
601 depends on CONFIG_MD5SUM || CONFIG_SHA1SUM
602
603config CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
604 bool " Enable -c, -s and -w options"
605 default n
606 depends on CONFIG_MD5SUM || CONFIG_SHA1SUM
607 help
608 Enabling the -c options allows files to be checked
609 against pre-calculated hash values.
610
611 -s and -w are useful options when verifying checksums.
612
613endmenu
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/coreutils
23SHELLUTILS_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include $(srcdir)/Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20COREUTILS_AR:=coreutils.a
21ifndef $(COREUTILS_DIR)
22COREUTILS_DIR:=$(top_builddir)/coreutils/
23endif
24srcdir=$(top_srcdir)/coreutils
25
26COREUTILS-y:=
27COREUTILS-$(CONFIG_BASENAME) += basename.o
28COREUTILS-$(CONFIG_CAL) += cal.o
29COREUTILS-$(CONFIG_CAT) += cat.o
30COREUTILS-$(CONFIG_CHGRP) += chgrp.o
31COREUTILS-$(CONFIG_CHMOD) += chmod.o
32COREUTILS-$(CONFIG_CHOWN) += chown.o
33COREUTILS-$(CONFIG_CHROOT) += chroot.o
34COREUTILS-$(CONFIG_CMP) += cmp.o
35COREUTILS-$(CONFIG_CP) += cp.o
36COREUTILS-$(CONFIG_CUT) += cut.o
37COREUTILS-$(CONFIG_DATE) += date.o
38COREUTILS-$(CONFIG_DD) += dd.o
39COREUTILS-$(CONFIG_DF) += df.o
40COREUTILS-$(CONFIG_DIRNAME) += dirname.o
41COREUTILS-$(CONFIG_DOS2UNIX) += dos2unix.o
42COREUTILS-$(CONFIG_DU) += du.o
43COREUTILS-$(CONFIG_ECHO) += echo.o
44COREUTILS-$(CONFIG_ENV) += env.o
45COREUTILS-$(CONFIG_EXPR) += expr.o
46COREUTILS-$(CONFIG_FALSE) += false.o
47COREUTILS-$(CONFIG_FOLD) += fold.o
48COREUTILS-$(CONFIG_HEAD) += head.o
49COREUTILS-$(CONFIG_HOSTID) += hostid.o
50COREUTILS-$(CONFIG_ID) += id.o
51COREUTILS-$(CONFIG_INSTALL) += install.o
52COREUTILS-$(CONFIG_LENGTH) += length.o
53COREUTILS-$(CONFIG_LN) += ln.o
54COREUTILS-$(CONFIG_LOGNAME) += logname.o
55COREUTILS-$(CONFIG_LS) += ls.o
56COREUTILS-$(CONFIG_MD5SUM) += md5_sha1_sum.o
57COREUTILS-$(CONFIG_MKDIR) += mkdir.o
58COREUTILS-$(CONFIG_MKFIFO) += mkfifo.o
59COREUTILS-$(CONFIG_MKNOD) += mknod.o
60COREUTILS-$(CONFIG_MV) += mv.o
61COREUTILS-$(CONFIG_OD) += od.o
62COREUTILS-$(CONFIG_PRINTF) += printf.o
63COREUTILS-$(CONFIG_PWD) += pwd.o
64COREUTILS-$(CONFIG_REALPATH) += realpath.o
65COREUTILS-$(CONFIG_RM) += rm.o
66COREUTILS-$(CONFIG_RMDIR) += rmdir.o
67COREUTILS-$(CONFIG_SEQ) += seq.o
68COREUTILS-$(CONFIG_SHA1SUM) += md5_sha1_sum.o
69COREUTILS-$(CONFIG_SLEEP) += sleep.o
70COREUTILS-$(CONFIG_SORT) += sort.o
71COREUTILS-$(CONFIG_STTY) += stty.o
72COREUTILS-$(CONFIG_SYNC) += sync.o
73COREUTILS-$(CONFIG_TAIL) += tail.o
74COREUTILS-$(CONFIG_TEE) += tee.o
75COREUTILS-$(CONFIG_TEST) += test.o
76COREUTILS-$(CONFIG_TOUCH) += touch.o
77COREUTILS-$(CONFIG_TR) += tr.o
78COREUTILS-$(CONFIG_TRUE) += true.o
79COREUTILS-$(CONFIG_TTY) += tty.o
80COREUTILS-$(CONFIG_UNAME) += uname.o
81COREUTILS-$(CONFIG_UNIQ) += uniq.o
82COREUTILS-$(CONFIG_USLEEP) += usleep.o
83COREUTILS-$(CONFIG_UUDECODE) += uudecode.o
84COREUTILS-$(CONFIG_UUENCODE) += uuencode.o
85COREUTILS-$(CONFIG_WATCH) += watch.o
86COREUTILS-$(CONFIG_WC) += wc.o
87COREUTILS-$(CONFIG_WHO) += who.o
88COREUTILS-$(CONFIG_WHOAMI) += whoami.o
89COREUTILS-$(CONFIG_YES) += yes.o
90
91libraries-y+=$(COREUTILS_DIR)$(COREUTILS_AR)
92
93$(COREUTILS_DIR)$(COREUTILS_AR): $(patsubst %,$(COREUTILS_DIR)%, $(COREUTILS-y))
94 $(AR) -ro $@ $(patsubst %,$(COREUTILS_DIR)%, $(COREUTILS-y))
95
96$(COREUTILS_DIR)%.o: $(srcdir)/%.c
97 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
98
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini basename implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/basename.html */
25
26
27/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
28 *
29 * Changes:
30 * 1) Now checks for too many args. Need at least one and at most two.
31 * 2) Don't check for options, as per SUSv3.
32 * 3) Save some space by using strcmp(). Calling strncmp() here was silly.
33 */
34
35#include <stdlib.h>
36#include <stdio.h>
37#include <string.h>
38#include "busybox.h"
39
40extern int basename_main(int argc, char **argv)
41{
42 size_t m, n;
43 char *s;
44
45 if (((unsigned int)(argc-2)) >= 2) {
46 bb_show_usage();
47 }
48
49 s = bb_get_last_path_component(*++argv);
50
51 if (*++argv) {
52 n = strlen(*argv);
53 m = strlen(s);
54 if ((m > n) && ((strcmp)(s+m-n, *argv) == 0)) {
55 s[m-n] = '\0';
56 }
57 }
58
59 puts(s);
60
61 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
62}
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 @@
1/*
2 * Calendar implementation for busybox
3 *
4 * See original copyright at the end of this file
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20*/
21
22/* BB_AUDIT SUSv3 compliant with -j and -y extensions (from util-linux). */
23/* BB_AUDIT BUG: The output of 'cal -j 1752' is incorrect. The upstream
24 * BB_AUDIT BUG: version in util-linux seems to be broken as well. */
25/* http://www.opengroup.org/onlinepubs/007904975/utilities/cal.html */
26
27/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
28 *
29 * Major size reduction... over 50% (>1.5k) on i386.
30 */
31
32#include <sys/types.h>
33#include <ctype.h>
34#include <err.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <time.h>
39#include <unistd.h>
40
41#include "busybox.h"
42
43#ifdef CONFIG_LOCALE_SUPPORT
44#include <locale.h>
45#endif
46
47#define THURSDAY 4 /* for reformation */
48#define SATURDAY 6 /* 1 Jan 1 was a Saturday */
49
50#define FIRST_MISSING_DAY 639787 /* 3 Sep 1752 */
51#define NUMBER_MISSING_DAYS 11 /* 11 day correction */
52
53#define MAXDAYS 42 /* max slots in a month array */
54#define SPACE -1 /* used in day array */
55
56static const char days_in_month[] = {
57 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
58};
59
60static const char sep1752[] = {
61 1, 2, 14, 15, 16,
62 17, 18, 19, 20, 21, 22, 23,
63 24, 25, 26, 27, 28, 29, 30
64};
65
66static int julian;
67
68/* leap year -- account for Gregorian reformation in 1752 */
69#define leap_year(yr) \
70 ((yr) <= 1752 ? !((yr) % 4) : \
71 (!((yr) % 4) && ((yr) % 100)) || !((yr) % 400))
72
73static int is_leap_year(int year)
74{
75 return leap_year(year);
76}
77#undef leap_year
78#define leap_year(yr) is_leap_year(yr)
79
80/* number of centuries since 1700, not inclusive */
81#define centuries_since_1700(yr) \
82 ((yr) > 1700 ? (yr) / 100 - 17 : 0)
83
84/* number of centuries since 1700 whose modulo of 400 is 0 */
85#define quad_centuries_since_1700(yr) \
86 ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
87
88/* number of leap years between year 1 and this year, not inclusive */
89#define leap_years_since_year_1(yr) \
90 ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
91
92static void center __P((char *, int, int));
93static void day_array __P((int, int, int *));
94static void trim_trailing_spaces_and_print __P((char *));
95
96static void blank_string(char *buf, size_t buflen);
97static char *build_row(char *p, int *dp);
98
99#define DAY_LEN 3 /* 3 spaces per day */
100#define J_DAY_LEN (DAY_LEN + 1)
101#define WEEK_LEN 20 /* 7 * 3 - one space at the end */
102#define J_WEEK_LEN (WEEK_LEN + 7)
103#define HEAD_SEP 2 /* spaces between day headings */
104
105int cal_main(int argc, char **argv)
106{
107 struct tm *local_time;
108 struct tm zero_tm;
109 time_t now;
110 int month, year, flags, i;
111 char *month_names[12];
112 char day_headings[28]; /* 28 for julian, 21 for nonjulian */
113 char buf[40];
114
115#ifdef CONFIG_LOCALE_SUPPORT
116 setlocale(LC_TIME, "");
117#endif
118
119 flags = bb_getopt_ulflags(argc, argv, "jy");
120
121 julian = flags & 1;
122
123 argv += optind;
124
125 month = 0;
126
127 if ((argc -= optind) > 2) {
128 bb_show_usage();
129 }
130
131 if (!argc) {
132 time(&now);
133 local_time = localtime(&now);
134 year = local_time->tm_year + 1900;
135 if (!(flags & 2)) {
136 month = local_time->tm_mon + 1;
137 }
138 } else {
139 if (argc == 2) {
140 month = bb_xgetularg10_bnd(*argv++, 1, 12);
141 }
142 year = bb_xgetularg10_bnd(*argv, 1, 9999);
143 }
144
145 blank_string(day_headings, sizeof(day_headings) - 7 + 7*julian);
146
147 i = 0;
148 do {
149 zero_tm.tm_mon = i;
150 strftime(buf, sizeof(buf), "%B", &zero_tm);
151 month_names[i] = bb_xstrdup(buf);
152
153 if (i < 7) {
154 zero_tm.tm_wday = i;
155 strftime(buf, sizeof(buf), "%a", &zero_tm);
156 strncpy(day_headings + i * (3+julian) + julian, buf, 2);
157 }
158 } while (++i < 12);
159
160 if (month) {
161 int row, len, days[MAXDAYS];
162 int *dp = days;
163 char lineout[30];
164
165 day_array(month, year, dp);
166 len = sprintf(lineout, "%s %d", month_names[month - 1], year);
167 bb_printf("%*s%s\n%s\n",
168 ((7*julian + WEEK_LEN) - len) / 2, "",
169 lineout, day_headings);
170 for (row = 0; row < 6; row++) {
171 build_row(lineout, dp)[0] = '\0';
172 dp += 7;
173 trim_trailing_spaces_and_print(lineout);
174 }
175 } else {
176 int row, which_cal, week_len, days[12][MAXDAYS];
177 int *dp;
178 char lineout[80];
179
180 sprintf(lineout, "%d", year);
181 center(lineout,
182 (WEEK_LEN * 3 + HEAD_SEP * 2)
183 + julian * (J_WEEK_LEN * 2 + HEAD_SEP
184 - (WEEK_LEN * 3 + HEAD_SEP * 2)),
185 0);
186 puts("\n"); /* two \n's */
187 for (i = 0; i < 12; i++) {
188 day_array(i + 1, year, days[i]);
189 }
190 blank_string(lineout, sizeof(lineout));
191 week_len = WEEK_LEN + julian * (J_WEEK_LEN - WEEK_LEN);
192 for (month = 0; month < 12; month += 3-julian) {
193 center(month_names[month], week_len, HEAD_SEP);
194 if (!julian) {
195 center(month_names[month + 1], week_len, HEAD_SEP);
196 }
197 center(month_names[month + 2 - julian], week_len, 0);
198 bb_printf("\n%s%*s%s", day_headings, HEAD_SEP, "", day_headings);
199 if (!julian) {
200 bb_printf("%*s%s", HEAD_SEP, "", day_headings);
201 }
202 putchar('\n');
203 for (row = 0; row < (6*7); row += 7) {
204 for (which_cal = 0; which_cal < 3-julian; which_cal++) {
205 dp = days[month + which_cal] + row;
206 build_row(lineout + which_cal * (week_len + 2), dp);
207 }
208 /* blank_string took care of nul termination. */
209 trim_trailing_spaces_and_print(lineout);
210 }
211 }
212 }
213
214 bb_fflush_stdout_and_exit(0);
215}
216
217/*
218 * day_array --
219 * Fill in an array of 42 integers with a calendar. Assume for a moment
220 * that you took the (maximum) 6 rows in a calendar and stretched them
221 * out end to end. You would have 42 numbers or spaces. This routine
222 * builds that array for any month from Jan. 1 through Dec. 9999.
223 */
224static void day_array(int month, int year, int *days)
225{
226 long temp;
227 int i;
228 int j_offset;
229 int day, dw, dm;
230
231 memset(days, SPACE, MAXDAYS * sizeof(int));
232
233 if ((month == 9) && (year == 1752)) {
234 j_offset = julian * 244;
235 i = 0;
236 do {
237 days[i+2] = sep1752[i] + j_offset;
238 } while (++i < sizeof(sep1752));
239
240 return;
241 }
242
243 /* day_in_year
244 * return the 1 based day number within the year
245 */
246 day = 1;
247 if ((month > 2) && leap_year(year)) {
248 ++day;
249 }
250
251 i = month;
252 while (i) {
253 day += days_in_month[--i];
254 }
255
256 /* day_in_week
257 * return the 0 based day number for any date from 1 Jan. 1 to
258 * 31 Dec. 9999. Assumes the Gregorian reformation eliminates
259 * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
260 * missing days.
261 */
262 dw = THURSDAY;
263 temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
264 + day;
265 if (temp < FIRST_MISSING_DAY) {
266 dw = ((temp - 1 + SATURDAY) % 7);
267 } else if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS)) {
268 dw = (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
269 }
270
271 if (!julian) {
272 day = 1;
273 }
274
275 dm = days_in_month[month];
276 if ((month == 2) && leap_year(year)) {
277 ++dm;
278 }
279
280 while (dm) {
281 days[dw++] = day++;
282 --dm;
283 }
284}
285
286static void trim_trailing_spaces_and_print(char *s)
287{
288 char *p = s;
289
290 while (*p) {
291 ++p;
292 }
293 while (p > s) {
294 --p;
295 if (!(isspace)(*p)) { /* We want the function... not the inline. */
296 p[1] = '\0';
297 break;
298 }
299 }
300
301 puts(s);
302}
303
304static void center(char *str, int len, int separate)
305{
306 int n = strlen(str);
307 len -= n;
308 bb_printf("%*s%*s", (len/2) + n, str, (len/2) + (len % 2) + separate, "");
309}
310
311static void blank_string(char *buf, size_t buflen)
312{
313 memset(buf, ' ', buflen);
314 buf[buflen-1] = '\0';
315}
316
317static char *build_row(char *p, int *dp)
318{
319 int col, val, day;
320
321 memset(p, ' ', (julian + DAY_LEN) * 7);
322
323 col = 0;
324 do {
325 if ((day = *dp++) != SPACE) {
326 if (julian) {
327 ++p;
328 if (day >= 100) {
329 *p = '0';
330 p[-1] = (day / 100) + '0';
331 day %= 100;
332 }
333 }
334 if ((val = day / 10) > 0) {
335 *p = val + '0';
336 }
337 *++p = day % 10 + '0';
338 p += 2;
339 } else {
340 p += DAY_LEN + julian;
341 }
342 } while (++col < 7);
343
344 return p;
345}
346
347/*
348 * Copyright (c) 1989, 1993, 1994
349 * The Regents of the University of California. All rights reserved.
350 *
351 * This code is derived from software contributed to Berkeley by
352 * Kim Letkeman.
353 *
354 * Redistribution and use in source and binary forms, with or without
355 * modification, are permitted provided that the following conditions
356 * are met:
357 * 1. Redistributions of source code must retain the above copyright
358 * notice, this list of conditions and the following disclaimer.
359 * 2. Redistributions in binary form must reproduce the above copyright
360 * notice, this list of conditions and the following disclaimer in the
361 * documentation and/or other materials provided with the distribution.
362 * 3. Neither the name of the University nor the names of its contributors
363 * may be used to endorse or promote products derived from this software
364 * without specific prior written permission.
365 *
366 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
367 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
368 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
369 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
370 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
371 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
372 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
373 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
374 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
375 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
376 * SUCH DAMAGE.
377 */
378
379
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * cat implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/cat.html */
25
26/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
27 *
28 * This is a new implementation of 'cat' which aims to be SUSv3 compliant.
29 *
30 * Changes from the previous implementation include:
31 * 1) Multiple '-' args are accepted as required by SUSv3. The previous
32 * implementation would close stdin and segfault on a subsequent '-'.
33 * 2) The '-u' options is required by SUSv3. Note that the specified
34 * behavior for '-u' is done by default, so all we need do is accept
35 * the option.
36 */
37
38#include <stdlib.h>
39#include <stdio.h>
40#include <unistd.h>
41#include "busybox.h"
42
43extern int cat_main(int argc, char **argv)
44{
45 FILE *f;
46 int retval = EXIT_SUCCESS;
47
48 bb_getopt_ulflags(argc, argv, "u");
49
50 argv += optind;
51 if (!*argv) {
52 *--argv = "-";
53 }
54
55 do {
56 if ((f = bb_wfopen_input(*argv)) != NULL) {
57 int r = bb_copyfd_eof(fileno(f), STDOUT_FILENO);
58 bb_fclose_nonstdin(f);
59 if (r >= 0) {
60 continue;
61 }
62 }
63 retval = EXIT_FAILURE;
64 } while (*++argv);
65
66 return retval;
67}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini chgrp implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 defects - unsupported options -h, -H, -L, and -P. */
24/* BB_AUDIT GNU defects - unsupported options -h, -c, -f, -v, and long options. */
25/* BB_AUDIT Note: gnu chgrp does not support -H, -L, or -P. */
26/* http://www.opengroup.org/onlinepubs/007904975/utilities/chgrp.html */
27
28#include <stdlib.h>
29#include <unistd.h>
30#include "busybox.h"
31
32/* Don't use lchown glibc older then 2.1.x */
33#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1)
34#define lchown chown
35#endif
36
37static int fileAction(const char *fileName, struct stat *statbuf, void* junk)
38{
39 if (lchown(fileName, statbuf->st_uid, *((long *) junk)) == 0) {
40 return (TRUE);
41 }
42 bb_perror_msg("%s", fileName); /* Avoid multibyte problems. */
43 return (FALSE);
44}
45
46int chgrp_main(int argc, char **argv)
47{
48 long gid;
49 int recursiveFlag;
50 int retval = EXIT_SUCCESS;
51
52 recursiveFlag = bb_getopt_ulflags(argc, argv, "R");
53
54 if (argc - optind < 2) {
55 bb_show_usage();
56 }
57
58 argv += optind;
59
60 /* Find the selected group */
61 gid = get_ug_id(*argv, my_getgrnam);
62 ++argv;
63
64 /* Ok, ready to do the deed now */
65 do {
66 if (! recursive_action (*argv, recursiveFlag, FALSE, FALSE,
67 fileAction, fileAction, &gid)) {
68 retval = EXIT_FAILURE;
69 }
70 } while (*++argv);
71
72 return retval;
73}
74
75/*
76Local Variables:
77c-file-style: "linux"
78c-basic-offset: 4
79tab-width: 4
80End:
81*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini chmod implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Reworked by (C) 2002 Vladimir Oleynik <dzo@simtreas.ru>
8 * to correctly parse '-rwxgoa'
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25
26/* BB_AUDIT SUSv3 compliant */
27/* BB_AUDIT GNU defects - unsupported options -c, -f, -v, and long options. */
28/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <unistd.h>
34#include <sys/stat.h>
35#include "busybox.h"
36
37static int fileAction(const char *fileName, struct stat *statbuf, void* junk)
38{
39 if (!bb_parse_mode((char *)junk, &(statbuf->st_mode)))
40 bb_error_msg_and_die( "invalid mode: %s", (char *)junk);
41 if (chmod(fileName, statbuf->st_mode) == 0)
42 return (TRUE);
43 bb_perror_msg("%s", fileName); /* Avoid multibyte problems. */
44 return (FALSE);
45}
46
47int chmod_main(int argc, char **argv)
48{
49 int retval = EXIT_SUCCESS;
50 int recursiveFlag = FALSE;
51 int count;
52 char *smode;
53 char **p;
54 char *p0;
55 char opt = '-';
56
57 ++argv;
58 count = 0;
59
60 for (p = argv ; *p ; p++) {
61 p0 = p[0];
62 if (p0[0] == opt) {
63 if ((p0[1] == '-') && !p0[2]) {
64 opt = 0; /* Disable further option processing. */
65 continue;
66 }
67 if (p0[1] == 'R') {
68 char *s = p0 + 2;
69 while (*s == 'R') {
70 ++s;
71 }
72 if (*s) {
73 bb_show_usage();
74 }
75 recursiveFlag = TRUE;
76 continue;
77 }
78 if (count) {
79 bb_show_usage();
80 }
81 }
82 argv[count] = p0;
83 ++count;
84 }
85
86 argv[count] = NULL;
87
88 if (count < 2) {
89 bb_show_usage();
90 }
91
92 smode = *argv;
93 ++argv;
94
95 /* Ok, ready to do the deed now */
96 do {
97 if (! recursive_action (*argv, recursiveFlag, FALSE, FALSE,
98 fileAction, fileAction, smode)) {
99 retval = EXIT_FAILURE;
100 }
101 } while (*++argv);
102
103 return retval;
104}
105
106/*
107Local Variables:
108c-file-style: "linux"
109c-basic-offset: 4
110tab-width: 4
111End:
112*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini chown implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 defects - unsupported options -h, -H, -L, and -P. */
24/* BB_AUDIT GNU defects - unsupported options -h, -c, -f, -v, and long options. */
25/* BB_AUDIT Note: gnu chown does not support -H, -L, or -P. */
26/* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */
27
28#include <stdlib.h>
29#include <unistd.h>
30#include <string.h>
31#include "busybox.h"
32
33/* Don't use lchown for glibc older then 2.1.x */
34#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1)
35#define lchown chown
36#endif
37
38static long uid;
39static long gid;
40
41static int (*chown_func)(const char *, uid_t, gid_t) = chown;
42
43static int fileAction(const char *fileName, struct stat *statbuf, void* junk)
44{
45 if (chown_func(fileName, uid, (gid == -1) ? statbuf->st_gid : gid) == 0) {
46 chmod(fileName, statbuf->st_mode);
47 return (TRUE);
48 }
49 bb_perror_msg("%s", fileName); /* Avoid multibyte problems. */
50 return (FALSE);
51}
52
53#define FLAG_R 1
54#define FLAG_h 2
55
56int chown_main(int argc, char **argv)
57{
58 int flags;
59 int retval = EXIT_SUCCESS;
60 char *groupName;
61
62 flags = bb_getopt_ulflags(argc, argv, "Rh");
63
64 if (flags & FLAG_h) chown_func = lchown;
65
66 if (argc - optind < 2) {
67 bb_show_usage();
68 }
69
70 argv += optind;
71
72 /* First, check if there is a group name here */
73 if ((groupName = strchr(*argv, '.')) == NULL) {
74 groupName = strchr(*argv, ':');
75 }
76
77 gid = -1;
78 if (groupName) {
79 *groupName++ = '\0';
80 gid = get_ug_id(groupName, my_getgrnam);
81 }
82
83 /* Now check for the username */
84 uid = get_ug_id(*argv, my_getpwnam);
85
86 ++argv;
87
88 /* Ok, ready to do the deed now */
89 do {
90 if (! recursive_action (*argv, (flags & FLAG_R), FALSE, FALSE,
91 fileAction, fileAction, NULL)) {
92 retval = EXIT_FAILURE;
93 }
94 } while (*++argv);
95
96 return retval;
97}
98
99/*
100Local Variables:
101c-file-style: "linux"
102c-basic-offset: 4
103tab-width: 4
104End:
105*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini chroot implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
24
25#include <stdlib.h>
26#include <stdio.h>
27#include <unistd.h>
28#include <errno.h>
29#include "busybox.h"
30
31int chroot_main(int argc, char **argv)
32{
33 if (argc < 2) {
34 bb_show_usage();
35 }
36
37 ++argv;
38 if (chroot(*argv) || (chdir("/"))) {
39 bb_perror_msg_and_die("cannot change root directory to %s", *argv);
40 }
41
42 ++argv;
43 if (argc == 2) {
44 argv -= 2;
45 if (!(*argv = getenv("SHELL"))) {
46 *argv = (char *) DEFAULT_SHELL;
47 }
48 argv[1] = (char *) "-i";
49 }
50
51 execvp(*argv, argv);
52 bb_perror_msg_and_die("cannot execute %s", *argv);
53}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini cmp implementation for busybox
4 *
5 * Copyright (C) 2000,2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 (virtually) compliant -- uses nicer GNU format for -l. */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */
25
26/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
27 *
28 * Original version majorly reworked for SUSv3 compliance, bug fixes, and
29 * size optimizations. Changes include:
30 * 1) Now correctly distinguishes between errors and actual file differences.
31 * 2) Proper handling of '-' args.
32 * 3) Actual error checking of i/o.
33 * 4) Accept SUSv3 -l option. Note that we use the slightly nicer gnu format
34 * in the '-l' case.
35 */
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <unistd.h>
40#include "busybox.h"
41
42static FILE *cmp_xfopen_input(const char *filename)
43{
44 FILE *fp;
45
46 if ((fp = bb_wfopen_input(filename)) != NULL) {
47 return fp;
48 }
49
50 exit(bb_default_error_retval); /* We already output an error message. */
51}
52
53static const char fmt_eof[] = "cmp: EOF on %s\n";
54static const char fmt_differ[] = "%s %s differ: char %d, line %d\n";
55#if 0
56static const char fmt_l_opt[] = "%.0s%.0s%d %o %o\n"; /* SUSv3 format */
57#else
58static const char fmt_l_opt[] = "%.0s%.0s%d %3o %3o\n"; /* nicer gnu format */
59#endif
60
61static const char opt_chars[] = "sl";
62
63enum {
64 OPT_s = 1,
65 OPT_l = 2
66};
67
68int cmp_main(int argc, char **argv)
69{
70 FILE *fp1, *fp2, *outfile = stdout;
71 const char *filename1, *filename2;
72 const char *fmt;
73 int c1, c2, char_pos, line_pos;
74 int opt_flags;
75 int exit_val = 0;
76
77 bb_default_error_retval = 2; /* 1 is returned if files are different. */
78
79 opt_flags = bb_getopt_ulflags(argc, argv, opt_chars);
80
81 if ((opt_flags == 3) || (((unsigned int)(--argc - optind)) > 1)) {
82 bb_show_usage();
83 }
84
85 fp1 = cmp_xfopen_input(filename1 = *(argv += optind));
86
87 filename2 = "-";
88 if (*++argv) {
89 filename2 = *argv;
90 }
91 fp2 = cmp_xfopen_input(filename2);
92
93 if (fp1 == fp2) { /* Paranioa check... stdin == stdin? */
94 /* Note that we don't bother reading stdin. Neither does gnu wc.
95 * But perhaps we should, so that other apps down the chain don't
96 * get the input. Consider 'echo hello | (cmp - - && cat -)'.
97 */
98 return 0;
99 }
100
101 fmt = fmt_differ;
102 if (opt_flags == OPT_l) {
103 fmt = fmt_l_opt;
104 }
105
106 char_pos = 0;
107 line_pos = 1;
108 do {
109 c1 = getc(fp1);
110 c2 = getc(fp2);
111 ++char_pos;
112 if (c1 != c2) { /* Remember -- a read error may have occurred. */
113 exit_val = 1; /* But assume the files are different for now. */
114 if (c2 == EOF) {
115 /* We know that fp1 isn't at EOF or in an error state. But to
116 * save space below, things are setup to expect an EOF in fp1
117 * if an EOF occurred. So, swap things around.
118 */
119 fp1 = fp2;
120 filename1 = filename2;
121 c1 = c2;
122 }
123 if (c1 == EOF) {
124 bb_xferror(fp1, filename1);
125 fmt = fmt_eof; /* Well, no error, so it must really be EOF. */
126 outfile = stderr;
127 /* There may have been output to stdout (option -l), so
128 * make sure we fflush before writing to stderr. */
129 bb_xfflush_stdout();
130 }
131 if (opt_flags != OPT_s) {
132 if (opt_flags == OPT_l) {
133 line_pos = c1; /* line_pos is unused in the -l case. */
134 }
135 bb_fprintf(outfile, fmt, filename1, filename2, char_pos, line_pos, c2);
136 if (opt_flags) { /* This must be -l since not -s. */
137 /* If we encountered and EOF, the while check will catch it. */
138 continue;
139 }
140 }
141 break;
142 }
143 if (c1 == '\n') {
144 ++line_pos;
145 }
146 } while (c1 != EOF);
147
148 bb_xferror(fp1, filename1);
149 bb_xferror(fp2, filename2);
150
151 bb_fflush_stdout_and_exit(exit_val);
152}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini cp implementation for busybox
4 *
5 * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 defects - unsupported options -H, -L, and -P. */
24/* BB_AUDIT GNU defects - only extension options supported are -a and -d. */
25/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */
26
27/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
28 *
29 * Size reduction.
30 */
31
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <unistd.h>
35#include <fcntl.h>
36#include <utime.h>
37#include <errno.h>
38#include <dirent.h>
39#include <stdlib.h>
40#include <assert.h>
41#include "busybox.h"
42#include "libcoreutils/coreutils.h"
43
44/* WARNING!! ORDER IS IMPORTANT!! */
45static const char cp_opts[] = "pdRfiar";
46
47extern int cp_main(int argc, char **argv)
48{
49 struct stat source_stat;
50 struct stat dest_stat;
51 const char *last;
52 const char *dest;
53 int s_flags;
54 int d_flags;
55 int flags;
56 int status = 0;
57
58 /* Since these are enums, #if tests will not work. So use assert()s. */
59 assert(FILEUTILS_PRESERVE_STATUS == 1);
60 assert(FILEUTILS_DEREFERENCE == 2);
61 assert(FILEUTILS_RECUR == 4);
62 assert(FILEUTILS_FORCE == 8);
63 assert(FILEUTILS_INTERACTIVE == 16);
64
65 flags = bb_getopt_ulflags(argc, argv, cp_opts);
66
67 if (flags & 32) {
68 flags |= (FILEUTILS_PRESERVE_STATUS | FILEUTILS_RECUR | FILEUTILS_DEREFERENCE);
69 }
70 if (flags & 64) {
71 /* Make -r a synonym for -R,
72 * -r was marked as obsolete in SUSv3, but is included for compatability
73 */
74 flags |= FILEUTILS_RECUR;
75 }
76
77 flags ^= FILEUTILS_DEREFERENCE; /* The sense of this flag was reversed. */
78
79 if (optind + 2 > argc) {
80 bb_show_usage();
81 }
82
83 last = argv[argc - 1];
84 argv += optind;
85
86 /* If there are only two arguments and... */
87 if (optind + 2 == argc) {
88 s_flags = cp_mv_stat2(*argv, &source_stat,
89 (flags & FILEUTILS_DEREFERENCE) ? stat : lstat);
90 if ((s_flags < 0) || ((d_flags = cp_mv_stat(last, &dest_stat)) < 0)) {
91 exit(EXIT_FAILURE);
92 }
93 /* ...if neither is a directory or... */
94 if ( !((s_flags | d_flags) & 2) ||
95 /* ...recursing, the 1st is a directory, and the 2nd doesn't exist... */
96 /* ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags) */
97 /* Simplify the above since FILEUTILS_RECUR >> 1 == 2. */
98 ((((flags & FILEUTILS_RECUR) >> 1) & s_flags) && !d_flags)
99 ) {
100 /* ...do a simple copy. */
101 dest = last;
102 goto DO_COPY; /* Note: optind+2==argc implies argv[1]==last below. */
103 }
104 }
105
106 do {
107 dest = concat_path_file(last, bb_get_last_path_component(*argv));
108 DO_COPY:
109 if (copy_file(*argv, dest, flags) < 0) {
110 status = 1;
111 }
112 if (*++argv == last) {
113 break;
114 }
115 free((void *) dest);
116 } while (1);
117
118 exit(status);
119}
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 @@
1/* vi: set sw=8 ts=8: */
2/*
3 * cut.c - minimalist version of cut
4 *
5 * Copyright (C) 1999,2000,2001 by Lineo, inc.
6 * Written by Mark Whitley <markw@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <getopt.h>
27#include <unistd.h>
28#include <string.h>
29#include <limits.h>
30#include "busybox.h"
31
32
33/* option vars */
34static const char optstring[] = "b:c:f:d:sn";
35#define OPT_BYTE_FLGS 1
36#define OPT_CHAR_FLGS 2
37#define OPT_FIELDS_FLGS 4
38#define OPT_DELIM_FLGS 8
39#define OPT_SUPRESS_FLGS 16
40static char part; /* (b)yte, (c)har, (f)ields */
41static unsigned int supress_non_delimited_lines;
42static char delim = '\t'; /* delimiter, default is tab */
43
44struct cut_list {
45 int startpos;
46 int endpos;
47};
48
49static const int BOL = 0;
50static const int EOL = INT_MAX;
51static const int NON_RANGE = -1;
52
53static struct cut_list *cut_lists = NULL; /* growable array holding a series of lists */
54static unsigned int nlists = 0; /* number of elements in above list */
55
56
57static int cmpfunc(const void *a, const void *b)
58{
59 struct cut_list *la = (struct cut_list *)a;
60 struct cut_list *lb = (struct cut_list *)b;
61
62 if (la->startpos > lb->startpos)
63 return 1;
64 if (la->startpos < lb->startpos)
65 return -1;
66 return 0;
67}
68
69
70/*
71 * parse_lists() - parses a list and puts values into startpos and endpos.
72 * valid list formats: N, N-, N-M, -M
73 * more than one list can be separated by commas
74 */
75static void parse_lists(char *lists)
76{
77 char *ltok = NULL;
78 char *ntok = NULL;
79 char *junk;
80 int s = 0, e = 0;
81
82 /* take apart the lists, one by one (they are separated with commas */
83 while ((ltok = strsep(&lists, ",")) != NULL) {
84
85 /* it's actually legal to pass an empty list */
86 if (strlen(ltok) == 0)
87 continue;
88
89 /* get the start pos */
90 ntok = strsep(&ltok, "-");
91 if (ntok == NULL) {
92 fprintf(stderr, "Help ntok is null for starting position! What do I do?\n");
93 } else if (strlen(ntok) == 0) {
94 s = BOL;
95 } else {
96 s = strtoul(ntok, &junk, 10);
97 if(*junk != '\0' || s < 0)
98 bb_error_msg_and_die("invalid byte or field list");
99
100 /* account for the fact that arrays are zero based, while the user
101 * expects the first char on the line to be char # 1 */
102 if (s != 0)
103 s--;
104 }
105
106 /* get the end pos */
107 ntok = strsep(&ltok, "-");
108 if (ntok == NULL) {
109 e = NON_RANGE;
110 } else if (strlen(ntok) == 0) {
111 e = EOL;
112 } else {
113 e = strtoul(ntok, &junk, 10);
114 if(*junk != '\0' || e < 0)
115 bb_error_msg_and_die("invalid byte or field list");
116 /* if the user specified and end position of 0, that means "til the
117 * end of the line */
118 if (e == 0)
119 e = INT_MAX;
120 e--; /* again, arrays are zero based, lines are 1 based */
121 if (e == s)
122 e = NON_RANGE;
123 }
124
125 /* if there's something left to tokenize, the user past an invalid list */
126 if (ltok)
127 bb_error_msg_and_die("invalid byte or field list");
128
129 /* add the new list */
130 cut_lists = xrealloc(cut_lists, sizeof(struct cut_list) * (++nlists));
131 cut_lists[nlists-1].startpos = s;
132 cut_lists[nlists-1].endpos = e;
133 }
134
135 /* make sure we got some cut positions out of all that */
136 if (nlists == 0)
137 bb_error_msg_and_die("missing list of positions");
138
139 /* now that the lists are parsed, we need to sort them to make life easier
140 * on us when it comes time to print the chars / fields / lines */
141 qsort(cut_lists, nlists, sizeof(struct cut_list), cmpfunc);
142
143}
144
145
146static void cut_line_by_chars(const char *line)
147{
148 int c, l;
149 /* set up a list so we can keep track of what's been printed */
150 char *printed = xcalloc(strlen(line), sizeof(char));
151
152 /* print the chars specified in each cut list */
153 for (c = 0; c < nlists; c++) {
154 l = cut_lists[c].startpos;
155 while (l < strlen(line)) {
156 if (!printed[l]) {
157 putchar(line[l]);
158 printed[l] = 'X';
159 }
160 l++;
161 if (cut_lists[c].endpos == NON_RANGE || l > cut_lists[c].endpos)
162 break;
163 }
164 }
165 putchar('\n'); /* cuz we were handed a chomped line */
166 free(printed);
167}
168
169
170static void cut_line_by_fields(char *line)
171{
172 int c, f;
173 int ndelim = -1; /* zero-based / one-based problem */
174 int nfields_printed = 0;
175 char *field = NULL;
176 char d[2] = { delim, 0 };
177 char *printed;
178
179 /* test the easy case first: does this line contain any delimiters? */
180 if (strchr(line, delim) == NULL) {
181 if (!supress_non_delimited_lines)
182 puts(line);
183 return;
184 }
185
186 /* set up a list so we can keep track of what's been printed */
187 printed = xcalloc(strlen(line), sizeof(char));
188
189 /* process each list on this line, for as long as we've got a line to process */
190 for (c = 0; c < nlists && line; c++) {
191 f = cut_lists[c].startpos;
192 do {
193
194 /* find the field we're looking for */
195 while (line && ndelim < f) {
196 field = strsep(&line, d);
197 ndelim++;
198 }
199
200 /* we found it, and it hasn't been printed yet */
201 if (field && ndelim == f && !printed[ndelim]) {
202 /* if this isn't our first time through, we need to print the
203 * delimiter after the last field that was printed */
204 if (nfields_printed > 0)
205 putchar(delim);
206 fputs(field, stdout);
207 printed[ndelim] = 'X';
208 nfields_printed++;
209 }
210
211 f++;
212
213 /* keep going as long as we have a line to work with, this is a
214 * list, and we're not at the end of that list */
215 } while (line && cut_lists[c].endpos != NON_RANGE && f <= cut_lists[c].endpos);
216 }
217
218 /* if we printed anything at all, we need to finish it with a newline cuz
219 * we were handed a chomped line */
220 putchar('\n');
221
222 free(printed);
223}
224
225
226static void cut_file_by_lines(const char *line, unsigned int linenum)
227{
228 static int c = 0;
229 static int l = -1;
230
231 /* I can't initialize this above cuz the "initializer isn't
232 * constant" *sigh* */
233 if (l == -1)
234 l = cut_lists[c].startpos;
235
236 /* get out if we have no more lists to process or if the lines are lower
237 * than what we're interested in */
238 if (c >= nlists || linenum < l)
239 return;
240
241 /* if the line we're looking for is lower than the one we were passed, it
242 * means we displayed it already, so move on */
243 while (l < linenum) {
244 l++;
245 /* move on to the next list if we're at the end of this one */
246 if (cut_lists[c].endpos == NON_RANGE || l > cut_lists[c].endpos) {
247 c++;
248 /* get out if there's no more lists to process */
249 if (c >= nlists)
250 return;
251 l = cut_lists[c].startpos;
252 /* get out if the current line is lower than the one we just became
253 * interested in */
254 if (linenum < l)
255 return;
256 }
257 }
258
259 /* If we made it here, it means we've found the line we're looking for, so print it */
260 puts(line);
261}
262
263
264/*
265 * snippy-snip
266 */
267static void cut_file(FILE *file)
268{
269 char *line = NULL;
270 unsigned int linenum = 0; /* keep these zero-based to be consistent */
271
272 /* go through every line in the file */
273 while ((line = bb_get_chomped_line_from_file(file)) != NULL) {
274
275 /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */
276 if ((part & (OPT_CHAR_FLGS | OPT_BYTE_FLGS)))
277 cut_line_by_chars(line);
278
279 /* cut based on fields */
280 else {
281 if (delim == '\n')
282 cut_file_by_lines(line, linenum);
283 else
284 cut_line_by_fields(line);
285 }
286
287 linenum++;
288 free(line);
289 }
290}
291
292
293extern int cut_main(int argc, char **argv)
294{
295 unsigned long opt;
296 char *sopt, *sdopt;
297
298 bb_opt_complementaly = "b~bcf:c~bcf:f~bcf";
299 opt = bb_getopt_ulflags(argc, argv, optstring, &sopt, &sopt, &sopt, &sdopt);
300 part = opt & (OPT_BYTE_FLGS|OPT_CHAR_FLGS|OPT_FIELDS_FLGS);
301 if(part == 0)
302 bb_error_msg_and_die("you must specify a list of bytes, characters, or fields");
303 if(opt & 0x80000000UL)
304 bb_error_msg_and_die("only one type of list may be specified");
305 parse_lists(sopt);
306 if((opt & (OPT_DELIM_FLGS))) {
307 if (strlen(sdopt) > 1) {
308 bb_error_msg_and_die("the delimiter must be a single character");
309 }
310 delim = sdopt[0];
311 }
312 supress_non_delimited_lines = opt & OPT_SUPRESS_FLGS;
313
314 /* non-field (char or byte) cutting has some special handling */
315 if (part != OPT_FIELDS_FLGS) {
316 if (supress_non_delimited_lines) {
317 bb_error_msg_and_die("suppressing non-delimited lines makes sense"
318 " only when operating on fields");
319 }
320 if (delim != '\t') {
321 bb_error_msg_and_die("a delimiter may be specified only when operating on fields");
322 }
323 }
324
325 /* argv[(optind)..(argc-1)] should be names of file to process. If no
326 * files were specified or '-' was specified, take input from stdin.
327 * Otherwise, we process all the files specified. */
328 if (argv[optind] == NULL || (strcmp(argv[optind], "-") == 0)) {
329 cut_file(stdin);
330 }
331 else {
332 int i;
333 FILE *file;
334 for (i = optind; i < argc; i++) {
335 file = bb_wfopen(argv[i], "r");
336 if(file) {
337 cut_file(file);
338 fclose(file);
339 }
340 }
341 }
342
343 return EXIT_SUCCESS;
344}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini date implementation for busybox
4 *
5 * by Matthew Grant <grantma@anathoth.gen.nz>
6 *
7 * iso-format handling added by Robert Griebl <griebl@gmx.de>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23*/
24
25#include <stdlib.h>
26#include <errno.h>
27#include <sys/time.h>
28#include <unistd.h>
29#include <time.h>
30#include <stdio.h>
31#include <string.h>
32#include <getopt.h>
33#include "busybox.h"
34
35
36/* This 'date' command supports only 2 time setting formats,
37 all the GNU strftime stuff (its in libc, lets use it),
38 setting time using UTC and displaying int, as well as
39 an RFC 822 complient date output for shell scripting
40 mail commands */
41
42/* Input parsing code is always bulky - used heavy duty libc stuff as
43 much as possible, missed out a lot of bounds checking */
44
45/* Default input handling to save surprising some people */
46
47static struct tm *date_conv_time(struct tm *tm_time, const char *t_string)
48{
49 int nr;
50 char *cp;
51
52 nr = sscanf(t_string, "%2d%2d%2d%2d%d", &(tm_time->tm_mon),
53 &(tm_time->tm_mday), &(tm_time->tm_hour), &(tm_time->tm_min),
54 &(tm_time->tm_year));
55
56 if (nr < 4 || nr > 5) {
57 bb_error_msg_and_die(bb_msg_invalid_date, t_string);
58 }
59
60 cp = strchr(t_string, '.');
61 if (cp) {
62 nr = sscanf(cp + 1, "%2d", &(tm_time->tm_sec));
63 if (nr != 1) {
64 bb_error_msg_and_die(bb_msg_invalid_date, t_string);
65 }
66 }
67
68 /* correct for century - minor Y2K problem here? */
69 if (tm_time->tm_year >= 1900) {
70 tm_time->tm_year -= 1900;
71 }
72 /* adjust date */
73 tm_time->tm_mon -= 1;
74
75 return (tm_time);
76
77}
78
79
80/* The new stuff for LRP */
81
82static struct tm *date_conv_ftime(struct tm *tm_time, const char *t_string)
83{
84 struct tm t;
85
86 /* Parse input and assign appropriately to tm_time */
87
88 if (t =
89 *tm_time, sscanf(t_string, "%d:%d:%d", &t.tm_hour, &t.tm_min,
90 &t.tm_sec) == 3) {
91 /* no adjustments needed */
92 } else if (t =
93 *tm_time, sscanf(t_string, "%d:%d", &t.tm_hour,
94 &t.tm_min) == 2) {
95 /* no adjustments needed */
96 } else if (t =
97 *tm_time, sscanf(t_string, "%d.%d-%d:%d:%d", &t.tm_mon,
98 &t.tm_mday, &t.tm_hour, &t.tm_min,
99 &t.tm_sec) == 5) {
100 /* Adjust dates from 1-12 to 0-11 */
101 t.tm_mon -= 1;
102 } else if (t =
103 *tm_time, sscanf(t_string, "%d.%d-%d:%d", &t.tm_mon,
104 &t.tm_mday, &t.tm_hour, &t.tm_min) == 4) {
105 /* Adjust dates from 1-12 to 0-11 */
106 t.tm_mon -= 1;
107 } else if (t =
108 *tm_time, sscanf(t_string, "%d.%d.%d-%d:%d:%d", &t.tm_year,
109 &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min,
110 &t.tm_sec) == 6) {
111 t.tm_year -= 1900; /* Adjust years */
112 t.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
113 } else if (t =
114 *tm_time, sscanf(t_string, "%d.%d.%d-%d:%d", &t.tm_year,
115 &t.tm_mon, &t.tm_mday, &t.tm_hour,
116 &t.tm_min) == 5) {
117 t.tm_year -= 1900; /* Adjust years */
118 t.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
119 } else {
120 bb_error_msg_and_die(bb_msg_invalid_date, t_string);
121 }
122 *tm_time = t;
123 return (tm_time);
124}
125
126#define DATE_OPT_RFC2822 0x01
127#define DATE_OPT_SET 0x02
128#define DATE_OPT_UTC 0x04
129#define DATE_OPT_DATE 0x08
130#define DATE_OPT_REFERENCE 0x10
131#ifdef CONFIG_FEATURE_DATE_ISOFMT
132# define DATE_OPT_TIMESPEC 0x20
133#endif
134
135int date_main(int argc, char **argv)
136{
137 char *date_str = NULL;
138 char *date_fmt = NULL;
139 char *t_buff;
140 int set_time;
141 int utc;
142 int use_arg = 0;
143 time_t tm;
144 unsigned long opt;
145 struct tm tm_time;
146 char *filename = NULL;
147
148#ifdef CONFIG_FEATURE_DATE_ISOFMT
149 int ifmt = 0;
150 char *isofmt_arg;
151
152# define GETOPT_ISOFMT "I::"
153#else
154# define GETOPT_ISOFMT
155#endif
156 bb_opt_complementaly = "d~ds:s~ds";
157 opt = bb_getopt_ulflags(argc, argv, "Rs:ud:r:" GETOPT_ISOFMT,
158 &date_str, &date_str, &filename
159#ifdef CONFIG_FEATURE_DATE_ISOFMT
160 , &isofmt_arg
161#endif
162 );
163 set_time = opt & DATE_OPT_SET;
164 utc = opt & DATE_OPT_UTC;
165 if ((utc) && (putenv("TZ=UTC0") != 0)) {
166 bb_error_msg_and_die(bb_msg_memory_exhausted);
167 }
168 use_arg = opt & DATE_OPT_DATE;
169 if(opt & 0x80000000UL)
170 bb_show_usage();
171#ifdef CONFIG_FEATURE_DATE_ISOFMT
172 if(opt & DATE_OPT_TIMESPEC) {
173 if (!isofmt_arg) {
174 ifmt = 1;
175 } else {
176 int ifmt_len = bb_strlen(isofmt_arg);
177
178 if ((ifmt_len <= 4)
179 && (strncmp(isofmt_arg, "date", ifmt_len) == 0)) {
180 ifmt = 1;
181 } else if ((ifmt_len <= 5)
182 && (strncmp(isofmt_arg, "hours", ifmt_len) == 0)) {
183 ifmt = 2;
184 } else if ((ifmt_len <= 7)
185 && (strncmp(isofmt_arg, "minutes", ifmt_len) == 0)) {
186 ifmt = 3;
187 } else if ((ifmt_len <= 7)
188 && (strncmp(isofmt_arg, "seconds", ifmt_len) == 0)) {
189 ifmt = 4;
190 }
191 }
192 if (!ifmt) {
193 bb_show_usage();
194 }
195 }
196#endif
197
198 if ((date_fmt == NULL) && (optind < argc) && (argv[optind][0] == '+')) {
199 date_fmt = &argv[optind][1]; /* Skip over the '+' */
200 } else if (date_str == NULL) {
201 set_time = 1;
202 date_str = argv[optind];
203 }
204
205 /* Now we have parsed all the information except the date format
206 which depends on whether the clock is being set or read */
207
208 if(filename) {
209 struct stat statbuf;
210 if(stat(filename,&statbuf))
211 bb_perror_msg_and_die("File '%s' not found.\n",filename);
212 tm=statbuf.st_mtime;
213 } else time(&tm);
214 memcpy(&tm_time, localtime(&tm), sizeof(tm_time));
215 /* Zero out fields - take her back to midnight! */
216 if (date_str != NULL) {
217 tm_time.tm_sec = 0;
218 tm_time.tm_min = 0;
219 tm_time.tm_hour = 0;
220
221 /* Process any date input to UNIX time since 1 Jan 1970 */
222 if (strchr(date_str, ':') != NULL) {
223 date_conv_ftime(&tm_time, date_str);
224 } else {
225 date_conv_time(&tm_time, date_str);
226 }
227
228 /* Correct any day of week and day of year etc. fields */
229 tm_time.tm_isdst = -1; /* Be sure to recheck dst. */
230 tm = mktime(&tm_time);
231 if (tm < 0) {
232 bb_error_msg_and_die(bb_msg_invalid_date, date_str);
233 }
234 if (utc && (putenv("TZ=UTC0") != 0)) {
235 bb_error_msg_and_die(bb_msg_memory_exhausted);
236 }
237
238 /* if setting time, set it */
239 if (set_time && (stime(&tm) < 0)) {
240 bb_perror_msg("cannot set date");
241 }
242 }
243
244 /* Display output */
245
246 /* Deal with format string */
247 if (date_fmt == NULL) {
248#ifdef CONFIG_FEATURE_DATE_ISOFMT
249 switch (ifmt) {
250 case 4:
251 date_fmt = utc ? "%Y-%m-%dT%H:%M:%SZ" : "%Y-%m-%dT%H:%M:%S%z";
252 break;
253 case 3:
254 date_fmt = utc ? "%Y-%m-%dT%H:%MZ" : "%Y-%m-%dT%H:%M%z";
255 break;
256 case 2:
257 date_fmt = utc ? "%Y-%m-%dT%HZ" : "%Y-%m-%dT%H%z";
258 break;
259 case 1:
260 date_fmt = "%Y-%m-%d";
261 break;
262 case 0:
263 default:
264#endif
265 date_fmt = (opt & DATE_OPT_RFC2822 ?
266 (utc ? "%a, %d %b %Y %H:%M:%S GMT" :
267 "%a, %d %b %Y %H:%M:%S %z") :
268 "%a %b %e %H:%M:%S %Z %Y");
269
270#ifdef CONFIG_FEATURE_DATE_ISOFMT
271 break;
272 }
273#endif
274 } else if (*date_fmt == '\0') {
275 /* Imitate what GNU 'date' does with NO format string! */
276 printf("\n");
277 return EXIT_SUCCESS;
278 }
279
280 /* Handle special conversions */
281
282 if (strncmp(date_fmt, "%f", 2) == 0) {
283 date_fmt = "%Y.%m.%d-%H:%M:%S";
284 }
285
286 /* Print OUTPUT (after ALL that!) */
287 t_buff = xmalloc(201);
288 strftime(t_buff, 200, date_fmt, &tm_time);
289 puts(t_buff);
290
291 return EXIT_SUCCESS;
292}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini dd implementation for busybox
4 *
5 *
6 * Copyright (C) 2000,2001 Matt Kraai
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <stdlib.h>
27#include <stdio.h>
28#include <unistd.h>
29#include <string.h>
30#include <fcntl.h>
31#include "busybox.h"
32
33
34static const struct suffix_mult dd_suffixes[] = {
35 { "c", 1 },
36 { "w", 2 },
37 { "b", 512 },
38 { "kD", 1000 },
39 { "k", 1024 },
40 { "MD", 1000000 },
41 { "M", 1048576 },
42 { "GD", 1000000000 },
43 { "G", 1073741824 },
44 { NULL, 0 }
45};
46
47int dd_main(int argc, char **argv)
48{
49 size_t out_full = 0;
50 size_t out_part = 0;
51 size_t in_full = 0;
52 size_t in_part = 0;
53 size_t count = -1;
54 size_t bs = 512;
55 ssize_t n;
56 off_t seek = 0;
57 off_t skip = 0;
58 int sync_flag = FALSE;
59 int noerror = FALSE;
60 int trunc_flag = TRUE;
61 int oflag;
62 int ifd;
63 int ofd;
64 int i;
65 const char *infile = NULL;
66 const char *outfile = NULL;
67 char *buf;
68
69 for (i = 1; i < argc; i++) {
70 if (strncmp("bs=", argv[i], 3) == 0)
71 bs = bb_xparse_number(argv[i]+3, dd_suffixes);
72 else if (strncmp("count=", argv[i], 6) == 0)
73 count = bb_xparse_number(argv[i]+6, dd_suffixes);
74 else if (strncmp("seek=", argv[i], 5) == 0)
75 seek = bb_xparse_number(argv[i]+5, dd_suffixes);
76 else if (strncmp("skip=", argv[i], 5) == 0)
77 skip = bb_xparse_number(argv[i]+5, dd_suffixes);
78 else if (strncmp("if=", argv[i], 3) == 0)
79 infile = argv[i]+3;
80 else if (strncmp("of=", argv[i], 3) == 0)
81 outfile = argv[i]+3;
82 else if (strncmp("conv=", argv[i], 5) == 0) {
83 buf = argv[i]+5;
84 while (1) {
85 if (strncmp("notrunc", buf, 7) == 0) {
86 trunc_flag = FALSE;
87 buf += 7;
88 } else if (strncmp("sync", buf, 4) == 0) {
89 sync_flag = TRUE;
90 buf += 4;
91 } else if (strncmp("noerror", buf, 7) == 0) {
92 noerror = TRUE;
93 buf += 7;
94 } else {
95 bb_error_msg_and_die("invalid conversion `%s'", argv[i]+5);
96 }
97 if (buf[0] == '\0')
98 break;
99 if (buf[0] == ',')
100 buf++;
101 }
102 } else
103 bb_show_usage();
104 }
105
106 buf = xmalloc(bs);
107
108 if (infile != NULL) {
109 ifd = bb_xopen(infile, O_RDONLY);
110 } else {
111 ifd = STDIN_FILENO;
112 infile = bb_msg_standard_input;
113 }
114
115 if (outfile != NULL) {
116 oflag = O_WRONLY | O_CREAT;
117
118 if (!seek && trunc_flag) {
119 oflag |= O_TRUNC;
120 }
121
122 if ((ofd = open(outfile, oflag, 0666)) < 0) {
123 bb_perror_msg_and_die("%s", outfile);
124 }
125
126 if (seek && trunc_flag) {
127 if (ftruncate(ofd, seek * bs) < 0) {
128 struct stat st;
129
130 if (fstat (ofd, &st) < 0 || S_ISREG (st.st_mode) ||
131 S_ISDIR (st.st_mode)) {
132 bb_perror_msg_and_die("%s", outfile);
133 }
134 }
135 }
136 } else {
137 ofd = STDOUT_FILENO;
138 outfile = bb_msg_standard_output;
139 }
140
141 if (skip) {
142 if (lseek(ifd, skip * bs, SEEK_CUR) < 0) {
143 bb_perror_msg_and_die("%s", infile);
144 }
145 }
146
147 if (seek) {
148 if (lseek(ofd, seek * bs, SEEK_CUR) < 0) {
149 bb_perror_msg_and_die("%s", outfile);
150 }
151 }
152
153 while (in_full + in_part != count) {
154 if (noerror) {
155 /* Pre-zero the buffer when doing the noerror thing */
156 memset(buf, '\0', bs);
157 }
158 n = safe_read(ifd, buf, bs);
159 if (n < 0) {
160 if (noerror) {
161 n = bs;
162 bb_perror_msg("%s", infile);
163 } else {
164 bb_perror_msg_and_die("%s", infile);
165 }
166 }
167 if (n == 0) {
168 break;
169 }
170 if (n == bs) {
171 in_full++;
172 } else {
173 in_part++;
174 }
175 if (sync_flag) {
176 memset(buf + n, '\0', bs - n);
177 n = bs;
178 }
179 n = bb_full_write(ofd, buf, n);
180 if (n < 0) {
181 bb_perror_msg_and_die("%s", outfile);
182 }
183 if (n == bs) {
184 out_full++;
185 } else {
186 out_part++;
187 }
188 }
189
190 if (close (ifd) < 0) {
191 bb_perror_msg_and_die("%s", infile);
192 }
193
194 if (close (ofd) < 0) {
195 bb_perror_msg_and_die("%s", outfile);
196 }
197
198 fprintf(stderr, "%ld+%ld records in\n%ld+%ld records out\n",
199 (long)in_full, (long)in_part,
200 (long)out_full, (long)out_part);
201
202 return EXIT_SUCCESS;
203}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini df implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 * based on original code by (I think) Bruce Perens <bruce@pixar.com>.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24/* BB_AUDIT SUSv3 _NOT_ compliant -- options -P and -t missing. Also blocksize. */
25/* http://www.opengroup.org/onlinepubs/007904975/utilities/df.html */
26
27/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
28 *
29 * Size reduction. Removed floating point dependency. Added error checking
30 * on output. Output stats on 0-sized filesystems if specifically listed on
31 * the command line. Properly round *-blocks, Used, and Available quantities.
32 */
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <unistd.h>
38#include <mntent.h>
39#include <sys/vfs.h>
40#include "busybox.h"
41
42#ifndef CONFIG_FEATURE_HUMAN_READABLE
43static long kscale(long b, long bs)
44{
45 return ( b * (long long) bs + KILOBYTE/2 ) / KILOBYTE;
46}
47#endif
48
49extern int df_main(int argc, char **argv)
50{
51 long blocks_used;
52 long blocks_percent_used;
53#ifdef CONFIG_FEATURE_HUMAN_READABLE
54 unsigned long df_disp_hr = KILOBYTE;
55#endif
56 int status = EXIT_SUCCESS;
57 unsigned long opt;
58 FILE *mount_table;
59 struct mntent *mount_entry;
60 struct statfs s;
61 static const char hdr_1k[] = "1k-blocks"; /* default display is kilobytes */
62 const char *disp_units_hdr = hdr_1k;
63
64#ifdef CONFIG_FEATURE_HUMAN_READABLE
65 bb_opt_complementaly = "h-km:k-hm:m-hk";
66 opt = bb_getopt_ulflags(argc, argv, "hmk");
67 if(opt & 1) {
68 df_disp_hr = 0;
69 disp_units_hdr = " Size";
70 }
71 if(opt & 2) {
72 df_disp_hr = MEGABYTE;
73 disp_units_hdr = "1M-blocks";
74 }
75#else
76 opt = bb_getopt_ulflags(argc, argv, "k");
77#endif
78
79 bb_printf("Filesystem%11s%-15sUsed Available Use%% Mounted on\n",
80 "", disp_units_hdr);
81
82 mount_table = NULL;
83 argv += optind;
84 if (optind >= argc) {
85 if (!(mount_table = setmntent(bb_path_mtab_file, "r"))) {
86 bb_perror_msg_and_die(bb_path_mtab_file);
87 }
88 }
89
90 do {
91 const char *device;
92 const char *mount_point;
93
94 if (mount_table) {
95 if (!(mount_entry = getmntent(mount_table))) {
96 endmntent(mount_table);
97 break;
98 }
99 } else {
100 if (!(mount_point = *argv++)) {
101 break;
102 }
103 if (!(mount_entry = find_mount_point(mount_point, bb_path_mtab_file))) {
104 bb_error_msg("%s: can't find mount point.", mount_point);
105 SET_ERROR:
106 status = EXIT_FAILURE;
107 continue;
108 }
109 }
110
111 device = mount_entry->mnt_fsname;
112 mount_point = mount_entry->mnt_dir;
113
114 if (statfs(mount_point, &s) != 0) {
115 bb_perror_msg("%s", mount_point);
116 goto SET_ERROR;
117 }
118
119 if ((s.f_blocks > 0) || !mount_table){
120 blocks_used = s.f_blocks - s.f_bfree;
121 blocks_percent_used = 0;
122 if (blocks_used + s.f_bavail) {
123 blocks_percent_used = (((long long) blocks_used) * 100
124 + (blocks_used + s.f_bavail)/2
125 ) / (blocks_used + s.f_bavail);
126 }
127
128 if (strcmp(device, "rootfs") == 0) {
129 continue;
130 } else if (strcmp(device, "/dev/root") == 0) {
131 /* Adjusts device to be the real root device,
132 * or leaves device alone if it can't find it */
133 if ((device = find_real_root_device_name()) == NULL) {
134 goto SET_ERROR;
135 }
136 }
137
138#ifdef CONFIG_FEATURE_HUMAN_READABLE
139 bb_printf("%-21s%9s ", device,
140 make_human_readable_str(s.f_blocks, s.f_bsize, df_disp_hr));
141
142 bb_printf("%9s ",
143 make_human_readable_str( (s.f_blocks - s.f_bfree),
144 s.f_bsize, df_disp_hr));
145
146 bb_printf("%9s %3ld%% %s\n",
147 make_human_readable_str(s.f_bavail, s.f_bsize, df_disp_hr),
148 blocks_percent_used, mount_point);
149#else
150 bb_printf("%-21s%9ld %9ld %9ld %3ld%% %s\n",
151 device,
152 kscale(s.f_blocks, s.f_bsize),
153 kscale(s.f_blocks-s.f_bfree, s.f_bsize),
154 kscale(s.f_bavail, s.f_bsize),
155 blocks_percent_used, mount_point);
156#endif
157 }
158
159 } while (1);
160
161 bb_fflush_stdout_and_exit(status);
162}
163
164/*
165Local Variables:
166c-file-style: "linux"
167c-basic-offset: 4
168tab-width: 4
169End:
170*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini dirname implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/dirname.html */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include "busybox.h"
29
30extern int dirname_main(int argc, char **argv)
31{
32 if (argc != 2) {
33 bb_show_usage();
34 }
35
36 puts(dirname(argv[1]));
37
38 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
39}
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 @@
1/*
2 * dos2unix for BusyBox
3 *
4 * dos2unix '\n' convertor 0.5.0
5 * based on Unix2Dos 0.9.0 by Peter Hanecak (made 19.2.1997)
6 * Copyright 1997,.. by Peter Hanecak <hanecak@megaloman.sk>.
7 * All rights reserved.
8 *
9 * dos2unix filters reading input from stdin and writing output to stdout.
10 * Without arguments it reverts the format (e.i. if source is in UNIX format,
11 * output is in DOS format and vice versa).
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 *
27 * See the COPYING file for license information.
28 */
29
30#include <string.h>
31#include <getopt.h>
32#include <unistd.h>
33#include <stdint.h>
34#include <fcntl.h>
35#include <sys/time.h>
36#include "busybox.h"
37
38#define CT_AUTO 0
39#define CT_UNIX2DOS 1
40#define CT_DOS2UNIX 2
41
42/* We are making a lame pseudo-random string generator here. in
43 * convert(), each pass through the while loop will add more and more
44 * stuff into value, which is _supposed_ to wrap. We don't care about
45 * it being accurate. We care about it being messy, since we then mod
46 * it by the sizeof(letters) and then use that as an index into letters
47 * to pick a random letter to add to out temporary file. */
48typedef unsigned long int bb_uint64_t;
49
50static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
51
52// if fn is NULL then input is stdin and output is stdout
53static int convert(char *fn, int ConvType)
54{
55 int c, fd;
56 struct timeval tv;
57 char tempFn[BUFSIZ];
58 static bb_uint64_t value=0;
59 FILE *in = stdin, *out = stdout;
60
61 if (fn != NULL) {
62 in = bb_xfopen(fn, "rw");
63 safe_strncpy(tempFn, fn, sizeof(tempFn));
64 c = strlen(tempFn);
65 tempFn[c] = '.';
66 while(1) {
67 /* tempFn is BUFSIZ so the last addressable spot it BUFSIZ-1.
68 * The loop increments by 2. So this must check for BUFSIZ-3. */
69 if (c >=BUFSIZ-3)
70 bb_error_msg_and_die("unique name not found");
71 /* Get some semi random stuff to try and make a
72 * random filename based (and in the same dir as)
73 * the input file... */
74 gettimeofday (&tv, NULL);
75 value += ((bb_uint64_t) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
76 tempFn[++c] = letters[value % 62];
77 tempFn[c+1] = '\0';
78 value /= 62;
79
80 if ((fd = open(tempFn, O_RDWR | O_CREAT | O_EXCL, 0600)) < 0 ) {
81 continue;
82 }
83 out = fdopen(fd, "w+");
84 if (!out) {
85 close(fd);
86 remove(tempFn);
87 continue;
88 }
89 break;
90 }
91 }
92
93 while ((c = fgetc(in)) != EOF) {
94 if (c == '\r') {
95 if ((ConvType == CT_UNIX2DOS) && (fn != NULL)) {
96 // file is alredy in DOS format so it is not necessery to touch it
97 remove(tempFn);
98 if (fclose(in) < 0 || fclose(out) < 0) {
99 bb_perror_nomsg();
100 return -2;
101 }
102 return 0;
103 }
104 if (!ConvType)
105 ConvType = CT_DOS2UNIX;
106 break;
107 }
108 if (c == '\n') {
109 if ((ConvType == CT_DOS2UNIX) && (fn != NULL)) {
110 // file is alredy in UNIX format so it is not necessery to touch it
111 remove(tempFn);
112 if ((fclose(in) < 0) || (fclose(out) < 0)) {
113 bb_perror_nomsg();
114 return -2;
115 }
116 return 0;
117 }
118 if (!ConvType) {
119 ConvType = CT_UNIX2DOS;
120 }
121 if (ConvType == CT_UNIX2DOS) {
122 fputc('\r', out);
123 }
124 fputc('\n', out);
125 break;
126 }
127 fputc(c, out);
128 }
129 if (c != EOF)
130 while ((c = fgetc(in)) != EOF) {
131 if (c == '\r')
132 continue;
133 if (c == '\n') {
134 if (ConvType == CT_UNIX2DOS)
135 fputc('\r', out);
136 fputc('\n', out);
137 continue;
138 }
139 fputc(c, out);
140 }
141
142 if (fn != NULL) {
143 if (fclose(in) < 0 || fclose(out) < 0) {
144 bb_perror_nomsg();
145 remove(tempFn);
146 return -2;
147 }
148
149 /* Assume they are both on the same filesystem (which
150 * should be true since we put them into the same directory
151 * so we _should_ be ok, but you never know... */
152 if (rename(tempFn, fn) < 0) {
153 bb_perror_msg("unable to rename '%s' as '%s'", tempFn, fn);
154 return -1;
155 }
156 }
157
158 return 0;
159}
160
161int dos2unix_main(int argc, char *argv[])
162{
163 int ConvType = CT_AUTO;
164 int o;
165
166 //See if we are supposed to be doing dos2unix or unix2dos
167 if (argv[0][0]=='d') {
168 ConvType = CT_DOS2UNIX;
169 }
170 if (argv[0][0]=='u') {
171 ConvType = CT_UNIX2DOS;
172 }
173
174 // process parameters
175 while ((o = getopt(argc, argv, "du")) != EOF) {
176 switch (o) {
177 case 'd':
178 ConvType = CT_UNIX2DOS;
179 break;
180 case 'u':
181 ConvType = CT_DOS2UNIX;
182 break;
183 default:
184 bb_show_usage();
185 }
186 }
187
188 if (optind < argc) {
189 while(optind < argc)
190 if ((o = convert(argv[optind++], ConvType)) < 0)
191 break;
192 }
193 else
194 o = convert(NULL, ConvType);
195
196 return o;
197}
198
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini du implementation for busybox
4 *
5 * Copyright (C) 1999,2000,2001 by Lineo, inc. and John Beppu
6 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7 * Copyright (C) 2002 Edward Betts <edward@debian.org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
24
25/* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */
26/* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */
27
28/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
29 *
30 * Mostly rewritten for SUSv3 compliance and to fix bugs/defects.
31 * 1) Added support for SUSv3 -a, -H, -L, gnu -c, and (busybox) -d options.
32 * The -d option allows setting of max depth (similar to gnu --max-depth).
33 * 2) Fixed incorrect size calculations for links and directories, especially
34 * when errors occurred. Calculates sizes should now match gnu du output.
35 * 3) Added error checking of output.
36 * 4) Fixed busybox bug #1284 involving long overflow with human_readable.
37 */
38
39#include <stdlib.h>
40#include <limits.h>
41#include <unistd.h>
42#include <dirent.h>
43#include <sys/stat.h>
44#include "busybox.h"
45
46#ifdef CONFIG_FEATURE_HUMAN_READABLE
47# ifdef CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
48static unsigned long disp_hr = KILOBYTE;
49# else
50static unsigned long disp_hr = 512;
51# endif
52#elif defined CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
53static unsigned int disp_k = 1;
54#else
55static unsigned int disp_k; /* bss inits to 0 */
56#endif
57
58static int max_print_depth = INT_MAX;
59static int count_hardlinks = 1;
60
61static int status
62#if EXIT_SUCCESS == 0
63 = EXIT_SUCCESS
64#endif
65 ;
66static int print_files;
67static int slink_depth;
68static int du_depth;
69static int one_file_system;
70static dev_t dir_dev;
71
72
73static void print(long size, char *filename)
74{
75 /* TODO - May not want to defer error checking here. */
76#ifdef CONFIG_FEATURE_HUMAN_READABLE
77 bb_printf("%s\t%s\n", make_human_readable_str(size, 512, disp_hr),
78 filename);
79#else
80 if (disp_k) {
81 size++;
82 size >>= 1;
83 }
84 bb_printf("%ld\t%s\n", size, filename);
85#endif
86}
87
88/* tiny recursive du */
89static long du(char *filename)
90{
91 struct stat statbuf;
92 long sum;
93
94 if ((lstat(filename, &statbuf)) != 0) {
95 bb_perror_msg("%s", filename);
96 status = EXIT_FAILURE;
97 return 0;
98 }
99
100 if (one_file_system) {
101 if (du_depth == 0) {
102 dir_dev = statbuf.st_dev;
103 } else if (dir_dev != statbuf.st_dev) {
104 return 0;
105 }
106 }
107
108 sum = statbuf.st_blocks;
109
110 if (S_ISLNK(statbuf.st_mode)) {
111 if (slink_depth > du_depth) { /* -H or -L */
112 if ((stat(filename, &statbuf)) != 0) {
113 bb_perror_msg("%s", filename);
114 status = EXIT_FAILURE;
115 return 0;
116 }
117 sum = statbuf.st_blocks;
118 if (slink_depth == 1) {
119 slink_depth = INT_MAX; /* Convert -H to -L. */
120 }
121 }
122 }
123
124 if (statbuf.st_nlink > count_hardlinks) {
125 /* Add files/directories with links only once */
126 if (is_in_ino_dev_hashtable(&statbuf, NULL)) {
127 return 0;
128 }
129 add_to_ino_dev_hashtable(&statbuf, NULL);
130 }
131
132 if (S_ISDIR(statbuf.st_mode)) {
133 DIR *dir;
134 struct dirent *entry;
135 char *newfile;
136
137 dir = opendir(filename);
138 if (!dir) {
139 bb_perror_msg("%s", filename);
140 status = EXIT_FAILURE;
141 return sum;
142 }
143
144 newfile = last_char_is(filename, '/');
145 if (newfile)
146 *newfile = '\0';
147
148 while ((entry = readdir(dir))) {
149 char *name = entry->d_name;
150
151 newfile = concat_subpath_file(filename, name);
152 if(newfile == NULL)
153 continue;
154 ++du_depth;
155 sum += du(newfile);
156 --du_depth;
157 free(newfile);
158 }
159 closedir(dir);
160 } else if (du_depth > print_files) {
161 return sum;
162 }
163 if (du_depth <= max_print_depth) {
164 print(sum, filename);
165 }
166 return sum;
167}
168
169int du_main(int argc, char **argv)
170{
171 long total;
172 int slink_depth_save;
173 int print_final_total;
174 char *smax_print_depth;
175 unsigned long opt;
176
177#ifdef CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
178 if (getenv("POSIXLY_CORRECT")) { /* TODO - a new libbb function? */
179#ifdef CONFIG_FEATURE_HUMAN_READABLE
180 disp_hr = 512;
181#else
182 disp_k = 0;
183#endif
184 }
185#endif
186
187 /* Note: SUSv3 specifies that -a and -s options can not be used together
188 * in strictly conforming applications. However, it also says that some
189 * du implementations may produce output when -a and -s are used together.
190 * gnu du exits with an error code in this case. We choose to simply
191 * ignore -a. This is consistent with -s being equivalent to -d 0.
192 */
193#ifdef CONFIG_FEATURE_HUMAN_READABLE
194 bb_opt_complementaly = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s";
195 opt = bb_getopt_ulflags(argc, argv, "aHkLsx" "d:" "lc" "hm", &smax_print_depth);
196 if((opt & (1 << 9))) {
197 /* -h opt */
198 disp_hr = 0;
199 }
200 if((opt & (1 << 10))) {
201 /* -m opt */
202 disp_hr = MEGABYTE;
203 }
204 if((opt & (1 << 2))) {
205 /* -k opt */
206 disp_hr = KILOBYTE;
207 }
208#else
209 bb_opt_complementaly = "H-L:L-H:s-d:d-s";
210 opt = bb_getopt_ulflags(argc, argv, "aHkLsx" "d:" "lc", &smax_print_depth);
211#if !defined CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
212 if((opt & (1 << 2))) {
213 /* -k opt */
214 disp_k = 1;
215 }
216#endif
217#endif
218 if((opt & (1 << 0))) {
219 /* -a opt */
220 print_files = INT_MAX;
221 }
222 if((opt & (1 << 1))) {
223 /* -H opt */
224 slink_depth = 1;
225 }
226 if((opt & (1 << 3))) {
227 /* -L opt */
228 slink_depth = INT_MAX;
229 }
230 if((opt & (1 << 4))) {
231 /* -s opt */
232 max_print_depth = 0;
233 }
234 one_file_system = opt & (1 << 5); /* -x opt */
235 if((opt & (1 << 6))) {
236 /* -d opt */
237 max_print_depth = bb_xgetularg10_bnd(smax_print_depth, 0, INT_MAX);
238 }
239 if((opt & (1 << 7))) {
240 /* -l opt */
241 count_hardlinks = INT_MAX;
242 }
243 print_final_total = opt & (1 << 8); /* -c opt */
244
245 /* go through remaining args (if any) */
246 argv += optind;
247 if (optind >= argc) {
248 *--argv = ".";
249 if (slink_depth == 1) {
250 slink_depth = 0;
251 }
252 }
253
254 slink_depth_save = slink_depth;
255 total = 0;
256 do {
257 total += du(*argv);
258 slink_depth = slink_depth_save;
259 } while (*++argv);
260#ifdef CONFIG_FEATURE_CLEAN_UP
261 reset_ino_dev_hashtable();
262#endif
263
264 if (print_final_total) {
265 print(total, "total");
266 }
267
268 bb_fflush_stdout_and_exit(status);
269}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * echo implementation for busybox
4 *
5 * Copyright (c) 1991, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * Original copyright notice is retained at the end of this file.
23 */
24
25/* BB_AUDIT SUSv3 compliant -- unless configured as fancy echo. */
26/* http://www.opengroup.org/onlinepubs/007904975/utilities/echo.html */
27
28/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
29 *
30 * Because of behavioral differences, implemented configurable SUSv3
31 * or 'fancy' gnu-ish behaviors. Also, reduced size and fixed bugs.
32 * 1) In handling '\c' escape, the previous version only suppressed the
33 * trailing newline. SUSv3 specifies _no_ output after '\c'.
34 * 2) SUSv3 specifies that octal escapes are of the form \0{#{#{#}}}.
35 * The previous version version did not allow 4-digit octals.
36 */
37
38#include <stdio.h>
39#include <string.h>
40#include <stdlib.h>
41#include "busybox.h"
42
43extern int echo_main(int argc, char** argv)
44{
45#ifndef CONFIG_FEATURE_FANCY_ECHO
46#define eflag '\\'
47 ++argv;
48#else
49 const char *p;
50 int nflag = 1;
51 int eflag = 0;
52
53 while (*++argv && (**argv == '-')) {
54 /* If it appears that we are handling options, then make sure
55 * that all of the options specified are actually valid.
56 * Otherwise, the string should just be echoed.
57 */
58
59 if (!*(p = *argv + 1)) { /* A single '-', so echo it. */
60 goto just_echo;
61 }
62
63 do {
64 if (strrchr("neE", *p) == 0) {
65 goto just_echo;
66 }
67 } while (*++p);
68
69 /* All of the options in this arg are valid, so handle them. */
70 p = *argv + 1;
71 do {
72 if (*p == 'n') {
73 nflag = 0;
74 } else if (*p == 'e') {
75 eflag = '\\';
76 } else {
77 eflag = 0;
78 }
79 } while (*++p);
80 }
81
82just_echo:
83#endif
84 while (*argv) {
85 register int c;
86
87 while ((c = *(*argv)++)) {
88 if (c == eflag) { /* Check for escape seq. */
89 if (**argv == 'c') {
90 /* '\c' means cancel newline and
91 * ignore all subsequent chars. */
92 goto DONE;
93 }
94#ifndef CONFIG_FEATURE_FANCY_ECHO
95 /* SUSv3 specifies that octal escapes must begin with '0'. */
96 if (((unsigned int)(**argv - '1')) >= 7)
97#endif
98 {
99 /* Since SUSv3 mandates a first digit of 0, 4-digit octals
100 * of the form \0### are accepted. */
101 if ((**argv == '0') && (((unsigned int)(argv[0][1] - '0')) < 8)) {
102 (*argv)++;
103 }
104 /* bb_process_escape_sequence can handle nul correctly */
105 c = bb_process_escape_sequence((const char **) argv);
106 }
107 }
108 putchar(c);
109 }
110
111 if (*++argv) {
112 putchar(' ');
113 }
114 }
115
116#ifdef CONFIG_FEATURE_FANCY_ECHO
117 if (nflag) {
118 putchar('\n');
119 }
120#else
121 putchar('\n');
122#endif
123
124DONE:
125 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
126}
127
128/*-
129 * Copyright (c) 1991, 1993
130 * The Regents of the University of California. All rights reserved.
131 *
132 * This code is derived from software contributed to Berkeley by
133 * Kenneth Almquist.
134 *
135 * Redistribution and use in source and binary forms, with or without
136 * modification, are permitted provided that the following conditions
137 * are met:
138 * 1. Redistributions of source code must retain the above copyright
139 * notice, this list of conditions and the following disclaimer.
140 * 2. Redistributions in binary form must reproduce the above copyright
141 * notice, this list of conditions and the following disclaimer in the
142 * documentation and/or other materials provided with the distribution.
143 *
144 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
145 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
146 *
147 * California, Berkeley and its contributors.
148 * 4. Neither the name of the University nor the names of its contributors
149 * may be used to endorse or promote products derived from this software
150 * without specific prior written permission.
151 *
152 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
153 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
154 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
155 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
156 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
157 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
158 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
159 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
160 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
161 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
162 * SUCH DAMAGE.
163 *
164 * @(#)echo.c 8.1 (Berkeley) 5/31/93
165 */
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * env implementation for busybox
4 *
5 * Copyright (c) 1988, 1993, 1994
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * Original copyright notice is retained at the end of this file.
23 *
24 * Modified for BusyBox by Erik Andersen <andersen@codepoet.org>
25 */
26
27/* BB_AUDIT SUSv3 compliant */
28/* http://www.opengroup.org/onlinepubs/007904975/utilities/env.html */
29
30/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
31 *
32 * Fixed bug involving exit return codes if execvp fails. Also added
33 * output error checking.
34 */
35
36/*
37 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
38 * - correct "-" option usage
39 * - multiple "-u unsetenv" support
40 * - GNU long option support
41 * - save errno after exec failed before bb_perror_msg()
42 */
43
44
45#include <stdio.h>
46#include <string.h>
47#include <stdlib.h>
48#include <errno.h>
49#include <unistd.h>
50#include <getopt.h>
51#include "busybox.h"
52
53
54static const struct option env_long_options[] = {
55 { "ignore-environment", 0, NULL, 'i' },
56 { "unset", 1, NULL, 'u' },
57 { 0, 0, 0, 0 }
58};
59
60extern int env_main(int argc, char** argv)
61{
62 char **ep, *p;
63 char *cleanenv[1] = { NULL };
64 unsigned long opt;
65 llist_t *unset_env = NULL;
66 extern char **environ;
67
68 bb_opt_complementaly = "u*";
69 bb_applet_long_options = env_long_options;
70
71 opt = bb_getopt_ulflags(argc, argv, "+iu:", &unset_env);
72
73 argv += optind;
74 if (*argv && (argv[0][0] == '-') && !argv[0][1]) {
75 opt |= 1;
76 ++argv;
77 }
78
79 if(opt & 1)
80 environ = cleanenv;
81 else if(opt & 2) {
82 while(unset_env) {
83 unsetenv(unset_env->data);
84 unset_env = unset_env->link;
85 }
86 }
87
88 while (*argv && ((p = strchr(*argv, '=')) != NULL)) {
89 if (putenv(*argv) < 0) {
90 bb_perror_msg_and_die("putenv");
91 }
92 ++argv;
93 }
94
95 if (*argv) {
96 int er;
97
98 execvp(*argv, argv);
99 er = errno;
100 bb_perror_msg("%s", *argv); /* Avoid multibyte problems. */
101 return (er == ENOENT) ? 127 : 126; /* SUSv3-mandated exit codes. */
102 }
103
104 for (ep = environ; *ep; ep++) {
105 puts(*ep);
106 }
107
108 bb_fflush_stdout_and_exit(0);
109}
110
111/*
112 * Copyright (c) 1988, 1993, 1994
113 * The Regents of the University of California. All rights reserved.
114 *
115 * Redistribution and use in source and binary forms, with or without
116 * modification, are permitted provided that the following conditions
117 * are met:
118 * 1. Redistributions of source code must retain the above copyright
119 * notice, this list of conditions and the following disclaimer.
120 * 2. Redistributions in binary form must reproduce the above copyright
121 * notice, this list of conditions and the following disclaimer in the
122 * documentation and/or other materials provided with the distribution.
123 *
124 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
125 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
126 *
127 * 4. Neither the name of the University nor the names of its contributors
128 * may be used to endorse or promote products derived from this software
129 * without specific prior written permission.
130 *
131 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
132 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
133 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
134 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
135 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
136 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
137 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
138 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
139 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
140 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
141 * SUCH DAMAGE.
142 */
143
144
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini expr implementation for busybox
4 *
5 * based on GNU expr Mike Parker.
6 * Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc.
7 *
8 * Busybox modifications
9 * Copyright (c) 2000 Edward Betts <edward@debian.org>.
10 * Aug 2003 Vladimir Oleynik - reduced 464 bytes.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 *
26 */
27
28/* This program evaluates expressions. Each token (operator, operand,
29 * parenthesis) of the expression must be a separate argument. The
30 * parser used is a reasonably general one, though any incarnation of
31 * it is language-specific. It is especially nice for expressions.
32 *
33 * No parse tree is needed; a new node is evaluated immediately.
34 * One function can handle multiple operators all of equal precedence,
35 * provided they all associate ((x op x) op x). */
36
37/* no getopt needed */
38
39#include <stdio.h>
40#include <string.h>
41#include <stdlib.h>
42#include <regex.h>
43#include <sys/types.h>
44#include <errno.h>
45#include "busybox.h"
46
47
48/* The kinds of value we can have. */
49enum valtype {
50 integer,
51 string
52};
53typedef enum valtype TYPE;
54
55/* A value is.... */
56struct valinfo {
57 TYPE type; /* Which kind. */
58 union { /* The value itself. */
59 int i;
60 char *s;
61 } u;
62};
63typedef struct valinfo VALUE;
64
65/* The arguments given to the program, minus the program name. */
66static char **args;
67
68static VALUE *docolon (VALUE *sv, VALUE *pv);
69static VALUE *eval (void);
70static VALUE *int_value (int i);
71static VALUE *str_value (char *s);
72static int nextarg (char *str);
73static int null (VALUE *v);
74static int toarith (VALUE *v);
75static void freev (VALUE *v);
76static void tostring (VALUE *v);
77
78int expr_main (int argc, char **argv)
79{
80 VALUE *v;
81
82 if (argc == 1) {
83 bb_error_msg_and_die("too few arguments");
84 }
85
86 args = argv + 1;
87
88 v = eval ();
89 if (*args)
90 bb_error_msg_and_die ("syntax error");
91
92 if (v->type == integer)
93 printf ("%d\n", v->u.i);
94 else
95 puts (v->u.s);
96
97 exit (null (v));
98}
99
100/* Return a VALUE for I. */
101
102static VALUE *int_value (int i)
103{
104 VALUE *v;
105
106 v = xmalloc (sizeof(VALUE));
107 v->type = integer;
108 v->u.i = i;
109 return v;
110}
111
112/* Return a VALUE for S. */
113
114static VALUE *str_value (char *s)
115{
116 VALUE *v;
117
118 v = xmalloc (sizeof(VALUE));
119 v->type = string;
120 v->u.s = bb_xstrdup (s);
121 return v;
122}
123
124/* Free VALUE V, including structure components. */
125
126static void freev (VALUE *v)
127{
128 if (v->type == string)
129 free (v->u.s);
130 free (v);
131}
132
133/* Return nonzero if V is a null-string or zero-number. */
134
135static int null (VALUE *v)
136{
137 switch (v->type) {
138 case integer:
139 return v->u.i == 0;
140 default: /* string: */
141 return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0;
142 }
143}
144
145/* Coerce V to a string value (can't fail). */
146
147static void tostring (VALUE *v)
148{
149 if (v->type == integer) {
150 bb_xasprintf (&(v->u.s), "%d", v->u.i);
151 v->type = string;
152 }
153}
154
155/* Coerce V to an integer value. Return 1 on success, 0 on failure. */
156
157static int toarith (VALUE *v)
158{
159 if(v->type == string) {
160 int i;
161 char *e;
162
163 /* Don't interpret the empty string as an integer. */
164 /* Currently does not worry about overflow or int/long differences. */
165 i = (int) strtol(v->u.s, &e, 10);
166 if ((v->u.s == e) || *e)
167 return 0;
168 free (v->u.s);
169 v->u.i = i;
170 v->type = integer;
171 }
172 return 1;
173}
174
175/* Return nonzero if the next token matches STR exactly.
176 STR must not be NULL. */
177
178static int
179nextarg (char *str)
180{
181 if (*args == NULL)
182 return 0;
183 return strcmp (*args, str) == 0;
184}
185
186/* The comparison operator handling functions. */
187
188static int cmp_common (VALUE *l, VALUE *r, int op)
189{
190 int cmpval;
191
192 if (l->type == string || r->type == string) {
193 tostring (l);
194 tostring (r);
195 cmpval = strcmp (l->u.s, r->u.s);
196 }
197 else
198 cmpval = l->u.i - r->u.i;
199 switch(op) {
200 case '<':
201 return cmpval < 0;
202 case ('L'+'E'):
203 return cmpval <= 0;
204 case '=':
205 return cmpval == 0;
206 case '!':
207 return cmpval != 0;
208 case '>':
209 return cmpval > 0;
210 default: /* >= */
211 return cmpval >= 0;
212 }
213}
214
215/* The arithmetic operator handling functions. */
216
217static int arithmetic_common (VALUE *l, VALUE *r, int op)
218{
219 int li, ri;
220
221 if (!toarith (l) || !toarith (r))
222 bb_error_msg_and_die ("non-numeric argument");
223 li = l->u.i;
224 ri = r->u.i;
225 if((op == '/' || op == '%') && ri == 0)
226 bb_error_msg_and_die ( "division by zero");
227 switch(op) {
228 case '+':
229 return li + ri;
230 case '-':
231 return li - ri;
232 case '*':
233 return li * ri;
234 case '/':
235 return li / ri;
236 default:
237 return li % ri;
238 }
239}
240
241/* Do the : operator.
242 SV is the VALUE for the lhs (the string),
243 PV is the VALUE for the rhs (the pattern). */
244
245static VALUE *docolon (VALUE *sv, VALUE *pv)
246{
247 VALUE *v;
248 const char *errmsg;
249 struct re_pattern_buffer re_buffer;
250 struct re_registers re_regs;
251 int len;
252
253 tostring (sv);
254 tostring (pv);
255
256 if (pv->u.s[0] == '^') {
257 fprintf (stderr, "\
258warning: unportable BRE: `%s': using `^' as the first character\n\
259of a basic regular expression is not portable; it is being ignored",
260 pv->u.s);
261 }
262
263 len = strlen (pv->u.s);
264 memset (&re_buffer, 0, sizeof (re_buffer));
265 memset (&re_regs, 0, sizeof (re_regs));
266 re_buffer.allocated = 2 * len;
267 re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated);
268 re_buffer.translate = 0;
269 re_syntax_options = RE_SYNTAX_POSIX_BASIC;
270 errmsg = re_compile_pattern (pv->u.s, len, &re_buffer);
271 if (errmsg) {
272 bb_error_msg_and_die("%s", errmsg);
273 }
274
275 len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
276 if (len >= 0) {
277 /* Were \(...\) used? */
278 if (re_buffer.re_nsub > 0) { /* was (re_regs.start[1] >= 0) */
279 sv->u.s[re_regs.end[1]] = '\0';
280 v = str_value (sv->u.s + re_regs.start[1]);
281 }
282 else
283 v = int_value (len);
284 }
285 else {
286 /* Match failed -- return the right kind of null. */
287 if (re_buffer.re_nsub > 0)
288 v = str_value ("");
289 else
290 v = int_value (0);
291 }
292 free (re_buffer.buffer);
293 return v;
294}
295
296/* Handle bare operands and ( expr ) syntax. */
297
298static VALUE *eval7 (void)
299{
300 VALUE *v;
301
302 if (!*args)
303 bb_error_msg_and_die ( "syntax error");
304
305 if (nextarg ("(")) {
306 args++;
307 v = eval ();
308 if (!nextarg (")"))
309 bb_error_msg_and_die ( "syntax error");
310 args++;
311 return v;
312 }
313
314 if (nextarg (")"))
315 bb_error_msg_and_die ( "syntax error");
316
317 return str_value (*args++);
318}
319
320/* Handle match, substr, index, length, and quote keywords. */
321
322static VALUE *eval6 (void)
323{
324 VALUE *l, *r, *v, *i1, *i2;
325
326 if (nextarg ("quote")) {
327 args++;
328 if (!*args)
329 bb_error_msg_and_die ( "syntax error");
330 return str_value (*args++);
331 }
332 else if (nextarg ("length")) {
333 args++;
334 r = eval6 ();
335 tostring (r);
336 v = int_value (strlen (r->u.s));
337 freev (r);
338 return v;
339 }
340 else if (nextarg ("match")) {
341 args++;
342 l = eval6 ();
343 r = eval6 ();
344 v = docolon (l, r);
345 freev (l);
346 freev (r);
347 return v;
348 }
349 else if (nextarg ("index")) {
350 args++;
351 l = eval6 ();
352 r = eval6 ();
353 tostring (l);
354 tostring (r);
355 v = int_value (strcspn (l->u.s, r->u.s) + 1);
356 if (v->u.i == (int) strlen (l->u.s) + 1)
357 v->u.i = 0;
358 freev (l);
359 freev (r);
360 return v;
361 }
362 else if (nextarg ("substr")) {
363 args++;
364 l = eval6 ();
365 i1 = eval6 ();
366 i2 = eval6 ();
367 tostring (l);
368 if (!toarith (i1) || !toarith (i2)
369 || i1->u.i > (int) strlen (l->u.s)
370 || i1->u.i <= 0 || i2->u.i <= 0)
371 v = str_value ("");
372 else {
373 v = xmalloc (sizeof(VALUE));
374 v->type = string;
375 v->u.s = bb_xstrndup(l->u.s + i1->u.i - 1, i2->u.i);
376 }
377 freev (l);
378 freev (i1);
379 freev (i2);
380 return v;
381 }
382 else
383 return eval7 ();
384}
385
386/* Handle : operator (pattern matching).
387 Calls docolon to do the real work. */
388
389static VALUE *eval5 (void)
390{
391 VALUE *l, *r, *v;
392
393 l = eval6 ();
394 while (nextarg (":")) {
395 args++;
396 r = eval6 ();
397 v = docolon (l, r);
398 freev (l);
399 freev (r);
400 l = v;
401 }
402 return l;
403}
404
405/* Handle *, /, % operators. */
406
407static VALUE *eval4 (void)
408{
409 VALUE *l, *r;
410 int op, val;
411
412 l = eval5 ();
413 while (1) {
414 if (nextarg ("*"))
415 op = '*';
416 else if (nextarg ("/"))
417 op = '/';
418 else if (nextarg ("%"))
419 op = '%';
420 else
421 return l;
422 args++;
423 r = eval5 ();
424 val = arithmetic_common (l, r, op);
425 freev (l);
426 freev (r);
427 l = int_value (val);
428 }
429}
430
431/* Handle +, - operators. */
432
433static VALUE *eval3 (void)
434{
435 VALUE *l, *r;
436 int op, val;
437
438 l = eval4 ();
439 while (1) {
440 if (nextarg ("+"))
441 op = '+';
442 else if (nextarg ("-"))
443 op = '-';
444 else
445 return l;
446 args++;
447 r = eval4 ();
448 val = arithmetic_common (l, r, op);
449 freev (l);
450 freev (r);
451 l = int_value (val);
452 }
453}
454
455/* Handle comparisons. */
456
457static VALUE *eval2 (void)
458{
459 VALUE *l, *r;
460 int op, val;
461
462 l = eval3 ();
463 while (1) {
464 if (nextarg ("<"))
465 op = '<';
466 else if (nextarg ("<="))
467 op = 'L'+'E';
468 else if (nextarg ("=") || nextarg ("=="))
469 op = '=';
470 else if (nextarg ("!="))
471 op = '!';
472 else if (nextarg (">="))
473 op = 'G'+'E';
474 else if (nextarg (">"))
475 op = '>';
476 else
477 return l;
478 args++;
479 r = eval3 ();
480 toarith (l);
481 toarith (r);
482 val = cmp_common (l, r, op);
483 freev (l);
484 freev (r);
485 l = int_value (val);
486 }
487}
488
489/* Handle &. */
490
491static VALUE *eval1 (void)
492{
493 VALUE *l, *r;
494
495 l = eval2 ();
496 while (nextarg ("&")) {
497 args++;
498 r = eval2 ();
499 if (null (l) || null (r)) {
500 freev (l);
501 freev (r);
502 l = int_value (0);
503 }
504 else
505 freev (r);
506 }
507 return l;
508}
509
510/* Handle |. */
511
512static VALUE *eval (void)
513{
514 VALUE *l, *r;
515
516 l = eval1 ();
517 while (nextarg ("|")) {
518 args++;
519 r = eval1 ();
520 if (null (l)) {
521 freev (l);
522 l = r;
523 }
524 else
525 freev (r);
526 }
527 return l;
528}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini false implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/false.html */
25
26#include <stdlib.h>
27#include "busybox.h"
28
29extern int false_main(int argc, char **argv)
30{
31 return EXIT_FAILURE;
32}
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 @@
1/* fold -- wrap each input line to fit in specified width.
2
3 Written by David MacKenzie, djm@gnu.ai.mit.edu.
4 Copyright (C) 91, 1995-2002 Free Software Foundation, Inc.
5
6 Modified for busybox based on coreutils v 5.0
7 Copyright (C) 2003 Glenn McGrath <bug1@iinet.net.au>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software Foundation,
21 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22*/
23
24#include <ctype.h>
25#include <errno.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <getopt.h>
30#include <sys/types.h>
31
32#include "busybox.h"
33
34/* If nonzero, count bytes, not column positions. */
35static int count_bytes;
36
37/* Assuming the current column is COLUMN, return the column that
38 printing C will move the cursor to.
39 The first column is 0. */
40
41static int adjust_column(int column, char c)
42{
43 if (!count_bytes) {
44 if (c == '\b') {
45 if (column > 0)
46 column--;
47 } else if (c == '\r')
48 column = 0;
49 else if (c == '\t')
50 column = column + 8 - column % 8;
51 else /* if (isprint (c)) */
52 column++;
53 } else
54 column++;
55 return column;
56}
57
58extern int fold_main(int argc, char **argv)
59{
60 /* If nonzero, try to break on whitespace. */
61 int break_spaces;
62
63 /* If nonzero, at least one of the files we read was standard input. */
64 int have_read_stdin;
65
66 int width = 80;
67 int i;
68 int optc;
69 int errs = 0;
70
71 break_spaces = count_bytes = have_read_stdin = 0;
72
73 /* Turn any numeric options into -w options. */
74 for (i = 1; i < argc; i++) {
75 char const *a = argv[i];
76
77 if (a[0] == '-') {
78 if (a[1] == '-' && !a[2])
79 break;
80 if (isdigit(a[1])) {
81 char *s = xmalloc(strlen(a) + 2);
82
83 s[0] = '-';
84 s[1] = 'w';
85 strcpy(s + 2, a + 1);
86 argv[i] = s;
87 }
88 }
89 }
90
91 while ((optc = getopt(argc, argv, "bsw:")) > 0) {
92 switch (optc) {
93 case 'b': /* Count bytes rather than columns. */
94 count_bytes = 1;
95 break;
96 case 's': /* Break at word boundaries. */
97 break_spaces = 1;
98 break;
99 case 'w': { /* Line width. */
100 width = bb_xgetlarg(optarg, 10, 1, 10000);
101 break;
102 }
103 default:
104 bb_show_usage();
105 }
106 }
107
108 argv += optind;
109 if (!*argv) {
110 *--argv = "-";
111 }
112
113 do {
114 FILE *istream = bb_wfopen_input(*argv);
115 if (istream != NULL) {
116 int c;
117 int column = 0; /* Screen column where next char will go. */
118 int offset_out = 0; /* Index in `line_out' for next char. */
119 static char *line_out = NULL;
120 static int allocated_out = 0;
121
122 while ((c = getc(istream)) != EOF) {
123 if (offset_out + 1 >= allocated_out) {
124 allocated_out += 1024;
125 line_out = xrealloc(line_out, allocated_out);
126 }
127
128 if (c == '\n') {
129 line_out[offset_out++] = c;
130 fwrite(line_out, sizeof(char), (size_t) offset_out, stdout);
131 column = offset_out = 0;
132 continue;
133 }
134
135rescan:
136 column = adjust_column(column, c);
137
138 if (column > width) {
139 /* This character would make the line too long.
140 Print the line plus a newline, and make this character
141 start the next line. */
142 if (break_spaces) {
143 /* Look for the last blank. */
144 int logical_end;
145
146 for (logical_end = offset_out - 1; logical_end >= 0; logical_end--) {
147 if (isblank(line_out[logical_end])) {
148 break;
149 }
150 }
151 if (logical_end >= 0) {
152 /* Found a blank. Don't output the part after it. */
153 logical_end++;
154 fwrite(line_out, sizeof(char), (size_t) logical_end, stdout);
155 putchar('\n');
156 /* Move the remainder to the beginning of the next line.
157 The areas being copied here might overlap. */
158 memmove(line_out, line_out + logical_end, offset_out - logical_end);
159 offset_out -= logical_end;
160 for (column = i = 0; i < offset_out; i++) {
161 column = adjust_column(column, line_out[i]);
162 }
163 goto rescan;
164 }
165 } else {
166 if (offset_out == 0) {
167 line_out[offset_out++] = c;
168 continue;
169 }
170 }
171 line_out[offset_out++] = '\n';
172 fwrite(line_out, sizeof(char), (size_t) offset_out, stdout);
173 column = offset_out = 0;
174 goto rescan;
175 }
176
177 line_out[offset_out++] = c;
178 }
179
180 if (offset_out) {
181 fwrite(line_out, sizeof(char), (size_t) offset_out, stdout);
182 }
183
184 if (ferror(istream) || bb_fclose_nonstdin(istream)) {
185 bb_perror_msg("%s", *argv); /* Avoid multibyte problems. */
186 errs |= EXIT_FAILURE;
187 }
188 } else {
189 errs |= EXIT_FAILURE;
190 }
191 } while (*++argv);
192
193 bb_fflush_stdout_and_exit(errs);
194}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * head implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant */
24/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
25/* http://www.opengroup.org/onlinepubs/007904975/utilities/head.html */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <limits.h>
30#include <ctype.h>
31#include <unistd.h>
32#include "busybox.h"
33
34static const char head_opts[] =
35 "n:"
36#ifdef CONFIG_FEATURE_FANCY_HEAD
37 "c:qv"
38#endif
39 ;
40
41static const char header_fmt_str[] = "\n==> %s <==\n";
42
43int head_main(int argc, char **argv)
44{
45 unsigned long count = 10;
46 unsigned long i;
47#ifdef CONFIG_FEATURE_FANCY_HEAD
48 int count_bytes = 0;
49 int header_threshhold = 1;
50#endif
51
52 FILE *fp;
53 const char *fmt;
54 char *p;
55 int opt;
56 int c;
57 int retval = EXIT_SUCCESS;
58
59 /* Allow legacy syntax of an initial numeric option without -n. */
60 if ((argc > 1) && (argv[1][0] == '-')
61 /* && (isdigit)(argv[1][1]) */
62 && (((unsigned int)(argv[1][1] - '0')) <= 9)
63 ) {
64 --argc;
65 ++argv;
66 p = (*argv) + 1;
67 goto GET_COUNT;
68 }
69
70 while ((opt = getopt(argc, argv, head_opts)) > 0) {
71 switch(opt) {
72#ifdef CONFIG_FEATURE_FANCY_HEAD
73 case 'q':
74 header_threshhold = INT_MAX;
75 break;
76 case 'v':
77 header_threshhold = -1;
78 break;
79 case 'c':
80 count_bytes = 1;
81 /* fall through */
82#endif
83 case 'n':
84 p = optarg;
85 GET_COUNT:
86 count = bb_xgetularg10(p);
87 break;
88 default:
89 bb_show_usage();
90 }
91 }
92
93 argv += optind;
94 if (!*argv) {
95 *--argv = "-";
96 }
97
98 fmt = header_fmt_str + 1;
99#ifdef CONFIG_FEATURE_FANCY_HEAD
100 if (argc - optind <= header_threshhold) {
101 header_threshhold = 0;
102 }
103#else
104 if (argc <= optind + 1) {
105 fmt += 11;
106 }
107 /* Now define some things here to avoid #ifdefs in the code below.
108 * These should optimize out of the if conditions below. */
109#define header_threshhold 1
110#define count_bytes 0
111#endif
112
113 do {
114 if ((fp = bb_wfopen_input(*argv)) != NULL) {
115 if (fp == stdin) {
116 *argv = (char *) bb_msg_standard_input;
117 }
118 if (header_threshhold) {
119 bb_printf(fmt, *argv);
120 }
121 i = count;
122 while (i && ((c = getc(fp)) != EOF)) {
123 if (count_bytes || (c == '\n')) {
124 --i;
125 }
126 putchar(c);
127 }
128 if (bb_fclose_nonstdin(fp)) {
129 bb_perror_msg("%s", *argv); /* Avoid multibyte problems. */
130 retval = EXIT_FAILURE;
131 }
132 bb_xferror_stdout();
133 }
134 fmt = header_fmt_str;
135 } while (*++argv);
136
137 bb_fflush_stdout_and_exit(retval);
138}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini hostid implementation for busybox
4 *
5 * Copyright (C) 2000 Edward Betts <edward@debian.org>.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
24
25#include <stdlib.h>
26#include <unistd.h>
27#include "busybox.h"
28
29extern int hostid_main(int argc, char **argv)
30{
31 if (argc > 1) {
32 bb_show_usage();
33 }
34
35 bb_printf("%lx\n", gethostid());
36
37 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
38}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini id implementation for busybox
4 *
5 * Copyright (C) 2000 by Randolph Chung <tausq@debian.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 _NOT_ compliant -- option -G is not currently supported. */
24/* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever length and to
25 * be more similar to GNU id.
26 */
27
28#include "busybox.h"
29#include "pwd_.h"
30#include <stdio.h>
31#include <unistd.h>
32#include <sys/types.h>
33
34#ifdef CONFIG_SELINUX
35#include <proc_secure.h>
36#include <flask_util.h>
37#endif
38
39#define PRINT_REAL 1
40#define NAME_NOT_NUMBER 2
41#define JUST_USER 4
42#define JUST_GROUP 8
43
44static short printf_full(unsigned int id, const char *arg, const char prefix)
45{
46 const char *fmt = "%cid=%u";
47 short status=EXIT_FAILURE;
48
49 if(arg) {
50 fmt = "%cid=%u(%s)";
51 status=EXIT_SUCCESS;
52 }
53 bb_printf(fmt, prefix, id, arg);
54 return status;
55}
56
57extern int id_main(int argc, char **argv)
58{
59 struct passwd *p;
60 uid_t uid;
61 gid_t gid;
62 unsigned long flags;
63 short status;
64#ifdef CONFIG_SELINUX
65 int is_flask_enabled_flag = is_flask_enabled();
66#endif
67
68 bb_opt_complementaly = "u~g:g~u";
69 flags = bb_getopt_ulflags(argc, argv, "rnug");
70
71 if ((flags & 0x80000000UL)
72 /* Don't allow -n -r -nr */
73 || (flags <= 3 && flags > 0)
74 /* Don't allow more than one username */
75 || (argc > optind + 1))
76 bb_show_usage();
77
78 /* This values could be overwritten later */
79 uid = geteuid();
80 gid = getegid();
81 if (flags & PRINT_REAL) {
82 uid = getuid();
83 gid = getgid();
84 }
85
86 if(argv[optind]) {
87 p=getpwnam(argv[optind]);
88 /* my_getpwnam is needed because it exits on failure */
89 uid = my_getpwnam(argv[optind]);
90 gid = p->pw_gid;
91 /* in this case PRINT_REAL is the same */
92 }
93
94 if(flags & (JUST_GROUP | JUST_USER)) {
95 /* JUST_GROUP and JUST_USER are mutually exclusive */
96 if(flags & NAME_NOT_NUMBER) {
97 /* my_getpwuid and my_getgrgid exit on failure so puts cannot segfault */
98 puts((flags & JUST_USER) ? my_getpwuid(NULL, uid, -1 ) : my_getgrgid(NULL, gid, -1 ));
99 } else {
100 bb_printf("%u\n",(flags & JUST_USER) ? uid : gid);
101 }
102 /* exit */
103 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
104 }
105
106 /* Print full info like GNU id */
107 /* my_getpwuid doesn't exit on failure here */
108 status=printf_full(uid, my_getpwuid(NULL, uid, 0), 'u');
109 putchar(' ');
110 /* my_getgrgid doesn't exit on failure here */
111 status|=printf_full(gid, my_getgrgid(NULL, gid, 0), 'g');
112#ifdef CONFIG_SELINUX
113 if(is_flask_enabled_flag) {
114 security_id_t mysid = getsecsid();
115 char context[80];
116 int len = sizeof(context);
117 context[0] = '\0';
118 if(security_sid_to_context(mysid, context, &len))
119 strcpy(context, "unknown");
120 bb_printf(" context=%s", context);
121 }
122#endif
123 putchar('\n');
124 bb_fflush_stdout_and_exit(status);
125}
126
127/* END CODE */
128/*
129Local Variables:
130c-file-style: "linux"
131c-basic-offset: 4
132tab-width: 4
133End:
134*/
135
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 @@
1/*
2 * Copyright (C) 2003 by Glenn McGrath <bug1@iinet.net.au>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 *
19 * TODO: -d option, need a way of recursively making directories and changing
20 * owner/group, will probably modify bb_make_directory(...)
21 */
22
23#include <sys/stat.h>
24#include <sys/types.h>
25#include <errno.h>
26#include <getopt.h>
27#include <stdlib.h>
28#include <string.h>
29#include <unistd.h>
30
31#include "busybox.h"
32#include "libcoreutils/coreutils.h"
33
34#define INSTALL_OPT_CMD 1
35#define INSTALL_OPT_DIRECTORY 2
36#define INSTALL_OPT_PRESERVE_TIME 4
37#define INSTALL_OPT_STRIP 8
38#define INSTALL_OPT_GROUP 16
39#define INSTALL_OPT_MODE 32
40#define INSTALL_OPT_OWNER 64
41
42static const struct option install_long_options[] = {
43 { "directory", 0, NULL, 'd' },
44 { "preserve-timestamps", 0, NULL, 'p' },
45 { "strip", 0, NULL, 's' },
46 { "group", 0, NULL, 'g' },
47 { "mode", 0, NULL, 'm' },
48 { "owner", 0, NULL, 'o' },
49 { 0, 0, 0, 0 }
50};
51
52extern int install_main(int argc, char **argv)
53{
54 struct stat statbuf;
55 mode_t mode;
56 uid_t uid;
57 gid_t gid;
58 char *gid_str = "-1";
59 char *uid_str = "-1";
60 char *mode_str = "0755";
61 int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE;
62 int ret = EXIT_SUCCESS;
63 int flags;
64 int i;
65
66 bb_applet_long_options = install_long_options;
67 bb_opt_complementaly = "s~d:d~s";
68 /* -c exists for backwards compatability, its needed */
69 flags = bb_getopt_ulflags(argc, argv, "cdpsg:m:o:", &gid_str, &mode_str, &uid_str); /* 'a' must be 2nd */
70
71 /* Check valid options were given */
72 if(flags & 0x80000000UL) {
73 bb_show_usage();
74 }
75
76 /* preserve access and modification time, this is GNU behaviour, BSD only preserves modification time */
77 if (flags & INSTALL_OPT_PRESERVE_TIME) {
78 copy_flags |= FILEUTILS_PRESERVE_STATUS;
79 }
80 bb_parse_mode(mode_str, &mode);
81 gid = get_ug_id(gid_str, my_getgrnam);
82 uid = get_ug_id(uid_str, my_getpwnam);
83 umask(0);
84
85 /* Create directories
86 * dont use bb_make_directory() as it cant change uid or gid
87 * perhaps bb_make_directory() should be improved.
88 */
89 if (flags & INSTALL_OPT_DIRECTORY) {
90 for (argv += optind; *argv; argv++) {
91 char *old_argv_ptr = *argv + 1;
92 char *argv_ptr;
93 do {
94 argv_ptr = strchr(old_argv_ptr, '/');
95 old_argv_ptr = argv_ptr;
96 if (argv_ptr) {
97 *argv_ptr = '\0';
98 old_argv_ptr++;
99 }
100 if (mkdir(*argv, mode) == -1) {
101 if (errno != EEXIST) {
102 bb_perror_msg("coulnt create %s", *argv);
103 ret = EXIT_FAILURE;
104 break;
105 }
106 }
107 else if (lchown(*argv, uid, gid) == -1) {
108 bb_perror_msg("cannot change ownership of %s", *argv);
109 ret = EXIT_FAILURE;
110 break;
111 }
112 if (argv_ptr) {
113 *argv_ptr = '/';
114 }
115 } while (old_argv_ptr);
116 }
117 return(ret);
118 }
119
120 cp_mv_stat2(argv[argc - 1], &statbuf, lstat);
121 for (i = optind; i < argc - 1; i++) {
122 unsigned char *dest;
123
124 if (S_ISDIR(statbuf.st_mode)) {
125 dest = concat_path_file(argv[argc - 1], basename(argv[i]));
126 } else {
127 dest = argv[argc - 1];
128 }
129 ret |= copy_file(argv[i], dest, copy_flags);
130
131 /* Set the file mode */
132 if (chmod(dest, mode) == -1) {
133 bb_perror_msg("cannot change permissions of %s", dest);
134 ret = EXIT_FAILURE;
135 }
136
137 /* Set the user and group id */
138 if (lchown(dest, uid, gid) == -1) {
139 bb_perror_msg("cannot change ownership of %s", dest);
140 ret = EXIT_FAILURE;
141 }
142 if (flags & INSTALL_OPT_STRIP) {
143 if (execlp("strip", "strip", dest, NULL) == -1) {
144 bb_error_msg("strip failed");
145 ret = EXIT_FAILURE;
146 }
147 }
148 }
149
150 return(ret);
151}
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 @@
1/* vi: set sw=4 ts=4: */
2
3/* BB_AUDIT SUSv3 N/A -- Apparently a busybox (obsolete?) extension. */
4
5#include <stdlib.h>
6#include <string.h>
7#include <stdio.h>
8#include "busybox.h"
9
10extern int length_main(int argc, char **argv)
11{
12 if ((argc != 2) || (**(++argv) == '-')) {
13 bb_show_usage();
14 }
15
16 bb_printf("%lu\n", (unsigned long)strlen(*argv));
17
18 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
19}
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=../..
21top_builddir=../..
22srcdir=$(top_srcdir)/coreutils/libcoreutils
23LIBCOREUTILS_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include $(srcdir)/Makefile.in
27
28all: $(libraries-y)
29-include $(top_builddir)/.depend
30
31clean:
32 rm -f *.o *.a $(AR_TARGET)
33
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20LIBCOREUTILS_AR:=libcoreutils.a
21ifndef $(LIBCOREUTILS_DIR)
22LIBCOREUTILS_DIR:=$(top_builddir)/coreutils/libcoreutils/
23endif
24srcdir=$(top_srcdir)/coreutils/libcoreutils
25
26LIBCOREUTILS_SRC:= cp_mv_stat.c getopt_mk_fifo_nod.c xgetoptfile_sort_uniq.c
27
28LIBCOREUTILS_OBJS=$(patsubst %.c,$(LIBCOREUTILS_DIR)%.o, $(LIBCOREUTILS_SRC))
29
30libraries-y+=$(LIBCOREUTILS_DIR)$(LIBCOREUTILS_AR)
31
32$(LIBCOREUTILS_DIR)$(LIBCOREUTILS_AR): $(LIBCOREUTILS_OBJS)
33 $(AR) -ro $@ $(LIBCOREUTILS_OBJS)
34
35$(LIBCOREUTILS_DIR)%.o: $(srcdir)/%.c
36 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
37
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 @@
1#ifndef COREUTILS_H
2#define COREUTILS_H 1
3
4typedef int (*stat_func)(const char *fn, struct stat *ps);
5
6extern int cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf);
7extern int cp_mv_stat(const char *fn, struct stat *fn_stat);
8
9extern mode_t getopt_mk_fifo_nod(int argc, char **argv);
10extern FILE *xgetoptfile_sort_uniq(char **argv, const char *mode);
11
12#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * coreutils utility routine
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <errno.h>
24#include <sys/stat.h>
25#include "libbb.h"
26#include "coreutils.h"
27
28extern int cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf)
29{
30 if (sf(fn, fn_stat) < 0) {
31 if (errno != ENOENT) {
32 bb_perror_msg("unable to stat `%s'", fn);
33 return -1;
34 }
35 return 0;
36 } else if (S_ISDIR(fn_stat->st_mode)) {
37 return 3;
38 }
39 return 1;
40}
41
42extern int cp_mv_stat(const char *fn, struct stat *fn_stat)
43{
44 return cp_mv_stat2(fn, fn_stat, stat);
45}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * coreutils utility routine
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <unistd.h>
26#include "libbb.h"
27#include "coreutils.h"
28
29extern mode_t getopt_mk_fifo_nod(int argc, char **argv)
30{
31 mode_t mode = 0666;
32 int opt;
33
34 while ((opt = getopt(argc, argv, "m:")) > 0) {
35 if (opt == 'm') {
36 mode = 0666;
37 if (bb_parse_mode(optarg, &mode)) {
38 umask(0);
39 continue;
40 }
41 }
42 bb_show_usage();
43 }
44 return mode;
45}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * coreutils utility routine
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <stdio.h>
24#include <unistd.h>
25#include "libbb.h"
26#include "coreutils.h"
27
28extern FILE *xgetoptfile_sort_uniq(char **argv, const char *mode)
29{
30 const char *n;
31
32 if ((n = *argv) != NULL) {
33 if ((*n != '-') || n[1]) {
34 return bb_xfopen(n, mode);
35 }
36 }
37 return (*mode == 'r') ? stdin : stdout;
38}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini ln implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant */
24/* BB_AUDIT GNU options missing: -b, -d, -F, -i, -S, and -v. */
25/* http://www.opengroup.org/onlinepubs/007904975/utilities/ln.html */
26
27/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
28 *
29 * Fixed bug involving -n option. Essentially, -n was always in effect.
30 */
31
32#include <stdlib.h>
33#include <unistd.h>
34#include "busybox.h"
35
36#define LN_SYMLINK 1
37#define LN_FORCE 2
38#define LN_NODEREFERENCE 4
39
40extern int ln_main(int argc, char **argv)
41{
42 int status = EXIT_SUCCESS;
43 int flag;
44 char *last;
45 char *src_name;
46 char *src;
47 struct stat statbuf;
48 int (*link_func)(const char *, const char *);
49
50 flag = bb_getopt_ulflags(argc, argv, "sfn");
51
52 if (argc == optind) {
53 bb_show_usage();
54 }
55
56 last = argv[argc - 1];
57 argv += optind;
58
59 if (argc == optind + 1) {
60 *--argv = last;
61 last = bb_get_last_path_component(bb_xstrdup(last));
62 }
63
64 do {
65 src_name = NULL;
66 src = last;
67
68 if (is_directory(src,
69 (flag & LN_NODEREFERENCE) ^ LN_NODEREFERENCE,
70 NULL)) {
71 src_name = bb_xstrdup(*argv);
72 src = concat_path_file(src, bb_get_last_path_component(src_name));
73 free(src_name);
74 src_name = src;
75 }
76 if (!(flag & LN_SYMLINK) && stat(*argv, &statbuf)) {
77 bb_perror_msg(*argv);
78 status = EXIT_FAILURE;
79 free(src_name);
80 continue;
81 }
82
83 if (flag & LN_FORCE) {
84 unlink(src);
85 }
86
87 link_func = link;
88 if (flag & LN_SYMLINK) {
89 link_func = symlink;
90 }
91
92 if (link_func(*argv, src) != 0) {
93 bb_perror_msg(src);
94 status = EXIT_FAILURE;
95 }
96
97 free(src_name);
98
99 } while ((++argv)[1]);
100
101 return status;
102}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini logname implementation for busybox
4 *
5 * Copyright (C) 2000 Edward Betts <edward@debian.org>.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/logname.html */
25
26/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
27 *
28 * SUSv3 specifies the string used is that returned from getlogin().
29 * The previous implementation used getpwuid() for geteuid(), which
30 * is _not_ the same. Erik apparently made this change almost 3 years
31 * ago to avoid failing when no utmp was available. However, the
32 * correct course of action wrt SUSv3 for a failing getlogin() is
33 * a diagnostic message and an error return.
34 */
35
36#include <stdio.h>
37#include <stdlib.h>
38#include <unistd.h>
39#include "busybox.h"
40
41extern int logname_main(int argc, char **argv)
42{
43 const char *p;
44
45 if (argc > 1) {
46 bb_show_usage();
47 }
48
49 if ((p = getlogin()) != NULL) {
50 puts(p);
51 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
52 }
53
54 bb_perror_msg_and_die("getlogin");
55}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * tiny-ls.c version 0.1.0: A minimalist 'ls'
4 * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21/*
22 * To achieve a small memory footprint, this version of 'ls' doesn't do any
23 * file sorting, and only has the most essential command line switches
24 * (i.e., the ones I couldn't live without :-) All features which involve
25 * linking in substantial chunks of libc can be disabled.
26 *
27 * Although I don't really want to add new features to this program to
28 * keep it small, I *am* interested to receive bug fixes and ways to make
29 * it more portable.
30 *
31 * KNOWN BUGS:
32 * 1. ls -l of a directory doesn't give "total <blocks>" header
33 * 2. ls of a symlink to a directory doesn't list directory contents
34 * 3. hidden files can make column width too large
35 *
36 * NON-OPTIMAL BEHAVIOUR:
37 * 1. autowidth reads directories twice
38 * 2. if you do a short directory listing without filetype characters
39 * appended, there's no need to stat each one
40 * PORTABILITY:
41 * 1. requires lstat (BSD) - how do you do it without?
42 */
43
44enum {
45 TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */
46 COLUMN_GAP = 2, /* includes the file type char */
47};
48
49/************************************************************************/
50
51#include <sys/types.h>
52#include <sys/stat.h>
53#include <stdio.h>
54#include <unistd.h>
55#include <dirent.h>
56#include <errno.h>
57#include <stdio.h>
58#include <string.h>
59#include <stdlib.h>
60#include <fcntl.h>
61#include <signal.h>
62#include <termios.h>
63#include <sys/ioctl.h>
64#include <sys/sysmacros.h> /* major() and minor() */
65#include "busybox.h"
66#ifdef CONFIG_SELINUX
67#include <fs_secure.h>
68#include <flask_util.h>
69#include <ss.h>
70#endif
71
72#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
73#include <time.h>
74#endif
75
76/* what is the overall style of the listing */
77#define STYLE_AUTO (0)
78#define STYLE_COLUMNS (1U<<21) /* fill columns */
79#define STYLE_LONG (2U<<21) /* one record per line, extended info */
80#define STYLE_SINGLE (3U<<21) /* one record per line */
81
82#define STYLE_MASK STYLE_SINGLE
83#define STYLE_ONE_RECORD_FLAG STYLE_LONG
84
85/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */
86/* what file information will be listed */
87#define LIST_INO (1U<<0)
88#define LIST_BLOCKS (1U<<1)
89#define LIST_MODEBITS (1U<<2)
90#define LIST_NLINKS (1U<<3)
91#define LIST_ID_NAME (1U<<4)
92#define LIST_ID_NUMERIC (1U<<5)
93#define LIST_CONTEXT (1U<<6)
94#define LIST_SIZE (1U<<7)
95#define LIST_DEV (1U<<8)
96#define LIST_DATE_TIME (1U<<9)
97#define LIST_FULLTIME (1U<<10)
98#define LIST_FILENAME (1U<<11)
99#define LIST_SYMLINK (1U<<12)
100#define LIST_FILETYPE (1U<<13)
101#define LIST_EXEC (1U<<14)
102
103#define LIST_MASK ((LIST_EXEC << 1) - 1)
104
105/* what files will be displayed */
106/* TODO -- We may be able to make DISP_NORMAL 0 to save a bit slot. */
107#define DISP_NORMAL (1U<<14) /* show normal filenames */
108#define DISP_DIRNAME (1U<<15) /* 2 or more items? label directories */
109#define DISP_HIDDEN (1U<<16) /* show filenames starting with . */
110#define DISP_DOT (1U<<17) /* show . and .. */
111#define DISP_NOLIST (1U<<18) /* show directory as itself, not contents */
112#define DISP_RECURSIVE (1U<<19) /* show directory and everything below it */
113#define DISP_ROWS (1U<<20) /* print across rows */
114
115#define DISP_MASK (((DISP_ROWS << 1) - 1) & ~(DISP_NORMAL - 1))
116
117#ifdef CONFIG_FEATURE_LS_SORTFILES
118/* how will the files be sorted */
119#define SORT_ORDER_FORWARD 0 /* sort in reverse order */
120#define SORT_ORDER_REVERSE (1U<<27) /* sort in reverse order */
121
122#define SORT_NAME 0 /* sort by file name */
123#define SORT_SIZE (1U<<28) /* sort by file size */
124#define SORT_ATIME (2U<<28) /* sort by last access time */
125#define SORT_CTIME (3U<<28) /* sort by last change time */
126#define SORT_MTIME (4U<<28) /* sort by last modification time */
127#define SORT_VERSION (5U<<28) /* sort by version */
128#define SORT_EXT (6U<<28) /* sort by file name extension */
129#define SORT_DIR (7U<<28) /* sort by file or directory */
130
131#define SORT_MASK (7U<<28)
132#endif
133
134#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
135/* which of the three times will be used */
136#define TIME_MOD 0
137#define TIME_CHANGE (1U<<23)
138#define TIME_ACCESS (1U<<24)
139
140#define TIME_MASK (3U<<23)
141#endif
142
143#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
144#define FOLLOW_LINKS (1U<<25)
145#endif
146#ifdef CONFIG_FEATURE_HUMAN_READABLE
147#define LS_DISP_HR (1U<<26)
148#endif
149
150#define LIST_SHORT (LIST_FILENAME)
151#define LIST_ISHORT (LIST_INO | LIST_FILENAME)
152#define LIST_LONG (LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
153 LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK)
154#define LIST_ILONG (LIST_INO | LIST_LONG)
155
156#define SPLIT_DIR 1
157#define SPLIT_FILE 0
158#define SPLIT_SUBDIR 2
159
160#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f)
161#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)])
162
163#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined(CONFIG_FEATURE_LS_COLOR)
164# define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)])
165#endif
166
167/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
168#ifdef CONFIG_FEATURE_LS_COLOR
169static int show_color = 0;
170
171#define COLOR(mode) ("\000\043\043\043\042\000\043\043"\
172 "\000\000\044\000\043\000\000\040" [TYPEINDEX(mode)])
173#define ATTR(mode) ("\00\00\01\00\01\00\01\00"\
174 "\00\00\01\00\01\00\00\01" [TYPEINDEX(mode)])
175#endif
176
177/*
178 * a directory entry and its stat info are stored here
179 */
180struct dnode { /* the basic node */
181 char *name; /* the dir entry name */
182 char *fullname; /* the dir entry name */
183 struct stat dstat; /* the file stat info */
184#ifdef CONFIG_SELINUX
185 security_id_t sid;
186#endif
187 struct dnode *next; /* point at the next node */
188};
189typedef struct dnode dnode_t;
190
191static struct dnode **list_dir(const char *);
192static struct dnode **dnalloc(int);
193static int list_single(struct dnode *);
194
195static unsigned int all_fmt;
196
197#ifdef CONFIG_SELINUX
198static int is_flask_enabled_flag;
199#endif
200
201#ifdef CONFIG_FEATURE_AUTOWIDTH
202static int terminal_width = TERMINAL_WIDTH;
203static unsigned short tabstops = COLUMN_GAP;
204#else
205#define tabstops COLUMN_GAP
206#define terminal_width TERMINAL_WIDTH
207#endif
208
209static int status = EXIT_SUCCESS;
210
211static struct dnode *my_stat(char *fullname, char *name)
212{
213 struct stat dstat;
214 struct dnode *cur;
215#ifdef CONFIG_SELINUX
216 security_id_t sid;
217#endif
218 int rc;
219
220#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
221 if (all_fmt & FOLLOW_LINKS) {
222#ifdef CONFIG_SELINUX
223 if(is_flask_enabled_flag)
224 rc = stat_secure(fullname, &dstat, &sid);
225 else
226#endif
227 rc = stat(fullname, &dstat);
228 if(rc)
229 {
230 bb_perror_msg("%s", fullname);
231 status = EXIT_FAILURE;
232 return 0;
233 }
234 } else
235#endif
236 {
237#ifdef CONFIG_SELINUX
238 if(is_flask_enabled_flag)
239 rc = lstat_secure(fullname, &dstat, &sid);
240 else
241#endif
242 rc = lstat(fullname, &dstat);
243 if(rc)
244 {
245 bb_perror_msg("%s", fullname);
246 status = EXIT_FAILURE;
247 return 0;
248 }
249 }
250
251 cur = (struct dnode *) xmalloc(sizeof(struct dnode));
252 cur->fullname = fullname;
253 cur->name = name;
254 cur->dstat = dstat;
255#ifdef CONFIG_SELINUX
256 cur->sid = sid;
257#endif
258 return cur;
259}
260
261/*----------------------------------------------------------------------*/
262#ifdef CONFIG_FEATURE_LS_COLOR
263static char fgcolor(mode_t mode)
264{
265 /* Check wheter the file is existing (if so, color it red!) */
266 if (errno == ENOENT) {
267 return '\037';
268 }
269 if (LIST_EXEC && S_ISREG(mode)
270 && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
271 return COLOR(0xF000); /* File is executable ... */
272 return COLOR(mode);
273}
274
275/*----------------------------------------------------------------------*/
276static char bgcolor(mode_t mode)
277{
278 if (LIST_EXEC && S_ISREG(mode)
279 && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
280 return ATTR(0xF000); /* File is executable ... */
281 return ATTR(mode);
282}
283#endif
284
285/*----------------------------------------------------------------------*/
286#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined(CONFIG_FEATURE_LS_COLOR)
287static char append_char(mode_t mode)
288{
289 if (!(all_fmt & LIST_FILETYPE))
290 return '\0';
291 if ((all_fmt & LIST_EXEC) && S_ISREG(mode)
292 && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
293 return '*';
294 return APPCHAR(mode);
295}
296#endif
297
298/*----------------------------------------------------------------------*/
299
300#define countdirs(A,B) count_dirs((A), (B), 1)
301#define countsubdirs(A,B) count_dirs((A), (B), 0)
302
303static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs)
304{
305 int i, dirs;
306
307 if (dn == NULL || nfiles < 1)
308 return (0);
309 dirs = 0;
310 for (i = 0; i < nfiles; i++) {
311 if (S_ISDIR(dn[i]->dstat.st_mode)
312 && (notsubdirs
313 || ((dn[i]->name[0] != '.')
314 || (dn[i]->name[1]
315 && ((dn[i]->name[1] != '.')
316 || dn[i]->name[2])))))
317 dirs++;
318 }
319 return (dirs);
320}
321
322static int countfiles(struct dnode **dnp)
323{
324 int nfiles;
325 struct dnode *cur;
326
327 if (dnp == NULL)
328 return (0);
329 nfiles = 0;
330 for (cur = dnp[0]; cur->next != NULL; cur = cur->next)
331 nfiles++;
332 nfiles++;
333 return (nfiles);
334}
335
336/* get memory to hold an array of pointers */
337static struct dnode **dnalloc(int num)
338{
339 struct dnode **p;
340
341 if (num < 1)
342 return (NULL);
343
344 p = (struct dnode **) xcalloc((size_t) num,
345 (size_t) (sizeof(struct dnode *)));
346 return (p);
347}
348
349#ifdef CONFIG_FEATURE_LS_RECURSIVE
350static void dfree(struct dnode **dnp)
351{
352 struct dnode *cur, *next;
353
354 if (dnp == NULL)
355 return;
356
357 cur = dnp[0];
358 while (cur != NULL) {
359 free(cur->fullname); /* free the filename */
360 next = cur->next;
361 free(cur); /* free the dnode */
362 cur = next;
363 }
364 free(dnp); /* free the array holding the dnode pointers */
365}
366#endif
367
368static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which)
369{
370 int dncnt, i, d;
371 struct dnode **dnp;
372
373 if (dn == NULL || nfiles < 1)
374 return (NULL);
375
376 /* count how many dirs and regular files there are */
377 if (which == SPLIT_SUBDIR)
378 dncnt = countsubdirs(dn, nfiles);
379 else {
380 dncnt = countdirs(dn, nfiles); /* assume we are looking for dirs */
381 if (which == SPLIT_FILE)
382 dncnt = nfiles - dncnt; /* looking for files */
383 }
384
385 /* allocate a file array and a dir array */
386 dnp = dnalloc(dncnt);
387
388 /* copy the entrys into the file or dir array */
389 for (d = i = 0; i < nfiles; i++) {
390 if (S_ISDIR(dn[i]->dstat.st_mode)) {
391 if (which & (SPLIT_DIR|SPLIT_SUBDIR)) {
392 if ((which & SPLIT_DIR)
393 || ((dn[i]->name[0] != '.')
394 || (dn[i]->name[1]
395 && ((dn[i]->name[1] != '.')
396 || dn[i]->name[2])))) {
397 dnp[d++] = dn[i];
398 }
399 }
400 } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) {
401 dnp[d++] = dn[i];
402 }
403 }
404 return (dnp);
405}
406
407/*----------------------------------------------------------------------*/
408#ifdef CONFIG_FEATURE_LS_SORTFILES
409static int sortcmp(struct dnode *d1, struct dnode *d2)
410{
411 unsigned int sort_opts = all_fmt & SORT_MASK;
412 int dif;
413
414 dif = 0; /* assume SORT_NAME */
415 if (sort_opts == SORT_SIZE) {
416 dif = (int) (d2->dstat.st_size - d1->dstat.st_size);
417 } else if (sort_opts == SORT_ATIME) {
418 dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime);
419 } else if (sort_opts == SORT_CTIME) {
420 dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime);
421 } else if (sort_opts == SORT_MTIME) {
422 dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime);
423 } else if (sort_opts == SORT_DIR) {
424 dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode);
425 /* } else if (sort_opts == SORT_VERSION) { */
426 /* } else if (sort_opts == SORT_EXT) { */
427 }
428
429 if (dif == 0) {
430 /* sort by name- may be a tie_breaker for time or size cmp */
431#ifdef CONFIG_LOCALE_SUPPORT
432 dif = strcoll(d1->name, d2->name);
433#else
434 dif = strcmp(d1->name, d2->name);
435#endif
436 }
437
438 if (all_fmt & SORT_ORDER_REVERSE) {
439 dif = -dif;
440 }
441 return (dif);
442}
443
444/*----------------------------------------------------------------------*/
445static void shellsort(struct dnode **dn, int size)
446{
447 struct dnode *temp;
448 int gap, i, j;
449
450 /* shell short the array */
451 if (dn == NULL || size < 2)
452 return;
453
454 for (gap = size / 2; gap > 0; gap /= 2) {
455 for (i = gap; i < size; i++) {
456 for (j = i - gap; j >= 0; j -= gap) {
457 if (sortcmp(dn[j], dn[j + gap]) <= 0)
458 break;
459 /* they are out of order, swap them */
460 temp = dn[j];
461 dn[j] = dn[j + gap];
462 dn[j + gap] = temp;
463 }
464 }
465 }
466}
467#endif
468
469/*----------------------------------------------------------------------*/
470static void showfiles(struct dnode **dn, int nfiles)
471{
472 int i, ncols, nrows, row, nc;
473 int column = 0;
474 int nexttab = 0;
475 int column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */
476
477 if (dn == NULL || nfiles < 1)
478 return;
479
480 if (all_fmt & STYLE_ONE_RECORD_FLAG) {
481 ncols = 1;
482 } else {
483 /* find the longest file name- use that as the column width */
484 for (i = 0; i < nfiles; i++) {
485 int len = strlen(dn[i]->name) +
486#ifdef CONFIG_SELINUX
487 ((all_fmt & LIST_CONTEXT) ? 33 : 0) +
488#endif
489 ((all_fmt & LIST_INO) ? 8 : 0) +
490 ((all_fmt & LIST_BLOCKS) ? 5 : 0);
491 if (column_width < len)
492 column_width = len;
493 }
494 column_width += tabstops;
495 ncols = (int) (terminal_width / column_width);
496 }
497
498 if (ncols > 1) {
499 nrows = nfiles / ncols;
500 if ((nrows * ncols) < nfiles)
501 nrows++; /* round up fractionals */
502 } else {
503 nrows = nfiles;
504 ncols = 1;
505 }
506
507 for (row = 0; row < nrows; row++) {
508 for (nc = 0; nc < ncols; nc++) {
509 /* reach into the array based on the column and row */
510 i = (nc * nrows) + row; /* assume display by column */
511 if (all_fmt & DISP_ROWS)
512 i = (row * ncols) + nc; /* display across row */
513 if (i < nfiles) {
514 if (column > 0) {
515 nexttab -= column;
516 while (nexttab--) {
517 putchar(' ');
518 column++;
519 }
520 }
521 nexttab = column + column_width;
522 column += list_single(dn[i]);
523 }
524 }
525 putchar('\n');
526 column = 0;
527 }
528}
529
530/*----------------------------------------------------------------------*/
531static void showdirs(struct dnode **dn, int ndirs, int first)
532{
533 int i, nfiles;
534 struct dnode **subdnp;
535
536#ifdef CONFIG_FEATURE_LS_RECURSIVE
537 int dndirs;
538 struct dnode **dnd;
539#endif
540
541 if (dn == NULL || ndirs < 1)
542 return;
543
544 for (i = 0; i < ndirs; i++) {
545 if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) {
546 if (!first)
547 printf("\n");
548 first = 0;
549 printf("%s:\n", dn[i]->fullname);
550 }
551 subdnp = list_dir(dn[i]->fullname);
552 nfiles = countfiles(subdnp);
553 if (nfiles > 0) {
554 /* list all files at this level */
555#ifdef CONFIG_FEATURE_LS_SORTFILES
556 shellsort(subdnp, nfiles);
557#endif
558 showfiles(subdnp, nfiles);
559#ifdef CONFIG_FEATURE_LS_RECURSIVE
560 if (all_fmt & DISP_RECURSIVE) {
561 /* recursive- list the sub-dirs */
562 dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR);
563 dndirs = countsubdirs(subdnp, nfiles);
564 if (dndirs > 0) {
565#ifdef CONFIG_FEATURE_LS_SORTFILES
566 shellsort(dnd, dndirs);
567#endif
568 showdirs(dnd, dndirs, 0);
569 free(dnd); /* free the array of dnode pointers to the dirs */
570 }
571 }
572 dfree(subdnp); /* free the dnodes and the fullname mem */
573#endif
574 }
575 }
576}
577
578/*----------------------------------------------------------------------*/
579static struct dnode **list_dir(const char *path)
580{
581 struct dnode *dn, *cur, **dnp;
582 struct dirent *entry;
583 DIR *dir;
584 int i, nfiles;
585
586 if (path == NULL)
587 return (NULL);
588
589 dn = NULL;
590 nfiles = 0;
591 dir = opendir(path);
592 if (dir == NULL) {
593 bb_perror_msg("%s", path);
594 status = EXIT_FAILURE;
595 return (NULL); /* could not open the dir */
596 }
597 while ((entry = readdir(dir)) != NULL) {
598 char *fullname;
599
600 /* are we going to list the file- it may be . or .. or a hidden file */
601 if (entry->d_name[0] == '.') {
602 if ((entry->d_name[1] == 0 || (
603 entry->d_name[1] == '.'
604 && entry->d_name[2] == 0))
605 && !(all_fmt & DISP_DOT))
606 continue;
607 if (!(all_fmt & DISP_HIDDEN))
608 continue;
609 }
610 fullname = concat_path_file(path, entry->d_name);
611 cur = my_stat(fullname, strrchr(fullname, '/') + 1);
612 if (!cur)
613 continue;
614 cur->next = dn;
615 dn = cur;
616 nfiles++;
617 }
618 closedir(dir);
619
620 /* now that we know how many files there are
621 ** allocate memory for an array to hold dnode pointers
622 */
623 if (dn == NULL)
624 return (NULL);
625 dnp = dnalloc(nfiles);
626 for (i = 0, cur = dn; i < nfiles; i++) {
627 dnp[i] = cur; /* save pointer to node in array */
628 cur = cur->next;
629 }
630
631 return (dnp);
632}
633
634/*----------------------------------------------------------------------*/
635static int list_single(struct dnode *dn)
636{
637 int i, column = 0;
638
639#ifdef CONFIG_FEATURE_LS_USERNAME
640 char scratch[16];
641#endif
642#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
643 char *filetime;
644 time_t ttime, age;
645#endif
646#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined (CONFIG_FEATURE_LS_COLOR)
647 struct stat info;
648 char append;
649#endif
650
651 if (dn->fullname == NULL)
652 return (0);
653
654#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
655 ttime = dn->dstat.st_mtime; /* the default time */
656 if (all_fmt & TIME_ACCESS)
657 ttime = dn->dstat.st_atime;
658 if (all_fmt & TIME_CHANGE)
659 ttime = dn->dstat.st_ctime;
660 filetime = ctime(&ttime);
661#endif
662#ifdef CONFIG_FEATURE_LS_FILETYPES
663 append = append_char(dn->dstat.st_mode);
664#endif
665
666 for (i = 0; i <= 31; i++) {
667 switch (all_fmt & (1 << i)) {
668 case LIST_INO:
669 column += printf("%7ld ", (long int) dn->dstat.st_ino);
670 break;
671 case LIST_BLOCKS:
672#if _FILE_OFFSET_BITS == 64
673 column += printf("%4lld ", dn->dstat.st_blocks >> 1);
674#else
675 column += printf("%4ld ", dn->dstat.st_blocks >> 1);
676#endif
677 break;
678 case LIST_MODEBITS:
679 column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode));
680 break;
681 case LIST_NLINKS:
682 column += printf("%4ld ", (long) dn->dstat.st_nlink);
683 break;
684 case LIST_ID_NAME:
685#ifdef CONFIG_FEATURE_LS_USERNAME
686 my_getpwuid(scratch, dn->dstat.st_uid, sizeof(scratch));
687 printf("%-8.8s ", scratch);
688 my_getgrgid(scratch, dn->dstat.st_gid, sizeof(scratch));
689 printf("%-8.8s", scratch);
690 column += 17;
691 break;
692#endif
693 case LIST_ID_NUMERIC:
694 column += printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid);
695 break;
696 case LIST_SIZE:
697 case LIST_DEV:
698 if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) {
699 column += printf("%4d, %3d ", (int) major(dn->dstat.st_rdev),
700 (int) minor(dn->dstat.st_rdev));
701 } else {
702#ifdef CONFIG_FEATURE_HUMAN_READABLE
703 if (all_fmt & LS_DISP_HR) {
704 column += printf("%9s ",
705 make_human_readable_str(dn->dstat.st_size, 1, 0));
706 } else
707#endif
708 {
709#if _FILE_OFFSET_BITS == 64
710 column += printf("%9lld ", (long long) dn->dstat.st_size);
711#else
712 column += printf("%9ld ", dn->dstat.st_size);
713#endif
714 }
715 }
716 break;
717#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
718 case LIST_FULLTIME:
719 printf("%24.24s ", filetime);
720 column += 25;
721 break;
722 case LIST_DATE_TIME:
723 if ((all_fmt & LIST_FULLTIME) == 0) {
724 age = time(NULL) - ttime;
725 printf("%6.6s ", filetime + 4);
726 if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
727 /* hh:mm if less than 6 months old */
728 printf("%5.5s ", filetime + 11);
729 } else {
730 printf(" %4.4s ", filetime + 20);
731 }
732 column += 13;
733 }
734 break;
735#endif
736#ifdef CONFIG_SELINUX
737 case LIST_CONTEXT:
738 {
739 char context[64];
740 int len = sizeof(context);
741 if(security_sid_to_context(dn->sid, context, &len))
742 {
743 strcpy(context, "unknown");
744 len = 7;
745 }
746 printf("%-32s ", context);
747 column += MAX(33, len);
748 }
749 break;
750#endif
751 case LIST_FILENAME:
752#ifdef CONFIG_FEATURE_LS_COLOR
753 errno = 0;
754 if (show_color && !lstat(dn->fullname, &info)) {
755 printf("\033[%d;%dm", bgcolor(info.st_mode),
756 fgcolor(info.st_mode));
757 }
758#endif
759 column += printf("%s", dn->name);
760#ifdef CONFIG_FEATURE_LS_COLOR
761 if (show_color) {
762 printf("\033[0m");
763 }
764#endif
765 break;
766 case LIST_SYMLINK:
767 if (S_ISLNK(dn->dstat.st_mode)) {
768 char *lpath = xreadlink(dn->fullname);
769
770 if (lpath) {
771 printf(" -> ");
772#if defined(CONFIG_FEATURE_LS_FILETYPES) || defined (CONFIG_FEATURE_LS_COLOR)
773 if (!stat(dn->fullname, &info)) {
774 append = append_char(info.st_mode);
775 }
776#endif
777#ifdef CONFIG_FEATURE_LS_COLOR
778 if (show_color) {
779 errno = 0;
780 printf("\033[%d;%dm", bgcolor(info.st_mode),
781 fgcolor(info.st_mode));
782 }
783#endif
784 column += printf("%s", lpath) + 4;
785#ifdef CONFIG_FEATURE_LS_COLOR
786 if (show_color) {
787 printf("\033[0m");
788 }
789#endif
790 free(lpath);
791 }
792 }
793 break;
794#ifdef CONFIG_FEATURE_LS_FILETYPES
795 case LIST_FILETYPE:
796 if (append != '\0') {
797 printf("%1c", append);
798 column++;
799 }
800 break;
801#endif
802 }
803 }
804
805 return column;
806}
807
808/*----------------------------------------------------------------------*/
809
810/* "[-]Cadil1", POSIX mandated options, busybox always supports */
811/* "[-]gnsx", POSIX non-mandated options, busybox always supports */
812/* "[-]Ak" GNU options, busybox always supports */
813/* "[-]FLRctur", POSIX mandated options, busybox optionally supports */
814/* "[-]p", POSIX non-mandated options, busybox optionally supports */
815/* "[-]SXvThw", GNU options, busybox optionally supports */
816/* "[-]K", SELinux mandated options, busybox optionally supports */
817/* "[-]e", I think we made this one up */
818
819#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
820# define LS_STR_TIMESTAMPS "cetu"
821#else
822# define LS_STR_TIMESTAMPS ""
823#endif
824
825#ifdef CONFIG_FEATURE_LS_SORTFILES
826# define LS_STR_SORTFILES "SXrv"
827#else
828# define LS_STR_SORTFILES ""
829#endif
830
831#ifdef CONFIG_FEATURE_LS_FILETYPES
832# define LS_STR_FILETYPES "Fp"
833#else
834# define LS_STR_FILETYPES ""
835#endif
836
837#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
838# define LS_STR_FOLLOW_LINKS "L"
839#else
840# define LS_STR_FOLLOW_LINKS ""
841#endif
842
843#ifdef CONFIG_FEATURE_LS_RECURSIVE
844# define LS_STR_RECURSIVE "R"
845#else
846# define LS_STR_RECURSIVE ""
847#endif
848
849#ifdef CONFIG_FEATURE_HUMAN_READABLE
850# define LS_STR_HUMAN_READABLE "h"
851#else
852# define LS_STR_HUMAN_READABLE ""
853#endif
854
855#ifdef CONFIG_SELINUX
856# define LS_STR_SELINUX "K"
857#else
858# define LS_STR_SELINUX ""
859#endif
860
861#ifdef CONFIG_FEATURE_AUTOWIDTH
862# define LS_STR_AUTOWIDTH "T:w:"
863#else
864# define LS_STR_AUTOWIDTH ""
865#endif
866
867static const char ls_options[]="Cadil1gnsxAk" \
868 LS_STR_TIMESTAMPS \
869 LS_STR_SORTFILES \
870 LS_STR_FILETYPES \
871 LS_STR_FOLLOW_LINKS \
872 LS_STR_RECURSIVE \
873 LS_STR_HUMAN_READABLE \
874 LS_STR_SELINUX \
875 LS_STR_AUTOWIDTH;
876
877#define LIST_MASK_TRIGGER 0
878#define STYLE_MASK_TRIGGER STYLE_MASK
879#define SORT_MASK_TRIGGER SORT_MASK
880#define DISP_MASK_TRIGGER DISP_ROWS
881#define TIME_MASK_TRIGGER TIME_MASK
882
883static const unsigned opt_flags[] = {
884 LIST_SHORT | STYLE_COLUMNS, /* C */
885 DISP_HIDDEN | DISP_DOT, /* a */
886 DISP_NOLIST, /* d */
887 LIST_INO, /* i */
888 LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */
889 LIST_SHORT | STYLE_SINGLE, /* 1 */
890 0, /* g - ingored */
891 LIST_ID_NUMERIC, /* n */
892 LIST_BLOCKS, /* s */
893 DISP_ROWS, /* x */
894 DISP_HIDDEN, /* A */
895#ifdef CONFIG_SELINUX
896 LIST_CONTEXT, /* k */
897#else
898 0, /* k - ingored */
899#endif
900#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
901# ifdef CONFIG_FEATURE_LS_SORTFILES
902 TIME_CHANGE | SORT_CTIME, /* c */
903# else
904 TIME_CHANGE, /* c */
905# endif
906 LIST_FULLTIME, /* e */
907# ifdef CONFIG_FEATURE_LS_SORTFILES
908 SORT_MTIME, /* t */
909# else
910 0, /* t - ignored -- is this correct? */
911# endif
912# ifdef CONFIG_FEATURE_LS_SORTFILES
913 TIME_ACCESS | SORT_ATIME, /* u */
914# else
915 TIME_ACCESS, /* u */
916# endif
917#endif
918#ifdef CONFIG_FEATURE_LS_SORTFILES
919 SORT_SIZE, /* S */
920 SORT_EXT, /* X */
921 SORT_ORDER_REVERSE, /* r */
922 SORT_VERSION, /* v */
923#endif
924#ifdef CONFIG_FEATURE_LS_FILETYPES
925 LIST_FILETYPE | LIST_EXEC, /* F */
926 LIST_FILETYPE, /* p */
927#endif
928#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
929 FOLLOW_LINKS, /* L */
930#endif
931#ifdef CONFIG_FEATURE_LS_RECURSIVE
932 DISP_RECURSIVE, /* R */
933#endif
934#ifdef CONFIG_FEATURE_HUMAN_READABLE
935 LS_DISP_HR, /* h */
936#endif
937#ifdef CONFIG_SELINUX
938 LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */
939#endif
940 (1U<<31)
941};
942
943
944/*----------------------------------------------------------------------*/
945
946extern int ls_main(int argc, char **argv)
947{
948 struct dnode **dnd;
949 struct dnode **dnf;
950 struct dnode **dnp;
951 struct dnode *dn;
952 struct dnode *cur;
953 long opt;
954 int nfiles = 0;
955 int dnfiles;
956 int dndirs;
957 int oi;
958 int ac;
959 int i;
960 char **av;
961#ifdef CONFIG_FEATURE_AUTOWIDTH
962 char *tabstops_str = NULL;
963 char *terminal_width_str = NULL;
964#endif
965
966#ifdef CONFIG_SELINUX
967 is_flask_enabled_flag = is_flask_enabled();
968#endif
969
970 all_fmt = LIST_SHORT | DISP_NORMAL | STYLE_AUTO
971#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
972 | TIME_MOD
973#endif
974#ifdef CONFIG_FEATURE_LS_SORTFILES
975 | SORT_NAME | SORT_ORDER_FORWARD
976#endif
977 ;
978
979#ifdef CONFIG_FEATURE_AUTOWIDTH
980 /* Obtain the terminal width. */
981 get_terminal_width_height(STDOUT_FILENO, &terminal_width, NULL);
982 /* Go one less... */
983 terminal_width--;
984#endif
985
986#ifdef CONFIG_FEATURE_LS_COLOR
987 if (isatty(STDOUT_FILENO))
988 show_color = 1;
989#endif
990
991 /* process options */
992#ifdef CONFIG_FEATURE_AUTOWIDTH
993 opt = bb_getopt_ulflags(argc, argv, ls_options, &tabstops_str, &terminal_width_str);
994 if (tabstops_str) {
995 tabstops = atoi(tabstops_str);
996 }
997 if (terminal_width_str) {
998 terminal_width = atoi(terminal_width_str);
999 }
1000#else
1001 opt = bb_getopt_ulflags(argc, argv, ls_options);
1002#endif
1003 for (i = 0; opt_flags[i] != (1U<<31); i++) {
1004 if (opt & (1 << i)) {
1005 unsigned int flags = opt_flags[i];
1006 if (flags & LIST_MASK_TRIGGER) {
1007 all_fmt &= ~LIST_MASK;
1008 }
1009 if (flags & STYLE_MASK_TRIGGER) {
1010 all_fmt &= ~STYLE_MASK;
1011 }
1012#ifdef CONFIG_FEATURE_LS_SORTFILES
1013 if (flags & SORT_MASK_TRIGGER) {
1014 all_fmt &= ~SORT_MASK;
1015 }
1016#endif
1017 if (flags & DISP_MASK_TRIGGER) {
1018 all_fmt &= ~DISP_MASK;
1019 }
1020#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
1021 if (flags & TIME_MASK_TRIGGER) {
1022 all_fmt &= ~TIME_MASK;
1023 }
1024#endif
1025 if (flags & LIST_CONTEXT) {
1026 all_fmt |= STYLE_SINGLE;
1027 }
1028#ifdef CONFIG_FEATURE_HUMAN_READABLE
1029 if (opt == 'l') {
1030 all_fmt &= ~LS_DISP_HR;
1031 }
1032#endif
1033 all_fmt |= flags;
1034 }
1035 }
1036
1037 /* sort out which command line options take precedence */
1038#ifdef CONFIG_FEATURE_LS_RECURSIVE
1039 if (all_fmt & DISP_NOLIST)
1040 all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */
1041#endif
1042#if defined (CONFIG_FEATURE_LS_TIMESTAMPS) && defined (CONFIG_FEATURE_LS_SORTFILES)
1043 if (all_fmt & TIME_CHANGE)
1044 all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME;
1045 if (all_fmt & TIME_ACCESS)
1046 all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME;
1047#endif
1048 if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */
1049 all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC);
1050#ifdef CONFIG_FEATURE_LS_USERNAME
1051 if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC))
1052 all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */
1053#endif
1054
1055 /* choose a display format */
1056 if ((all_fmt & STYLE_MASK) == STYLE_AUTO)
1057#if STYLE_AUTO != 0
1058 all_fmt = (all_fmt & ~STYLE_MASK)
1059 | (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
1060#else
1061 all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE);
1062#endif
1063
1064 /*
1065 * when there are no cmd line args we have to supply a default "." arg.
1066 * we will create a second argv array, "av" that will hold either
1067 * our created "." arg, or the real cmd line args. The av array
1068 * just holds the pointers- we don't move the date the pointers
1069 * point to.
1070 */
1071 ac = argc - optind; /* how many cmd line args are left */
1072 if (ac < 1) {
1073 av = (char **) xcalloc((size_t) 1, (size_t) (sizeof(char *)));
1074 av[0] = bb_xstrdup(".");
1075 ac = 1;
1076 } else {
1077 av = (char **) xcalloc((size_t) ac, (size_t) (sizeof(char *)));
1078 for (oi = 0; oi < ac; oi++) {
1079 av[oi] = argv[optind++]; /* copy pointer to real cmd line arg */
1080 }
1081 }
1082
1083 /* now, everything is in the av array */
1084 if (ac > 1)
1085 all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */
1086
1087 /* stuff the command line file names into an dnode array */
1088 dn = NULL;
1089 for (oi = 0; oi < ac; oi++) {
1090 char *fullname = bb_xstrdup(av[oi]);
1091
1092 cur = my_stat(fullname, fullname);
1093 if (!cur)
1094 continue;
1095 cur->next = dn;
1096 dn = cur;
1097 nfiles++;
1098 }
1099
1100 /* now that we know how many files there are
1101 ** allocate memory for an array to hold dnode pointers
1102 */
1103 dnp = dnalloc(nfiles);
1104 for (i = 0, cur = dn; i < nfiles; i++) {
1105 dnp[i] = cur; /* save pointer to node in array */
1106 cur = cur->next;
1107 }
1108
1109 if (all_fmt & DISP_NOLIST) {
1110#ifdef CONFIG_FEATURE_LS_SORTFILES
1111 shellsort(dnp, nfiles);
1112#endif
1113 if (nfiles > 0)
1114 showfiles(dnp, nfiles);
1115 } else {
1116 dnd = splitdnarray(dnp, nfiles, SPLIT_DIR);
1117 dnf = splitdnarray(dnp, nfiles, SPLIT_FILE);
1118 dndirs = countdirs(dnp, nfiles);
1119 dnfiles = nfiles - dndirs;
1120 if (dnfiles > 0) {
1121#ifdef CONFIG_FEATURE_LS_SORTFILES
1122 shellsort(dnf, dnfiles);
1123#endif
1124 showfiles(dnf, dnfiles);
1125 }
1126 if (dndirs > 0) {
1127#ifdef CONFIG_FEATURE_LS_SORTFILES
1128 shellsort(dnd, dndirs);
1129#endif
1130 showdirs(dnd, dndirs, dnfiles == 0);
1131 }
1132 }
1133 return (status);
1134}
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 @@
1/*
2 * Copyright (C) 2003 Glenn L. McGrath
3 * Copyright (C) 2003-2004 Erik Andersen
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20#include <fcntl.h>
21#include <limits.h>
22#include <stdio.h>
23#include <stdint.h>
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27
28#include "busybox.h"
29
30
31#define FLAG_SILENT 1
32#define FLAG_CHECK 2
33#define FLAG_WARN 4
34
35/* This might be useful elsewhere */
36static unsigned char *hash_bin_to_hex(unsigned char *hash_value,
37 unsigned char hash_length)
38{
39 int x, len, max;
40 unsigned char *hex_value;
41
42 max = (hash_length * 2) + 2;
43 hex_value = xmalloc(max);
44 for (x = len = 0; x < hash_length; x++) {
45 len += snprintf(hex_value + len, max - len, "%02x", hash_value[x]);
46 }
47 return (hex_value);
48}
49
50static uint8_t *hash_file(const char *filename, uint8_t hash_algo)
51{
52 uint8_t *hash_value_bin;
53 uint8_t *hash_value = NULL;
54 uint8_t hash_length;
55 int src_fd;
56
57 if (strcmp(filename, "-") == 0) {
58 src_fd = STDIN_FILENO;
59 } else {
60 src_fd = open(filename, O_RDONLY);
61 }
62
63 if (hash_algo == HASH_MD5) {
64 hash_length = 16;
65 } else {
66 hash_length = 20;
67 }
68
69 hash_value_bin = xmalloc(hash_length);
70
71 if ((src_fd != -1) && (hash_fd(src_fd, -1, hash_algo, hash_value_bin) != -2)) {
72 hash_value = hash_bin_to_hex(hash_value_bin, hash_length);
73 } else {
74 bb_perror_msg("%s", filename);
75 }
76
77 close(src_fd);
78
79 return(hash_value);
80}
81
82/* This could become a common function for md5 as well, by using md5_stream */
83extern int hash_files(int argc, char **argv, const uint8_t hash_algo)
84{
85 int return_value = EXIT_SUCCESS;
86 uint8_t *hash_value;
87
88#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
89 unsigned int flags;
90
91 flags = bb_getopt_ulflags(argc, argv, "scw");
92#endif
93
94#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
95 if (!(flags & FLAG_CHECK)) {
96 if (flags & FLAG_SILENT) {
97 bb_error_msg_and_die
98 ("the -s option is meaningful only when verifying checksums");
99 } else if (flags & FLAG_WARN) {
100 bb_error_msg_and_die
101 ("the -w option is meaningful only when verifying checksums");
102 }
103 }
104#endif
105
106 if (argc == optind) {
107 argv[argc++] = "-";
108 }
109#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
110 if (flags & FLAG_CHECK) {
111 FILE *pre_computed_stream;
112 int count_total = 0;
113 int count_failed = 0;
114 unsigned char *file_ptr = argv[optind];
115 char *line;
116
117 if (optind + 1 != argc) {
118 bb_error_msg_and_die
119 ("only one argument may be specified when using -c");
120 }
121
122 if (strcmp(file_ptr, "-") == 0) {
123 pre_computed_stream = stdin;
124 } else {
125 pre_computed_stream = bb_xfopen(file_ptr, "r");
126 }
127
128 while ((line = bb_get_chomped_line_from_file(pre_computed_stream)) != NULL) {
129 char *filename_ptr;
130
131 count_total++;
132 filename_ptr = strstr(line, " ");
133 if (filename_ptr == NULL) {
134 if (flags & FLAG_WARN) {
135 bb_error_msg("Invalid format");
136 }
137 free(line);
138 continue;
139 }
140 *filename_ptr = '\0';
141 filename_ptr += 2;
142
143 hash_value = hash_file(filename_ptr, hash_algo);
144
145 if (hash_value && (strcmp(hash_value, line) == 0)) {
146 if (!(flags & FLAG_SILENT))
147 printf("%s: OK\n", filename_ptr);
148 } else {
149 if (!(flags & FLAG_SILENT))
150 printf("%s: FAILED\n", filename_ptr);
151 count_failed++;
152 return_value = EXIT_FAILURE;
153 }
154 /* possible free(NULL) */
155 free(hash_value);
156 free(line);
157 }
158 if (count_failed && !(flags & FLAG_SILENT)) {
159 bb_error_msg("WARNING: %d of %d computed checksums did NOT match",
160 count_failed, count_total);
161 }
162 if (bb_fclose_nonstdin(pre_computed_stream) == EOF) {
163 bb_perror_msg_and_die("Couldnt close file %s", file_ptr);
164 }
165 } else
166#endif
167 {
168 uint8_t hash_length;
169
170 if (hash_algo == HASH_MD5) {
171 hash_length = 16;
172 } else {
173 hash_length = 20;
174 }
175 hash_value = xmalloc(hash_length);
176
177 while (optind < argc) {
178 unsigned char *file_ptr = argv[optind++];
179
180 hash_value = hash_file(file_ptr, hash_algo);
181 if (hash_value == NULL) {
182 return_value = EXIT_FAILURE;
183 } else {
184 printf("%s %s\n", hash_value, file_ptr);
185 free(hash_value);
186 }
187 }
188 }
189 return (return_value);
190}
191
192#ifdef CONFIG_MD5SUM
193extern int md5sum_main(int argc, char **argv)
194{
195 return(hash_files(argc, argv, HASH_MD5));
196}
197#endif
198
199#ifdef CONFIG_SHA1SUM
200extern int sha1sum_main(int argc, char **argv)
201{
202 return(hash_files(argc, argv, HASH_SHA1));
203}
204#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini mkdir implementation for busybox
4 *
5 * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkdir.html */
25
26/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
27 *
28 * Fixed broken permission setting when -p was used; especially in
29 * conjunction with -m.
30 */
31
32#include <stdlib.h>
33#include <unistd.h>
34#include <getopt.h>
35#include "busybox.h"
36
37static const struct option mkdir_long_options[] = {
38 { "mode", 1, NULL, 'm' },
39 { "parents", 0, NULL, 'p' },
40 { 0, 0, 0, 0 }
41};
42
43extern int mkdir_main (int argc, char **argv)
44{
45 mode_t mode = (mode_t)(-1);
46 int status = EXIT_SUCCESS;
47 int flags = 0;
48 unsigned long opt;
49 char *smode;
50
51 bb_applet_long_options = mkdir_long_options;
52 opt = bb_getopt_ulflags(argc, argv, "m:p", &smode);
53 if(opt & 1) {
54 mode = 0777;
55 if (!bb_parse_mode (smode, &mode)) {
56 bb_error_msg_and_die ("invalid mode `%s'", smode);
57 }
58 }
59 if(opt & 2)
60 flags |= FILEUTILS_RECUR;
61
62 if (optind == argc) {
63 bb_show_usage();
64 }
65
66 argv += optind;
67
68 do {
69 if (bb_make_directory(*argv, mode, flags)) {
70 status = EXIT_FAILURE;
71 }
72 } while (*++argv);
73
74 return status;
75}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * mkfifo implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkfifo.html */
25
26#include <stdlib.h>
27#include <unistd.h>
28#include <sys/types.h>
29#include "busybox.h"
30#include "libcoreutils/coreutils.h"
31
32extern int mkfifo_main(int argc, char **argv)
33{
34 mode_t mode;
35 int retval = EXIT_SUCCESS;
36
37 mode = getopt_mk_fifo_nod(argc, argv);
38
39 if (!*(argv += optind)) {
40 bb_show_usage();
41 }
42
43 do {
44 if (mkfifo(*argv, mode) < 0) {
45 bb_perror_msg("%s", *argv); /* Avoid multibyte problems. */
46 retval = EXIT_FAILURE;
47 }
48 } while (*++argv);
49
50 return retval;
51}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * mknod implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
24
25#include <stdlib.h>
26#include <string.h>
27#include <sys/stat.h>
28#include <unistd.h>
29#include "busybox.h"
30#include "libcoreutils/coreutils.h"
31
32static const char modes_chars[] = { 'p', 'c', 'u', 'b', 0, 1, 1, 2 };
33static const mode_t modes_cubp[] = { S_IFIFO, S_IFCHR, S_IFBLK };
34
35extern int mknod_main(int argc, char **argv)
36{
37 mode_t mode;
38 dev_t dev;
39 const char *name;
40
41 mode = getopt_mk_fifo_nod(argc, argv);
42 argv += optind;
43 argc -= optind;
44
45 if ((argc >= 2) && ((name = strchr(modes_chars, argv[1][0])) != NULL)) {
46 mode |= modes_cubp[(int)(name[4])];
47
48 dev = 0;
49 if ((*name != 'p') && ((argc -= 2) == 2)) {
50 dev = (bb_xgetularg10_bnd(argv[2], 0, 255) << 8)
51 + bb_xgetularg10_bnd(argv[3], 0, 255);
52 }
53
54 if (argc == 2) {
55 name = *argv;
56 if (mknod(name, mode, dev) == 0) {
57 return EXIT_SUCCESS;
58 }
59 bb_perror_msg_and_die("%s", name);
60 }
61 }
62 bb_show_usage();
63}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini mv implementation for busybox
4 *
5 * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
24 *
25 * Size reduction and improved error checking.
26 */
27
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <unistd.h>
31#include <dirent.h>
32#include <errno.h>
33#include <stdlib.h>
34#include <getopt.h>
35#include "busybox.h"
36#include "libcoreutils/coreutils.h"
37
38static const struct option mv_long_options[] = {
39 { "interactive", 0, NULL, 'i' },
40 { "force", 0, NULL, 'f' },
41 { 0, 0, 0, 0 }
42};
43
44#define OPT_FILEUTILS_FORCE 1
45#define OPT_FILEUTILS_INTERACTIVE 2
46
47static const char fmt[] = "cannot overwrite %sdirectory with %sdirectory";
48
49extern int mv_main(int argc, char **argv)
50{
51 struct stat dest_stat;
52 const char *last;
53 const char *dest;
54 unsigned long flags;
55 int dest_exists;
56 int status = 0;
57
58 bb_applet_long_options = mv_long_options;
59 bb_opt_complementaly = "f-i:i-f";
60 flags = bb_getopt_ulflags(argc, argv, "fi");
61 if (optind + 2 > argc) {
62 bb_show_usage();
63 }
64
65 last = argv[argc - 1];
66 argv += optind;
67
68 if (optind + 2 == argc) {
69 if ((dest_exists = cp_mv_stat(last, &dest_stat)) < 0) {
70 return 1;
71 }
72
73 if (!(dest_exists & 2)) {
74 dest = last;
75 goto DO_MOVE;
76 }
77 }
78
79 do {
80 dest = concat_path_file(last, bb_get_last_path_component(*argv));
81
82 if ((dest_exists = cp_mv_stat(dest, &dest_stat)) < 0) {
83 goto RET_1;
84 }
85
86DO_MOVE:
87
88 if (dest_exists && !(flags & OPT_FILEUTILS_FORCE) &&
89 ((access(dest, W_OK) < 0 && isatty(0)) ||
90 (flags & OPT_FILEUTILS_INTERACTIVE))) {
91 if (fprintf(stderr, "mv: overwrite `%s'? ", dest) < 0) {
92 goto RET_1; /* Ouch! fprintf failed! */
93 }
94 if (!bb_ask_confirmation()) {
95 goto RET_0;
96 }
97 }
98 if (rename(*argv, dest) < 0) {
99 struct stat source_stat;
100 int source_exists;
101
102 if (errno != EXDEV) {
103 bb_perror_msg("unable to rename `%s'", *argv);
104 }
105 else if ((source_exists = cp_mv_stat(*argv, &source_stat)) >= 0) {
106 if (dest_exists) {
107 if (dest_exists == 3) {
108 if (source_exists != 3) {
109 bb_error_msg(fmt, "", "non-");
110 goto RET_1;
111 }
112 } else {
113 if (source_exists == 3) {
114 bb_error_msg(fmt, "non-", "");
115 goto RET_1;
116 }
117 }
118 if (unlink(dest) < 0) {
119 bb_perror_msg("cannot remove `%s'", dest);
120 goto RET_1;
121 }
122 }
123 if ((copy_file(*argv, dest,
124 FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS) >= 0) &&
125 (remove_file(*argv, FILEUTILS_RECUR | FILEUTILS_FORCE) >= 0)) {
126 goto RET_0;
127 }
128 }
129RET_1:
130 status = 1;
131 }
132RET_0:
133 if (dest != last) {
134 free((void *) dest);
135 }
136 } while (*++argv != last);
137
138 return (status);
139}
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 @@
1/*
2 * od implementation for busybox
3 * Based on code from util-linux v 2.11l
4 *
5 * Copyright (c) 1990
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * Original copyright notice is retained at the end of this file.
23 */
24
25#include <ctype.h>
26#include <string.h>
27#include <getopt.h>
28#include <stdlib.h>
29#include "busybox.h"
30#include "dump.h"
31
32#define isdecdigit(c) (isdigit)(c)
33#define ishexdigit(c) (isxdigit)(c)
34
35static void
36odoffset(int argc, char ***argvp)
37{
38 register char *num, *p;
39 int base;
40 char *end;
41
42 /*
43 * The offset syntax of od(1) was genuinely bizarre. First, if
44 * it started with a plus it had to be an offset. Otherwise, if
45 * there were at least two arguments, a number or lower-case 'x'
46 * followed by a number makes it an offset. By default it was
47 * octal; if it started with 'x' or '0x' it was hex. If it ended
48 * in a '.', it was decimal. If a 'b' or 'B' was appended, it
49 * multiplied the number by 512 or 1024 byte units. There was
50 * no way to assign a block count to a hex offset.
51 *
52 * We assumes it's a file if the offset is bad.
53 */
54 p = **argvp;
55
56 if (!p) {
57 /* hey someone is probably piping to us ... */
58 return;
59 }
60
61 if ((*p != '+')
62 && (argc < 2
63 || (!isdecdigit(p[0])
64 && ((p[0] != 'x') || !ishexdigit(p[1])))))
65 return;
66
67 base = 0;
68 /*
69 * bb_dump_skip over leading '+', 'x[0-9a-fA-f]' or '0x', and
70 * set base.
71 */
72 if (p[0] == '+')
73 ++p;
74 if (p[0] == 'x' && ishexdigit(p[1])) {
75 ++p;
76 base = 16;
77 } else if (p[0] == '0' && p[1] == 'x') {
78 p += 2;
79 base = 16;
80 }
81
82 /* bb_dump_skip over the number */
83 if (base == 16)
84 for (num = p; ishexdigit(*p); ++p);
85 else
86 for (num = p; isdecdigit(*p); ++p);
87
88 /* check for no number */
89 if (num == p)
90 return;
91
92 /* if terminates with a '.', base is decimal */
93 if (*p == '.') {
94 if (base)
95 return;
96 base = 10;
97 }
98
99 bb_dump_skip = strtol(num, &end, base ? base : 8);
100
101 /* if end isn't the same as p, we got a non-octal digit */
102 if (end != p)
103 bb_dump_skip = 0;
104 else {
105 if (*p) {
106 if (*p == 'b') {
107 bb_dump_skip *= 512;
108 ++p;
109 } else if (*p == 'B') {
110 bb_dump_skip *= 1024;
111 ++p;
112 }
113 }
114 if (*p)
115 bb_dump_skip = 0;
116 else {
117 ++*argvp;
118 /*
119 * If the offset uses a non-octal base, the base of
120 * the offset is changed as well. This isn't pretty,
121 * but it's easy.
122 */
123#define TYPE_OFFSET 7
124 {
125 char x_or_d;
126 if (base == 16) {
127 x_or_d = 'x';
128 goto DO_X_OR_D;
129 }
130 if (base == 10) {
131 x_or_d = 'd';
132 DO_X_OR_D:
133 bb_dump_fshead->nextfu->fmt[TYPE_OFFSET]
134 = bb_dump_fshead->nextfs->nextfu->fmt[TYPE_OFFSET]
135 = x_or_d;
136 }
137 }
138 }
139 }
140}
141
142static const char * const add_strings[] = {
143 "16/1 \"%3_u \" \"\\n\"", /* a */
144 "8/2 \" %06o \" \"\\n\"", /* B, o */
145 "16/1 \"%03o \" \"\\n\"", /* b */
146 "16/1 \"%3_c \" \"\\n\"", /* c */
147 "8/2 \" %05u \" \"\\n\"", /* d */
148 "4/4 \" %010u \" \"\\n\"", /* D */
149 "2/8 \" %21.14e \" \"\\n\"", /* e (undocumented in od), F */
150 "4/4 \" %14.7e \" \"\\n\"", /* f */
151 "4/4 \" %08x \" \"\\n\"", /* H, X */
152 "8/2 \" %04x \" \"\\n\"", /* h, x */
153 "4/4 \" %11d \" \"\\n\"", /* I, L, l */
154 "8/2 \" %6d \" \"\\n\"", /* i */
155 "4/4 \" %011o \" \"\\n\"", /* O */
156};
157
158static const signed char od_opts[] = "aBbcDdeFfHhIiLlOoXxv";
159
160static const signed char od_o2si[] = {
161 0, 1, 2, 3, 5,
162 4, 6, 6, 7, 8,
163 9, 0xa, 0xb, 0xa, 0xa,
164 0xb, 1, 8, 9,
165};
166
167int od_main(int argc, char **argv)
168{
169 int ch;
170 int first = 1;
171 signed char *p;
172 bb_dump_vflag = FIRST;
173 bb_dump_length = -1;
174
175 while ((ch = getopt(argc, argv, od_opts)) > 0) {
176 if (ch == 'v') {
177 bb_dump_vflag = ALL;
178 } else if (((p = strchr(od_opts, ch)) != NULL) && (*p >= 0)) {
179 if (first) {
180 first = 0;
181 bb_dump_add("\"%07.7_Ao\n\"");
182 bb_dump_add("\"%07.7_ao \"");
183 } else {
184 bb_dump_add("\" \"");
185 }
186 bb_dump_add(add_strings[od_o2si[(int)(p-od_opts)]]);
187 } else { /* P, p, s, w, or other unhandled */
188 bb_show_usage();
189 }
190 }
191 if (!bb_dump_fshead) {
192 bb_dump_add("\"%07.7_Ao\n\"");
193 bb_dump_add("\"%07.7_ao \" 8/2 \"%06o \" \"\\n\"");
194 }
195
196 argc -= optind;
197 argv += optind;
198
199 odoffset(argc, &argv);
200
201 return(bb_dump_dump(argv));
202}
203
204/*-
205 * Copyright (c) 1990 The Regents of the University of California.
206 * All rights reserved.
207 *
208 * Redistribution and use in source and binary forms, with or without
209 * modification, are permitted provided that the following conditions
210 * are met:
211 * 1. Redistributions of source code must retain the above copyright
212 * notice, this list of conditions and the following disclaimer.
213 * 2. Redistributions in binary form must reproduce the above copyright
214 * notice, this list of conditions and the following disclaimer in the
215 * documentation and/or other materials provided with the distribution.
216 * 3. Neither the name of the University nor the names of its contributors
217 * may be used to endorse or promote products derived from this software
218 * without specific prior written permission.
219 *
220 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
222 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
223 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
224 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
225 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
226 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
227 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
228 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
229 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
230 * SUCH DAMAGE.
231 */
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 @@
1/* vi: set sw=4 ts=4: */
2/* printf - format and print data
3 Copyright (C) 90, 91, 92, 93, 94, 95, 1996 Free Software Foundation, Inc.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18
19/* Usage: printf format [argument...]
20
21 A front end to the printf function that lets it be used from the shell.
22
23 Backslash escapes:
24
25 \" = double quote
26 \\ = backslash
27 \a = alert (bell)
28 \b = backspace
29 \c = produce no further output
30 \f = form feed
31 \n = new line
32 \r = carriage return
33 \t = horizontal tab
34 \v = vertical tab
35 \0ooo = octal number (ooo is 0 to 3 digits)
36 \xhhh = hexadecimal number (hhh is 1 to 3 digits)
37
38 Additional directive:
39
40 %b = print an argument string, interpreting backslash escapes
41
42 The `format' argument is re-used as many times as necessary
43 to convert all of the given arguments.
44
45 David MacKenzie <djm@gnu.ai.mit.edu> */
46
47
48// 19990508 Busy Boxed! Dave Cinege
49
50#include <unistd.h>
51#include <stdio.h>
52#include <sys/types.h>
53#include <string.h>
54#include <errno.h>
55#include <stdlib.h>
56#include <fcntl.h>
57#include <ctype.h>
58#include <assert.h>
59#include "busybox.h"
60
61static double xstrtod __P((char *s));
62static long xstrtol __P((char *s));
63static unsigned long xstrtoul __P((char *s));
64static void print_esc_string __P((char *str));
65static int print_formatted __P((char *format, int argc, char **argv));
66static void print_direc __P( (char *start, size_t length,
67 int field_width, int precision, char *argument));
68
69int printf_main(int argc, char **argv)
70{
71 char *format;
72 int args_used;
73
74 if (argc <= 1 || **(argv + 1) == '-') {
75 bb_show_usage();
76 }
77
78 format = argv[1];
79 argc -= 2;
80 argv += 2;
81
82 do {
83 args_used = print_formatted(format, argc, argv);
84 argc -= args_used;
85 argv += args_used;
86 }
87 while (args_used > 0 && argc > 0);
88
89/*
90 if (argc > 0)
91 fprintf(stderr, "excess args ignored");
92*/
93
94 return EXIT_SUCCESS;
95}
96
97/* Print the text in FORMAT, using ARGV (with ARGC elements) for
98 arguments to any `%' directives.
99 Return the number of elements of ARGV used. */
100
101static int print_formatted(char *format, int argc, char **argv)
102{
103 int save_argc = argc; /* Preserve original value. */
104 char *f; /* Pointer into `format'. */
105 char *direc_start; /* Start of % directive. */
106 size_t direc_length; /* Length of % directive. */
107 int field_width; /* Arg to first '*', or -1 if none. */
108 int precision; /* Arg to second '*', or -1 if none. */
109
110 for (f = format; *f; ++f) {
111 switch (*f) {
112 case '%':
113 direc_start = f++;
114 direc_length = 1;
115 field_width = precision = -1;
116 if (*f == '%') {
117 putchar('%');
118 break;
119 }
120 if (*f == 'b') {
121 if (argc > 0) {
122 print_esc_string(*argv);
123 ++argv;
124 --argc;
125 }
126 break;
127 }
128 if (strchr("-+ #", *f)) {
129 ++f;
130 ++direc_length;
131 }
132 if (*f == '*') {
133 ++f;
134 ++direc_length;
135 if (argc > 0) {
136 field_width = xstrtoul(*argv);
137 ++argv;
138 --argc;
139 } else
140 field_width = 0;
141 } else
142 while (isdigit(*f)) {
143 ++f;
144 ++direc_length;
145 }
146 if (*f == '.') {
147 ++f;
148 ++direc_length;
149 if (*f == '*') {
150 ++f;
151 ++direc_length;
152 if (argc > 0) {
153 precision = xstrtoul(*argv);
154 ++argv;
155 --argc;
156 } else
157 precision = 0;
158 } else
159 while (isdigit(*f)) {
160 ++f;
161 ++direc_length;
162 }
163 }
164 if (*f == 'l' || *f == 'L' || *f == 'h') {
165 ++f;
166 ++direc_length;
167 }
168 /*
169 if (!strchr ("diouxXfeEgGcs", *f))
170 fprintf(stderr, "%%%c: invalid directive", *f);
171 */
172 ++direc_length;
173 if (argc > 0) {
174 print_direc(direc_start, direc_length, field_width,
175 precision, *argv);
176 ++argv;
177 --argc;
178 } else
179 print_direc(direc_start, direc_length, field_width,
180 precision, "");
181 break;
182
183 case '\\':
184 if (*++f == 'c')
185 exit(0);
186 putchar(bb_process_escape_sequence((const char **)&f));
187 f--;
188 break;
189
190 default:
191 putchar(*f);
192 }
193 }
194
195 return save_argc - argc;
196}
197
198static void
199print_direc(char *start, size_t length, int field_width, int precision,
200 char *argument)
201{
202 char *p; /* Null-terminated copy of % directive. */
203
204 p = xmalloc((unsigned) (length + 1));
205 strncpy(p, start, length);
206 p[length] = 0;
207
208 switch (p[length - 1]) {
209 case 'd':
210 case 'i':
211 if (field_width < 0) {
212 if (precision < 0)
213 printf(p, xstrtol(argument));
214 else
215 printf(p, precision, xstrtol(argument));
216 } else {
217 if (precision < 0)
218 printf(p, field_width, xstrtol(argument));
219 else
220 printf(p, field_width, precision, xstrtol(argument));
221 }
222 break;
223
224 case 'o':
225 case 'u':
226 case 'x':
227 case 'X':
228 if (field_width < 0) {
229 if (precision < 0)
230 printf(p, xstrtoul(argument));
231 else
232 printf(p, precision, xstrtoul(argument));
233 } else {
234 if (precision < 0)
235 printf(p, field_width, xstrtoul(argument));
236 else
237 printf(p, field_width, precision, xstrtoul(argument));
238 }
239 break;
240
241 case 'f':
242 case 'e':
243 case 'E':
244 case 'g':
245 case 'G':
246 if (field_width < 0) {
247 if (precision < 0)
248 printf(p, xstrtod(argument));
249 else
250 printf(p, precision, xstrtod(argument));
251 } else {
252 if (precision < 0)
253 printf(p, field_width, xstrtod(argument));
254 else
255 printf(p, field_width, precision, xstrtod(argument));
256 }
257 break;
258
259 case 'c':
260 printf(p, *argument);
261 break;
262
263 case 's':
264 if (field_width < 0) {
265 if (precision < 0)
266 printf(p, argument);
267 else
268 printf(p, precision, argument);
269 } else {
270 if (precision < 0)
271 printf(p, field_width, argument);
272 else
273 printf(p, field_width, precision, argument);
274 }
275 break;
276 }
277
278 free(p);
279}
280
281static unsigned long xstrtoul(char *arg)
282{
283 unsigned long result;
284 if (safe_strtoul(arg, &result))
285 fprintf(stderr, "%s", arg);
286 return result;
287}
288
289static long xstrtol(char *arg)
290{
291 long result;
292 if (safe_strtol(arg, &result))
293 fprintf(stderr, "%s", arg);
294 return result;
295}
296
297static double xstrtod(char *arg)
298{
299 double result;
300 if (safe_strtod(arg, &result))
301 fprintf(stderr, "%s", arg);
302 return result;
303}
304
305static void print_esc_string(char *str)
306{
307 for (; *str; str++) {
308 if (*str == '\\') {
309 str++;
310 putchar(bb_process_escape_sequence((const char **)&str));
311 } else {
312 putchar(*str);
313 }
314
315 }
316}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini pwd implementation for busybox
4 *
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include "busybox.h"
26
27extern int pwd_main(int argc, char **argv)
28{
29 char *buf;
30
31 if ((buf = xgetcwd(NULL)) != NULL) {
32 puts(buf);
33 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
34 }
35
36 return EXIT_FAILURE;
37}
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */
18
19/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
20 *
21 * Now does proper error checking on output and returns a failure exit code
22 * if one or more paths can not be resolved.
23 */
24
25#include <limits.h>
26#include <stdlib.h>
27#include "busybox.h"
28
29int realpath_main(int argc, char **argv)
30{
31 int retval = EXIT_SUCCESS;
32
33 RESERVE_CONFIG_BUFFER(resolved_path, PATH_MAX);
34
35 if (--argc == 0) {
36 bb_show_usage();
37 }
38
39 do {
40 argv++;
41 if (realpath(*argv, resolved_path) != NULL) {
42 puts(resolved_path);
43 } else {
44 retval = EXIT_FAILURE;
45 bb_perror_msg("%s", *argv);
46 }
47 } while (--argc);
48
49#ifdef CONFIG_FEATURE_CLEAN_UP
50 RELEASE_CONFIG_BUFFER(resolved_path);
51#endif
52
53 bb_fflush_stdout_and_exit(retval);
54}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini rm implementation for busybox
4 *
5 * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
6 *
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24/* BB_AUDIT SUSv3 compliant */
25/* http://www.opengroup.org/onlinepubs/007904975/utilities/rm.html */
26
27/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
28 *
29 * Size reduction.
30 */
31
32#include <unistd.h>
33#include "busybox.h"
34
35extern int rm_main(int argc, char **argv)
36{
37 int status = 0;
38 int flags = 0;
39 unsigned long opt;
40
41 bb_opt_complementaly = "f-i:i-f";
42 opt = bb_getopt_ulflags(argc, argv, "fiRr");
43 if(opt & 1)
44 flags |= FILEUTILS_FORCE;
45 if(opt & 2)
46 flags |= FILEUTILS_INTERACTIVE;
47 if(opt & 12)
48 flags |= FILEUTILS_RECUR;
49
50 if (*(argv += optind) != NULL) {
51 do {
52 const char *base = bb_get_last_path_component(*argv);
53
54 if ((base[0] == '.') && (!base[1] || ((base[1] == '.') && !base[2]))) {
55 bb_error_msg("cannot remove `.' or `..'");
56 } else if (remove_file(*argv, flags) >= 0) {
57 continue;
58 }
59 status = 1;
60 } while (*++argv);
61 } else if (!(flags & FILEUTILS_FORCE)) {
62 bb_show_usage();
63 }
64
65 return status;
66}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * rmdir implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/rmdir.html */
25
26#include <stdlib.h>
27#include <unistd.h>
28#include <libgen.h>
29#include "busybox.h"
30
31extern int rmdir_main(int argc, char **argv)
32{
33 int status = EXIT_SUCCESS;
34 int flags;
35 int do_dot;
36 char *path;
37
38 flags = bb_getopt_ulflags(argc, argv, "p");
39
40 argv += optind;
41
42 if (!*argv) {
43 bb_show_usage();
44 }
45
46 do {
47 path = *argv;
48
49 /* Record if the first char was a '.' so we can use dirname later. */
50 do_dot = (*path == '.');
51
52 do {
53 if (rmdir(path) < 0) {
54 bb_perror_msg("`%s'", path); /* Match gnu rmdir msg. */
55 status = EXIT_FAILURE;
56 } else if (flags) {
57 /* Note: path was not empty or null since rmdir succeeded. */
58 path = dirname(path);
59 /* Path is now just the parent component. Note that dirname
60 * returns "." if there are no parents. We must distinguish
61 * this from the case of the original path starting with '.'.
62 */
63 if (do_dot || (*path != '.') || path[1]) {
64 continue;
65 }
66 }
67 break;
68 } while (1);
69
70 } while (*++argv);
71
72 return status;
73}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * seq implementation for busybox
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19#include <stdio.h>
20#include <stdlib.h>
21#include "busybox.h"
22
23extern int seq_main(int argc, char **argv)
24{
25 double last;
26 double first = 1;
27 double increment = 1;
28 double i;
29
30 if (argc == 4) {
31 first = atof(argv[1]);
32 increment = atof(argv[2]);
33 } else if (argc == 3) {
34 first = atof(argv[1]);
35 } else if (argc != 2) {
36 bb_show_usage();
37 }
38 last = atof(argv[argc - 1]);
39
40 /* You should note that this is pos-5.0.91 semantics, -- FK. */
41 if ((first > last) && (increment > 0)) {
42 return EXIT_SUCCESS;
43 }
44
45 for (i = first; ((first <= last) ? (i <= last) : (i >= last));
46 i += increment) {
47 printf("%g\n", i);
48 }
49
50 return EXIT_SUCCESS;
51}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * sleep implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant */
24/* BB_AUDIT GNU issues -- fancy version matches except args must be ints. */
25/* http://www.opengroup.org/onlinepubs/007904975/utilities/sleep.html */
26
27/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
28 *
29 * Rewritten to do proper arg and error checking.
30 * Also, added a 'fancy' configuration to accept multiple args with
31 * time suffixes for seconds, minutes, hours, and days.
32 */
33
34#include <stdlib.h>
35#include <limits.h>
36#include <unistd.h>
37#include "busybox.h"
38
39#ifdef CONFIG_FEATURE_FANCY_SLEEP
40static const struct suffix_mult sleep_suffixes[] = {
41 { "s", 1 },
42 { "m", 60 },
43 { "h", 60*60 },
44 { "d", 24*60*60 },
45 { NULL, 0 }
46};
47#endif
48
49extern int sleep_main(int argc, char **argv)
50{
51 unsigned int duration;
52
53#ifdef CONFIG_FEATURE_FANCY_SLEEP
54
55 if (argc < 2) {
56 bb_show_usage();
57 }
58
59 ++argv;
60 duration = 0;
61 do {
62 duration += bb_xgetularg_bnd_sfx(*argv, 10,
63 0, UINT_MAX-duration,
64 sleep_suffixes);
65 } while (*++argv);
66
67#else /* CONFIG_FEATURE_FANCY_SLEEP */
68
69 if (argc != 2) {
70 bb_show_usage();
71 }
72
73#if UINT_MAX == ULONG_MAX
74 duration = bb_xgetularg10(argv[1]);
75#else
76 duration = bb_xgetularg10_bnd(argv[1], 0, UINT_MAX);
77#endif
78
79#endif /* CONFIG_FEATURE_FANCY_SLEEP */
80
81 if (sleep(duration)) {
82 bb_perror_nomsg_and_die();
83 }
84
85 return EXIT_SUCCESS;
86}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini sort implementation for busybox
4 *
5 * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 _NOT_ compliant -- a number of options are not supported. */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html */
25
26/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
27 *
28 * Now does proper error checking on i/o. Plus some space savings.
29 */
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <unistd.h>
35#include "busybox.h"
36#include "libcoreutils/coreutils.h"
37
38static int compare_ascii(const void *x, const void *y)
39{
40 return strcmp(*(char **)x, *(char **)y);
41}
42
43static int compare_numeric(const void *x, const void *y)
44{
45 int z = atoi(*(char **)x) - atoi(*(char **)y);
46 return z ? z : strcmp(*(char **)x, *(char **)y);
47}
48
49int sort_main(int argc, char **argv)
50{
51 FILE *fp;
52 char *line, **lines = NULL;
53 int i, nlines = 0, inc;
54 int (*compare)(const void *, const void *) = compare_ascii;
55
56 int flags;
57
58 bb_default_error_retval = 2;
59
60 flags = bb_getopt_ulflags(argc, argv, "nru");
61 if (flags & 1) {
62 compare = compare_numeric;
63 }
64
65 argv += optind;
66 if (!*argv) {
67 *--argv = "-";
68 }
69
70 do {
71 fp = xgetoptfile_sort_uniq(argv, "r");
72 while ((line = bb_get_chomped_line_from_file(fp)) != NULL) {
73 lines = xrealloc(lines, sizeof(char *) * (nlines + 1));
74 lines[nlines++] = line;
75 }
76 bb_xferror(fp, *argv);
77 bb_fclose_nonstdin(fp);
78 } while (*++argv);
79
80 /* sort it */
81 qsort(lines, nlines, sizeof(char *), compare);
82
83 /* print it */
84 i = 0;
85 --nlines;
86 if ((inc = 1 - (flags & 2)) < 0) { /* reverse */
87 i = nlines;
88 }
89 flags &= 4;
90
91 while (nlines >= 0) {
92 if (!flags || !nlines || strcmp(lines[i+inc], lines[i])) {
93 puts(lines[i]);
94 }
95 i += inc;
96 --nlines;
97 }
98
99 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
100}
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 @@
1/* vi: set sw=4 ts=4: */
2/* stty -- change and print terminal line settings
3 Copyright (C) 1990-1999 Free Software Foundation, Inc.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 You should have received a copy of the GNU General Public License
11 along with this program; if not, write to the Free Software Foundation,
12 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
13
14/* Usage: stty [-ag] [-F device] [setting...]
15
16 Options:
17 -a Write all current settings to stdout in human-readable form.
18 -g Write all current settings to stdout in stty-readable form.
19 -F Open and use the specified device instead of stdin
20
21 If no args are given, write to stdout the baud rate and settings that
22 have been changed from their defaults. Mode reading and changes
23 are done on the specified device, or stdin if none was specified.
24
25 David MacKenzie <djm@gnu.ai.mit.edu>
26
27 Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
28
29 */
30
31//#define TEST
32
33#include <stddef.h>
34#include <termios.h>
35#include <sys/ioctl.h>
36
37#include <sys/param.h>
38#include <unistd.h>
39
40#ifndef STDIN_FILENO
41# define STDIN_FILENO 0
42#endif
43
44#ifndef STDOUT_FILENO
45# define STDOUT_FILENO 1
46#endif
47
48#include <stdlib.h>
49#include <string.h>
50#include <assert.h>
51#include <ctype.h>
52#include <errno.h>
53#include <limits.h>
54#include <memory.h>
55#include <fcntl.h>
56#include "busybox.h"
57
58#define STREQ(a, b) (strcmp ((a), (b)) == 0)
59
60
61#ifndef _POSIX_VDISABLE
62# define _POSIX_VDISABLE ((unsigned char) 0)
63#endif
64
65#define Control(c) ((c) & 0x1f)
66/* Canonical values for control characters. */
67#ifndef CINTR
68# define CINTR Control ('c')
69#endif
70#ifndef CQUIT
71# define CQUIT 28
72#endif
73#ifndef CERASE
74# define CERASE 127
75#endif
76#ifndef CKILL
77# define CKILL Control ('u')
78#endif
79#ifndef CEOF
80# define CEOF Control ('d')
81#endif
82#ifndef CEOL
83# define CEOL _POSIX_VDISABLE
84#endif
85#ifndef CSTART
86# define CSTART Control ('q')
87#endif
88#ifndef CSTOP
89# define CSTOP Control ('s')
90#endif
91#ifndef CSUSP
92# define CSUSP Control ('z')
93#endif
94#if defined(VEOL2) && !defined(CEOL2)
95# define CEOL2 _POSIX_VDISABLE
96#endif
97/* ISC renamed swtch to susp for termios, but we'll accept either name. */
98#if defined(VSUSP) && !defined(VSWTCH)
99# define VSWTCH VSUSP
100# define CSWTCH CSUSP
101#endif
102#if defined(VSWTCH) && !defined(CSWTCH)
103# define CSWTCH _POSIX_VDISABLE
104#endif
105
106/* SunOS 5.3 loses (^Z doesn't work) if `swtch' is the same as `susp'.
107 So the default is to disable `swtch.' */
108#if defined (__sparc__) && defined (__svr4__)
109# undef CSWTCH
110# define CSWTCH _POSIX_VDISABLE
111#endif
112
113#if defined(VWERSE) && !defined (VWERASE) /* AIX-3.2.5 */
114# define VWERASE VWERSE
115#endif
116#if defined(VDSUSP) && !defined (CDSUSP)
117# define CDSUSP Control ('y')
118#endif
119#if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */
120# define VREPRINT VRPRNT
121#endif
122#if defined(VREPRINT) && !defined(CRPRNT)
123# define CRPRNT Control ('r')
124#endif
125#if defined(VWERASE) && !defined(CWERASE)
126# define CWERASE Control ('w')
127#endif
128#if defined(VLNEXT) && !defined(CLNEXT)
129# define CLNEXT Control ('v')
130#endif
131#if defined(VDISCARD) && !defined(VFLUSHO)
132# define VFLUSHO VDISCARD
133#endif
134#if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */
135# define VFLUSHO VFLUSH
136#endif
137#if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */
138# define ECHOCTL CTLECH
139#endif
140#if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */
141# define ECHOCTL TCTLECH
142#endif
143#if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */
144# define ECHOKE CRTKIL
145#endif
146#if defined(VFLUSHO) && !defined(CFLUSHO)
147# define CFLUSHO Control ('o')
148#endif
149#if defined(VSTATUS) && !defined(CSTATUS)
150# define CSTATUS Control ('t')
151#endif
152
153/* Which speeds to set. */
154enum speed_setting {
155 input_speed, output_speed, both_speeds
156};
157
158/* Which member(s) of `struct termios' a mode uses. */
159enum mode_type {
160 /* Do NOT change the order or values, as mode_type_flag()
161 * depends on them. */
162 control, input, output, local, combination
163};
164
165
166static const char evenp [] = "evenp";
167static const char raw [] = "raw";
168static const char stty_min [] = "min";
169static const char stty_time [] = "time";
170static const char stty_swtch[] = "swtch";
171static const char stty_eol [] = "eol";
172static const char stty_eof [] = "eof";
173static const char parity [] = "parity";
174static const char stty_oddp [] = "oddp";
175static const char stty_nl [] = "nl";
176static const char stty_ek [] = "ek";
177static const char stty_sane [] = "sane";
178static const char cbreak [] = "cbreak";
179static const char stty_pass8[] = "pass8";
180static const char litout [] = "litout";
181static const char cooked [] = "cooked";
182static const char decctlq [] = "decctlq";
183static const char stty_tabs [] = "tabs";
184static const char stty_lcase[] = "lcase";
185static const char stty_LCASE[] = "LCASE";
186static const char stty_crt [] = "crt";
187static const char stty_dec [] = "dec";
188
189
190/* Flags for `struct mode_info'. */
191#define SANE_SET 1 /* Set in `sane' mode. */
192#define SANE_UNSET 2 /* Unset in `sane' mode. */
193#define REV 4 /* Can be turned off by prepending `-'. */
194#define OMIT 8 /* Don't display value. */
195
196/* Each mode. */
197struct mode_info {
198 const char *name; /* Name given on command line. */
199 /* enum mode_type type; */
200 char type; /* Which structure element to change. */
201 char flags; /* Setting and display options. */
202 unsigned short mask; /* Other bits to turn off for this mode. */
203 unsigned long bits; /* Bits to set for this mode. */
204};
205
206#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B }
207
208static const struct mode_info mode_info[] = {
209 MI_ENTRY("parenb", control, REV, PARENB, 0 ),
210 MI_ENTRY("parodd", control, REV, PARODD, 0 ),
211 MI_ENTRY("cs5", control, 0, CS5, CSIZE),
212 MI_ENTRY("cs6", control, 0, CS6, CSIZE),
213 MI_ENTRY("cs7", control, 0, CS7, CSIZE),
214 MI_ENTRY("cs8", control, 0, CS8, CSIZE),
215 MI_ENTRY("hupcl", control, REV, HUPCL, 0 ),
216 MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ),
217 MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ),
218 MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ),
219 MI_ENTRY("clocal", control, REV, CLOCAL, 0 ),
220#ifdef CRTSCTS
221 MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ),
222#endif
223 MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ),
224 MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ),
225 MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ),
226 MI_ENTRY("parmrk", input, REV, PARMRK, 0 ),
227 MI_ENTRY("inpck", input, REV, INPCK, 0 ),
228 MI_ENTRY("istrip", input, REV, ISTRIP, 0 ),
229 MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ),
230 MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ),
231 MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ),
232 MI_ENTRY("ixon", input, REV, IXON, 0 ),
233 MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ),
234 MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ),
235#ifdef IUCLC
236 MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ),
237#endif
238#ifdef IXANY
239 MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ),
240#endif
241#ifdef IMAXBEL
242 MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ),
243#endif
244 MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ),
245#ifdef OLCUC
246 MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ),
247#endif
248#ifdef OCRNL
249 MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ),
250#endif
251#ifdef ONLCR
252 MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ),
253#endif
254#ifdef ONOCR
255 MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ),
256#endif
257#ifdef ONLRET
258 MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ),
259#endif
260#ifdef OFILL
261 MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ),
262#endif
263#ifdef OFDEL
264 MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ),
265#endif
266#ifdef NLDLY
267 MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY),
268 MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY),
269#endif
270#ifdef CRDLY
271 MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY),
272 MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY),
273 MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY),
274 MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY),
275#endif
276
277#ifdef TABDLY
278 MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY),
279 MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY),
280 MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY),
281 MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY),
282#else
283# ifdef OXTABS
284 MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ),
285# endif
286#endif
287
288#ifdef BSDLY
289 MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY),
290 MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY),
291#endif
292#ifdef VTDLY
293 MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY),
294 MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY),
295#endif
296#ifdef FFDLY
297 MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY),
298 MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY),
299#endif
300 MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ),
301 MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ),
302#ifdef IEXTEN
303 MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ),
304#endif
305 MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ),
306 MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ),
307 MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ),
308 MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ),
309 MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ),
310 MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ),
311#ifdef XCASE
312 MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ),
313#endif
314#ifdef TOSTOP
315 MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ),
316#endif
317#ifdef ECHOPRT
318 MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ),
319 MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ),
320#endif
321#ifdef ECHOCTL
322 MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ),
323 MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ),
324#endif
325#ifdef ECHOKE
326 MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ),
327 MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ),
328#endif
329 MI_ENTRY(evenp, combination, REV | OMIT, 0, 0 ),
330 MI_ENTRY(parity, combination, REV | OMIT, 0, 0 ),
331 MI_ENTRY(stty_oddp, combination, REV | OMIT, 0, 0 ),
332 MI_ENTRY(stty_nl, combination, REV | OMIT, 0, 0 ),
333 MI_ENTRY(stty_ek, combination, OMIT, 0, 0 ),
334 MI_ENTRY(stty_sane, combination, OMIT, 0, 0 ),
335 MI_ENTRY(cooked, combination, REV | OMIT, 0, 0 ),
336 MI_ENTRY(raw, combination, REV | OMIT, 0, 0 ),
337 MI_ENTRY(stty_pass8, combination, REV | OMIT, 0, 0 ),
338 MI_ENTRY(litout, combination, REV | OMIT, 0, 0 ),
339 MI_ENTRY(cbreak, combination, REV | OMIT, 0, 0 ),
340#ifdef IXANY
341 MI_ENTRY(decctlq, combination, REV | OMIT, 0, 0 ),
342#endif
343#if defined (TABDLY) || defined (OXTABS)
344 MI_ENTRY(stty_tabs, combination, REV | OMIT, 0, 0 ),
345#endif
346#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
347 MI_ENTRY(stty_lcase, combination, REV | OMIT, 0, 0 ),
348 MI_ENTRY(stty_LCASE, combination, REV | OMIT, 0, 0 ),
349#endif
350 MI_ENTRY(stty_crt, combination, OMIT, 0, 0 ),
351 MI_ENTRY(stty_dec, combination, OMIT, 0, 0 ),
352};
353
354static const int NUM_mode_info =
355
356 (sizeof(mode_info) / sizeof(struct mode_info));
357
358/* Control character settings. */
359struct control_info {
360 const char *name; /* Name given on command line. */
361 unsigned char saneval; /* Value to set for `stty sane'. */
362 unsigned char offset; /* Offset in c_cc. */
363};
364
365/* Control characters. */
366
367static const struct control_info control_info[] = {
368 {"intr", CINTR, VINTR},
369 {"quit", CQUIT, VQUIT},
370 {"erase", CERASE, VERASE},
371 {"kill", CKILL, VKILL},
372 {stty_eof, CEOF, VEOF},
373 {stty_eol, CEOL, VEOL},
374#ifdef VEOL2
375 {"eol2", CEOL2, VEOL2},
376#endif
377#ifdef VSWTCH
378 {stty_swtch, CSWTCH, VSWTCH},
379#endif
380 {"start", CSTART, VSTART},
381 {"stop", CSTOP, VSTOP},
382 {"susp", CSUSP, VSUSP},
383#ifdef VDSUSP
384 {"dsusp", CDSUSP, VDSUSP},
385#endif
386#ifdef VREPRINT
387 {"rprnt", CRPRNT, VREPRINT},
388#endif
389#ifdef VWERASE
390 {"werase", CWERASE, VWERASE},
391#endif
392#ifdef VLNEXT
393 {"lnext", CLNEXT, VLNEXT},
394#endif
395#ifdef VFLUSHO
396 {"flush", CFLUSHO, VFLUSHO},
397#endif
398#ifdef VSTATUS
399 {"status", CSTATUS, VSTATUS},
400#endif
401 /* These must be last because of the display routines. */
402 {stty_min, 1, VMIN},
403 {stty_time, 0, VTIME},
404};
405
406static const int NUM_control_info =
407 (sizeof(control_info) / sizeof(struct control_info));
408
409
410static const char * visible(unsigned int ch);
411static int recover_mode(char *arg, struct termios *mode);
412static int screen_columns(void);
413static int set_mode(const struct mode_info *info,
414 int reversed, struct termios *mode);
415static speed_t string_to_baud(const char *arg);
416static tcflag_t* mode_type_flag(enum mode_type type, struct termios *mode);
417static void display_all(struct termios *mode, int fd);
418static void display_changed(struct termios *mode, int fd);
419static void display_recoverable(struct termios *mode, int fd);
420static void display_speed(struct termios *mode, int fancy);
421static void display_window_size(int fancy, int fd);
422static void sane_mode(struct termios *mode);
423static void set_control_char(const struct control_info *info,
424 const char *arg, struct termios *mode);
425static void set_speed(enum speed_setting type,
426 const char *arg, struct termios *mode);
427static void set_window_size(int rows, int cols, int fd);
428
429static const char *device_name;
430
431static __attribute__ ((noreturn)) void perror_on_device(const char *fmt)
432{
433 bb_perror_msg_and_die(fmt, device_name);
434}
435
436
437/* The width of the screen, for output wrapping. */
438static int max_col;
439
440/* Current position, to know when to wrap. */
441static int current_col;
442
443/* Print format string MESSAGE and optional args.
444 Wrap to next line first if it won't fit.
445 Print a space first unless MESSAGE will start a new line. */
446
447static void wrapf(const char *message, ...)
448{
449 va_list args;
450 char buf[1024]; /* Plenty long for our needs. */
451 int buflen;
452
453 va_start(args, message);
454 vsprintf(buf, message, args);
455 va_end(args);
456 buflen = strlen(buf);
457 if (current_col + (current_col > 0) + buflen >= max_col) {
458 putchar('\n');
459 current_col = 0;
460 }
461 if (current_col > 0) {
462 putchar(' ');
463 current_col++;
464 }
465 fputs(buf, stdout);
466 current_col += buflen;
467}
468
469static const struct suffix_mult stty_suffixes[] = {
470 {"b", 512 },
471 {"k", 1024},
472 {"B", 1024},
473 {NULL, 0 }
474};
475
476#ifndef TEST
477extern int stty_main(int argc, char **argv)
478#else
479extern int main(int argc, char **argv)
480#endif
481{
482 struct termios mode;
483 void (*output_func)(struct termios *, int);
484 int optc;
485 int require_set_attr;
486 int speed_was_set;
487 int verbose_output;
488 int recoverable_output;
489 int k;
490 int noargs = 1;
491 char * file_name = NULL;
492 int fd;
493
494
495 output_func = display_changed;
496 verbose_output = 0;
497 recoverable_output = 0;
498
499 /* Don't print error messages for unrecognized options. */
500 opterr = 0;
501
502 while ((optc = getopt(argc, argv, "agF:")) != -1) {
503 switch (optc) {
504 case 'a':
505 verbose_output = 1;
506 output_func = display_all;
507 break;
508
509 case 'g':
510 recoverable_output = 1;
511 output_func = display_recoverable;
512 break;
513
514 case 'F':
515 if (file_name)
516 bb_error_msg_and_die("only one device may be specified");
517 file_name = optarg;
518 break;
519
520 default: /* unrecognized option */
521 noargs = 0;
522 break;
523 }
524
525 if (noargs == 0)
526 break;
527 }
528
529 if (optind < argc)
530 noargs = 0;
531
532 /* Specifying both -a and -g gets an error. */
533 if (verbose_output & recoverable_output)
534 bb_error_msg_and_die ("verbose and stty-readable output styles are mutually exclusive");
535
536 /* Specifying any other arguments with -a or -g gets an error. */
537 if (~noargs & (verbose_output | recoverable_output))
538 bb_error_msg_and_die ("modes may not be set when specifying an output style");
539
540 /* FIXME: it'd be better not to open the file until we've verified
541 that all arguments are valid. Otherwise, we could end up doing
542 only some of the requested operations and then failing, probably
543 leaving things in an undesirable state. */
544
545 if (file_name) {
546 int fdflags;
547
548 device_name = file_name;
549 fd = bb_xopen(device_name, O_RDONLY | O_NONBLOCK);
550 if ((fdflags = fcntl(fd, F_GETFL)) == -1
551 || fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
552 perror_on_device("%s: couldn't reset non-blocking mode");
553 } else {
554 fd = 0;
555 device_name = bb_msg_standard_input;
556 }
557
558 /* Initialize to all zeroes so there is no risk memcmp will report a
559 spurious difference in an uninitialized portion of the structure. */
560 memset(&mode, 0, sizeof(mode));
561 if (tcgetattr(fd, &mode))
562 perror_on_device("%s");
563
564 if (verbose_output | recoverable_output | noargs) {
565 max_col = screen_columns();
566 current_col = 0;
567 output_func(&mode, fd);
568 return EXIT_SUCCESS;
569 }
570
571 speed_was_set = 0;
572 require_set_attr = 0;
573 k = 0;
574 while (++k < argc) {
575 int match_found = 0;
576 int reversed = 0;
577 int i;
578
579 if (argv[k][0] == '-') {
580 char *find_dev_opt;
581
582 ++argv[k];
583
584 /* Handle "-a", "-ag", "-aF/dev/foo", "-aF /dev/foo", etc.
585 Find the options that have been parsed. This is really
586 gross, but it's needed because stty SETTINGS look like options to
587 getopt(), so we need to work around things in a really horrible
588 way. If any new options are ever added to stty, the short option
589 MUST NOT be a letter which is the first letter of one of the
590 possible stty settings.
591 */
592 find_dev_opt = strchr(argv[k], 'F'); /* find -*F* */
593 if(find_dev_opt) {
594 if(find_dev_opt[1]==0) /* -*F /dev/foo */
595 k++; /* skip /dev/foo */
596 continue; /* else -*F/dev/foo - no skip */
597 }
598 if(argv[k][0]=='a' || argv[k][0]=='g')
599 continue;
600 /* Is not options - is reverse params */
601 reversed = 1;
602 }
603 for (i = 0; i < NUM_mode_info; ++i)
604 if (STREQ(argv[k], mode_info[i].name)) {
605 match_found = set_mode(&mode_info[i], reversed, &mode);
606 require_set_attr = 1;
607 break;
608 }
609
610 if (match_found == 0 && reversed)
611 bb_error_msg_and_die("invalid argument `%s'", --argv[k]);
612
613 if (match_found == 0)
614 for (i = 0; i < NUM_control_info; ++i)
615 if (STREQ(argv[k], control_info[i].name)) {
616 if (k == argc - 1)
617 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
618 match_found = 1;
619 ++k;
620 set_control_char(&control_info[i], argv[k], &mode);
621 require_set_attr = 1;
622 break;
623 }
624
625 if (match_found == 0) {
626 if (STREQ(argv[k], "ispeed")) {
627 if (k == argc - 1)
628 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
629 ++k;
630 set_speed(input_speed, argv[k], &mode);
631 speed_was_set = 1;
632 require_set_attr = 1;
633 } else if (STREQ(argv[k], "ospeed")) {
634 if (k == argc - 1)
635 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
636 ++k;
637 set_speed(output_speed, argv[k], &mode);
638 speed_was_set = 1;
639 require_set_attr = 1;
640 }
641#ifdef TIOCGWINSZ
642 else if (STREQ(argv[k], "rows")) {
643 if (k == argc - 1)
644 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
645 ++k;
646 set_window_size((int) bb_xparse_number(argv[k], stty_suffixes),
647 -1, fd);
648 } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) {
649 if (k == argc - 1)
650 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
651 ++k;
652 set_window_size(-1,
653 (int) bb_xparse_number(argv[k], stty_suffixes),
654 fd);
655 } else if (STREQ(argv[k], "size")) {
656 max_col = screen_columns();
657 current_col = 0;
658 display_window_size(0, fd);
659 }
660#endif
661#ifdef HAVE_C_LINE
662 else if (STREQ(argv[k], "line")) {
663 if (k == argc - 1)
664 bb_error_msg_and_die("missing argument to `%s'", argv[k]);
665 ++k;
666 mode.c_line = bb_xparse_number(argv[k], stty_suffixes);
667 require_set_attr = 1;
668 }
669#endif
670 else if (STREQ(argv[k], "speed")) {
671 max_col = screen_columns();
672 display_speed(&mode, 0);
673 } else if (recover_mode(argv[k], &mode) == 1)
674 require_set_attr = 1;
675 else if (string_to_baud(argv[k]) != (speed_t) - 1) {
676 set_speed(both_speeds, argv[k], &mode);
677 speed_was_set = 1;
678 require_set_attr = 1;
679 } else
680 bb_error_msg_and_die("invalid argument `%s'", argv[k]);
681 }
682 }
683
684 if (require_set_attr) {
685 struct termios new_mode;
686
687 if (tcsetattr(fd, TCSADRAIN, &mode))
688 perror_on_device("%s");
689
690 /* POSIX (according to Zlotnick's book) tcsetattr returns zero if
691 it performs *any* of the requested operations. This means it
692 can report `success' when it has actually failed to perform
693 some proper subset of the requested operations. To detect
694 this partial failure, get the current terminal attributes and
695 compare them to the requested ones. */
696
697 /* Initialize to all zeroes so there is no risk memcmp will report a
698 spurious difference in an uninitialized portion of the structure. */
699 memset(&new_mode, 0, sizeof(new_mode));
700 if (tcgetattr(fd, &new_mode))
701 perror_on_device("%s");
702
703 /* Normally, one shouldn't use memcmp to compare structures that
704 may have `holes' containing uninitialized data, but we have been
705 careful to initialize the storage of these two variables to all
706 zeroes. One might think it more efficient simply to compare the
707 modified fields, but that would require enumerating those fields --
708 and not all systems have the same fields in this structure. */
709
710 if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
711#ifdef CIBAUD
712 /* SunOS 4.1.3 (at least) has the problem that after this sequence,
713 tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2);
714 sometimes (m1 != m2). The only difference is in the four bits
715 of the c_cflag field corresponding to the baud rate. To save
716 Sun users a little confusion, don't report an error if this
717 happens. But suppress the error only if we haven't tried to
718 set the baud rate explicitly -- otherwise we'd never give an
719 error for a true failure to set the baud rate. */
720
721 new_mode.c_cflag &= (~CIBAUD);
722 if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
723#endif
724 perror_on_device ("%s: unable to perform all requested operations");
725 }
726 }
727
728 return EXIT_SUCCESS;
729}
730
731/* Return 0 if not applied because not reversible; otherwise return 1. */
732
733static int
734set_mode(const struct mode_info *info, int reversed, struct termios *mode)
735{
736 tcflag_t *bitsp;
737
738 if (reversed && (info->flags & REV) == 0)
739 return 0;
740
741 bitsp = mode_type_flag(info->type, mode);
742
743 if (bitsp == NULL) {
744 /* Combination mode. */
745 if (info->name == evenp || info->name == parity) {
746 if (reversed)
747 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
748 else
749 mode->c_cflag =
750 (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7;
751 } else if (info->name == stty_oddp) {
752 if (reversed)
753 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
754 else
755 mode->c_cflag =
756 (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB;
757 } else if (info->name == stty_nl) {
758 if (reversed) {
759 mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR;
760 mode->c_oflag = (mode->c_oflag
761#ifdef ONLCR
762 | ONLCR
763#endif
764 )
765#ifdef OCRNL
766 & ~OCRNL
767#endif
768#ifdef ONLRET
769 & ~ONLRET
770#endif
771 ;
772 } else {
773 mode->c_iflag = mode->c_iflag & ~ICRNL;
774#ifdef ONLCR
775 mode->c_oflag = mode->c_oflag & ~ONLCR;
776#endif
777 }
778 } else if (info->name == stty_ek) {
779 mode->c_cc[VERASE] = CERASE;
780 mode->c_cc[VKILL] = CKILL;
781 } else if (info->name == stty_sane)
782 sane_mode(mode);
783 else if (info->name == cbreak) {
784 if (reversed)
785 mode->c_lflag |= ICANON;
786 else
787 mode->c_lflag &= ~ICANON;
788 } else if (info->name == stty_pass8) {
789 if (reversed) {
790 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
791 mode->c_iflag |= ISTRIP;
792 } else {
793 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
794 mode->c_iflag &= ~ISTRIP;
795 }
796 } else if (info->name == litout) {
797 if (reversed) {
798 mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
799 mode->c_iflag |= ISTRIP;
800 mode->c_oflag |= OPOST;
801 } else {
802 mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
803 mode->c_iflag &= ~ISTRIP;
804 mode->c_oflag &= ~OPOST;
805 }
806 } else if (info->name == raw || info->name == cooked) {
807 if ((info->name[0] == 'r' && reversed)
808 || (info->name[0] == 'c' && !reversed)) {
809 /* Cooked mode. */
810 mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
811 mode->c_oflag |= OPOST;
812 mode->c_lflag |= ISIG | ICANON;
813#if VMIN == VEOF
814 mode->c_cc[VEOF] = CEOF;
815#endif
816#if VTIME == VEOL
817 mode->c_cc[VEOL] = CEOL;
818#endif
819 } else {
820 /* Raw mode. */
821 mode->c_iflag = 0;
822 mode->c_oflag &= ~OPOST;
823 mode->c_lflag &= ~(ISIG | ICANON
824#ifdef XCASE
825 | XCASE
826#endif
827 );
828 mode->c_cc[VMIN] = 1;
829 mode->c_cc[VTIME] = 0;
830 }
831 }
832#ifdef IXANY
833 else if (info->name == decctlq) {
834 if (reversed)
835 mode->c_iflag |= IXANY;
836 else
837 mode->c_iflag &= ~IXANY;
838 }
839#endif
840#ifdef TABDLY
841 else if (info->name == stty_tabs) {
842 if (reversed)
843 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3;
844 else
845 mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0;
846 }
847#else
848# ifdef OXTABS
849 else if (info->name == stty_tabs) {
850 if (reversed)
851 mode->c_oflag = mode->c_oflag | OXTABS;
852 else
853 mode->c_oflag = mode->c_oflag & ~OXTABS;
854 }
855# endif
856#endif
857#if defined(XCASE) && defined(IUCLC) && defined(OLCUC)
858 else if (info->name == stty_lcase || info->name == stty_LCASE) {
859 if (reversed) {
860 mode->c_lflag &= ~XCASE;
861 mode->c_iflag &= ~IUCLC;
862 mode->c_oflag &= ~OLCUC;
863 } else {
864 mode->c_lflag |= XCASE;
865 mode->c_iflag |= IUCLC;
866 mode->c_oflag |= OLCUC;
867 }
868 }
869#endif
870 else if (info->name == stty_crt)
871 mode->c_lflag |= ECHOE
872#ifdef ECHOCTL
873 | ECHOCTL
874#endif
875#ifdef ECHOKE
876 | ECHOKE
877#endif
878 ;
879 else if (info->name == stty_dec) {
880 mode->c_cc[VINTR] = 3; /* ^C */
881 mode->c_cc[VERASE] = 127; /* DEL */
882 mode->c_cc[VKILL] = 21; /* ^U */
883 mode->c_lflag |= ECHOE
884#ifdef ECHOCTL
885 | ECHOCTL
886#endif
887#ifdef ECHOKE
888 | ECHOKE
889#endif
890 ;
891#ifdef IXANY
892 mode->c_iflag &= ~IXANY;
893#endif
894 }
895 } else if (reversed)
896 *bitsp = *bitsp & ~((unsigned long)info->mask) & ~info->bits;
897 else
898 *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits;
899
900 return 1;
901}
902
903static void
904set_control_char(const struct control_info *info, const char *arg,
905 struct termios *mode)
906{
907 unsigned char value;
908
909 if (info->name == stty_min || info->name == stty_time)
910 value = bb_xparse_number(arg, stty_suffixes);
911 else if (arg[0] == '\0' || arg[1] == '\0')
912 value = arg[0];
913 else if (STREQ(arg, "^-") || STREQ(arg, "undef"))
914 value = _POSIX_VDISABLE;
915 else if (arg[0] == '^' && arg[1] != '\0') { /* Ignore any trailing junk. */
916 if (arg[1] == '?')
917 value = 127;
918 else
919 value = arg[1] & ~0140; /* Non-letters get weird results. */
920 } else
921 value = bb_xparse_number(arg, stty_suffixes);
922 mode->c_cc[info->offset] = value;
923}
924
925static void
926set_speed(enum speed_setting type, const char *arg, struct termios *mode)
927{
928 speed_t baud;
929
930 baud = string_to_baud(arg);
931
932 if (type != output_speed) { /* either input or both */
933 cfsetispeed(mode, baud);
934 }
935 if (type != input_speed) { /* either output or both */
936 cfsetospeed(mode, baud);
937 }
938}
939
940#ifdef TIOCGWINSZ
941
942static int get_win_size(int fd, struct winsize *win)
943{
944 int err = ioctl(fd, TIOCGWINSZ, (char *) win);
945
946 return err;
947}
948
949static void
950set_window_size(int rows, int cols, int fd)
951{
952 struct winsize win;
953
954 if (get_win_size(fd, &win)) {
955 if (errno != EINVAL)
956 perror_on_device("%s");
957 memset(&win, 0, sizeof(win));
958 }
959
960 if (rows >= 0)
961 win.ws_row = rows;
962 if (cols >= 0)
963 win.ws_col = cols;
964
965# ifdef TIOCSSIZE
966 /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote:
967 The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel.
968 This comment from sys/ttold.h describes Sun's twisted logic - a better
969 test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0).
970 At any rate, the problem is gone in Solaris 2.x. */
971
972 if (win.ws_row == 0 || win.ws_col == 0) {
973 struct ttysize ttysz;
974
975 ttysz.ts_lines = win.ws_row;
976 ttysz.ts_cols = win.ws_col;
977
978 win.ws_row = win.ws_col = 1;
979
980 if ((ioctl(fd, TIOCSWINSZ, (char *) &win) != 0)
981 || (ioctl(fd, TIOCSSIZE, (char *) &ttysz) != 0)) {
982 perror_on_device("%s");
983 }
984 return;
985 }
986# endif
987
988 if (ioctl(fd, TIOCSWINSZ, (char *) &win))
989 perror_on_device("%s");
990}
991
992static void display_window_size(int fancy, int fd)
993{
994 const char *fmt_str = "%s" "\0" "%s: no size information for this device";
995 struct winsize win;
996
997 if (get_win_size(fd, &win)) {
998 if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
999 perror_on_device(fmt_str);
1000 }
1001 } else {
1002 wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n",
1003 win.ws_row, win.ws_col);
1004 if (!fancy)
1005 current_col = 0;
1006 }
1007}
1008#endif
1009
1010static int screen_columns(void)
1011{
1012 int columns;
1013 const char *s;
1014
1015#ifdef TIOCGWINSZ
1016 struct winsize win;
1017
1018 /* With Solaris 2.[123], this ioctl fails and errno is set to
1019 EINVAL for telnet (but not rlogin) sessions.
1020 On ISC 3.0, it fails for the console and the serial port
1021 (but it works for ptys).
1022 It can also fail on any system when stdout isn't a tty.
1023 In case of any failure, just use the default. */
1024 if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0)
1025 return win.ws_col;
1026#endif
1027
1028 columns = 80;
1029 if ((s = getenv("COLUMNS"))) {
1030 columns = atoi(s);
1031 }
1032 return columns;
1033}
1034
1035static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode)
1036{
1037 static const unsigned char tcflag_offsets[] = {
1038 offsetof(struct termios, c_cflag), /* control */
1039 offsetof(struct termios, c_iflag), /* input */
1040 offsetof(struct termios, c_oflag), /* output */
1041 offsetof(struct termios, c_lflag) /* local */
1042 };
1043
1044 if (((unsigned int) type) <= local) {
1045 return (tcflag_t *)(((char *) mode) + tcflag_offsets[(int)type]);
1046 }
1047 return NULL;
1048}
1049
1050static void display_changed(struct termios *mode, int fd)
1051{
1052 int i;
1053 int empty_line;
1054 tcflag_t *bitsp;
1055 unsigned long mask;
1056 enum mode_type prev_type = control;
1057
1058 display_speed(mode, 1);
1059#ifdef HAVE_C_LINE
1060 wrapf("line = %d;", mode->c_line);
1061#endif
1062 putchar('\n');
1063 current_col = 0;
1064
1065 empty_line = 1;
1066 for (i = 0; control_info[i].name != stty_min; ++i) {
1067 if (mode->c_cc[control_info[i].offset] == control_info[i].saneval)
1068 continue;
1069 /* If swtch is the same as susp, don't print both. */
1070#if VSWTCH == VSUSP
1071 if (control_info[i].name == stty_swtch)
1072 continue;
1073#endif
1074 /* If eof uses the same slot as min, only print whichever applies. */
1075#if VEOF == VMIN
1076 if ((mode->c_lflag & ICANON) == 0
1077 && (control_info[i].name == stty_eof
1078 || control_info[i].name == stty_eol)) continue;
1079#endif
1080
1081 empty_line = 0;
1082 wrapf("%s = %s;", control_info[i].name,
1083 visible(mode->c_cc[control_info[i].offset]));
1084 }
1085 if ((mode->c_lflag & ICANON) == 0) {
1086 wrapf("min = %d; time = %d;\n", (int) mode->c_cc[VMIN],
1087 (int) mode->c_cc[VTIME]);
1088 } else if (empty_line == 0)
1089 putchar('\n');
1090 current_col = 0;
1091
1092 empty_line = 1;
1093 for (i = 0; i < NUM_mode_info; ++i) {
1094 if (mode_info[i].flags & OMIT)
1095 continue;
1096 if (mode_info[i].type != prev_type) {
1097 if (empty_line == 0) {
1098 putchar('\n');
1099 current_col = 0;
1100 empty_line = 1;
1101 }
1102 prev_type = mode_info[i].type;
1103 }
1104
1105 bitsp = mode_type_flag(mode_info[i].type, mode);
1106 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1107 if ((*bitsp & mask) == mode_info[i].bits) {
1108 if (mode_info[i].flags & SANE_UNSET) {
1109 wrapf("%s", mode_info[i].name);
1110 empty_line = 0;
1111 }
1112 }
1113 else if ((mode_info[i].flags & (SANE_SET | REV)) ==
1114 (SANE_SET | REV)) {
1115 wrapf("-%s", mode_info[i].name);
1116 empty_line = 0;
1117 }
1118 }
1119 if (empty_line == 0)
1120 putchar('\n');
1121 current_col = 0;
1122}
1123
1124static void
1125display_all(struct termios *mode, int fd)
1126{
1127 int i;
1128 tcflag_t *bitsp;
1129 unsigned long mask;
1130 enum mode_type prev_type = control;
1131
1132 display_speed(mode, 1);
1133#ifdef TIOCGWINSZ
1134 display_window_size(1, fd);
1135#endif
1136#ifdef HAVE_C_LINE
1137 wrapf("line = %d;", mode->c_line);
1138#endif
1139 putchar('\n');
1140 current_col = 0;
1141
1142 for (i = 0; control_info[i].name != stty_min; ++i) {
1143 /* If swtch is the same as susp, don't print both. */
1144#if VSWTCH == VSUSP
1145 if (control_info[i].name == stty_swtch)
1146 continue;
1147#endif
1148 /* If eof uses the same slot as min, only print whichever applies. */
1149#if VEOF == VMIN
1150 if ((mode->c_lflag & ICANON) == 0
1151 && (control_info[i].name == stty_eof
1152 || control_info[i].name == stty_eol)) continue;
1153#endif
1154 wrapf("%s = %s;", control_info[i].name,
1155 visible(mode->c_cc[control_info[i].offset]));
1156 }
1157#if VEOF == VMIN
1158 if ((mode->c_lflag & ICANON) == 0)
1159#endif
1160 wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
1161 if (current_col != 0)
1162 putchar('\n');
1163 current_col = 0;
1164
1165 for (i = 0; i < NUM_mode_info; ++i) {
1166 if (mode_info[i].flags & OMIT)
1167 continue;
1168 if (mode_info[i].type != prev_type) {
1169 putchar('\n');
1170 current_col = 0;
1171 prev_type = mode_info[i].type;
1172 }
1173
1174 bitsp = mode_type_flag(mode_info[i].type, mode);
1175 mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits;
1176 if ((*bitsp & mask) == mode_info[i].bits)
1177 wrapf("%s", mode_info[i].name);
1178 else if (mode_info[i].flags & REV)
1179 wrapf("-%s", mode_info[i].name);
1180 }
1181 putchar('\n');
1182 current_col = 0;
1183}
1184
1185static void display_speed(struct termios *mode, int fancy)
1186{
1187 unsigned long ispeed, ospeed;
1188 const char *fmt_str =
1189 "%lu %lu\n\0" "ispeed %lu baud; ospeed %lu baud;\0"
1190 "%lu\n\0" "\0\0\0\0" "speed %lu baud;";
1191
1192 ospeed = ispeed = cfgetispeed(mode);
1193 if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) {
1194 ispeed = ospeed; /* in case ispeed was 0 */
1195 fmt_str += 43;
1196 }
1197 if (fancy) {
1198 fmt_str += 9;
1199 }
1200 wrapf(fmt_str, bb_baud_to_value(ispeed), bb_baud_to_value(ospeed));
1201 if (!fancy)
1202 current_col = 0;
1203}
1204
1205static void display_recoverable(struct termios *mode, int fd)
1206{
1207 int i;
1208
1209 printf("%lx:%lx:%lx:%lx",
1210 (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
1211 (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
1212 for (i = 0; i < NCCS; ++i)
1213 printf(":%x", (unsigned int) mode->c_cc[i]);
1214 putchar('\n');
1215}
1216
1217static int recover_mode(char *arg, struct termios *mode)
1218{
1219 int i, n;
1220 unsigned int chr;
1221 unsigned long iflag, oflag, cflag, lflag;
1222
1223 /* Scan into temporaries since it is too much trouble to figure out
1224 the right format for `tcflag_t'. */
1225 if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
1226 &iflag, &oflag, &cflag, &lflag, &n) != 4)
1227 return 0;
1228 mode->c_iflag = iflag;
1229 mode->c_oflag = oflag;
1230 mode->c_cflag = cflag;
1231 mode->c_lflag = lflag;
1232 arg += n;
1233 for (i = 0; i < NCCS; ++i) {
1234 if (sscanf(arg, ":%x%n", &chr, &n) != 1)
1235 return 0;
1236 mode->c_cc[i] = chr;
1237 arg += n;
1238 }
1239
1240 /* Fail if there are too many fields. */
1241 if (*arg != '\0')
1242 return 0;
1243
1244 return 1;
1245}
1246
1247static speed_t string_to_baud(const char *arg)
1248{
1249 return bb_value_to_baud(bb_xparse_number(arg, 0));
1250}
1251
1252static void sane_mode(struct termios *mode)
1253{
1254 int i;
1255 tcflag_t *bitsp;
1256
1257 for (i = 0; i < NUM_control_info; ++i) {
1258#if VMIN == VEOF
1259 if (control_info[i].name == stty_min)
1260 break;
1261#endif
1262 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1263 }
1264
1265 for (i = 0; i < NUM_mode_info; ++i) {
1266 if (mode_info[i].flags & SANE_SET) {
1267 bitsp = mode_type_flag(mode_info[i].type, mode);
1268 *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask))
1269 | mode_info[i].bits;
1270 } else if (mode_info[i].flags & SANE_UNSET) {
1271 bitsp = mode_type_flag(mode_info[i].type, mode);
1272 *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask)
1273 & ~mode_info[i].bits;
1274 }
1275 }
1276}
1277
1278/* Return a string that is the printable representation of character CH. */
1279/* Adapted from `cat' by Torbjorn Granlund. */
1280
1281static const char *visible(unsigned int ch)
1282{
1283 static char buf[10];
1284 char *bpout = buf;
1285
1286 if (ch == _POSIX_VDISABLE) {
1287 return "<undef>";
1288 }
1289
1290 if (ch >= 128) {
1291 ch -= 128;
1292 *bpout++ = 'M';
1293 *bpout++ = '-';
1294 }
1295
1296 if (ch < 32) {
1297 *bpout++ = '^';
1298 *bpout++ = ch + 64;
1299 } else if (ch < 127) {
1300 *bpout++ = ch;
1301 } else {
1302 *bpout++ = '^';
1303 *bpout++ = '?';
1304 }
1305
1306 *bpout = '\0';
1307 return (const char *) buf;
1308}
1309
1310#ifdef TEST
1311
1312const char *bb_applet_name = "stty";
1313
1314#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini sync implementation for busybox
4 *
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
24
25#include <stdlib.h>
26#include <unistd.h>
27#include "busybox.h"
28
29extern int sync_main(int argc, char **argv)
30{
31 bb_warn_ignoring_args(argc - 1);
32
33 sync();
34
35 return(EXIT_SUCCESS);
36}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini tail implementation for busybox
4 *
5 * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant (need fancy for -c) */
24/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
25/* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */
26
27/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
28 *
29 * Pretty much rewritten to fix numerous bugs and reduce realloc() calls.
30 * Bugs fixed (although I may have forgotten one or two... it was pretty bad)
31 * 1) mixing printf/write without fflush()ing stdout
32 * 2) no check that any open files are present
33 * 3) optstring had -q taking an arg
34 * 4) no error checking on write in some cases, and a warning even then
35 * 5) q and s interaction bug
36 * 6) no check for lseek error
37 * 7) lseek attempted when count==0 even if arg was +0 (from top)
38 */
39
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <ctype.h>
44#include <unistd.h>
45#include <fcntl.h>
46#include <sys/stat.h>
47#include "busybox.h"
48
49static const struct suffix_mult tail_suffixes[] = {
50 { "b", 512 },
51 { "k", 1024 },
52 { "m", 1048576 },
53 { NULL, 0 }
54};
55
56static int status
57#if EXIT_SUCCESS != 0
58 = EXIT_SUCCESS /* If it is 0 (paranoid check), let bss initialize it. */
59#endif
60 ;
61
62static void tail_xprint_header(const char *fmt, const char *filename)
63{
64 /* If we get an output error, there is really no sense in continuing. */
65 if (dprintf(STDOUT_FILENO, fmt, filename) < 0) {
66 bb_perror_nomsg_and_die();
67 }
68}
69
70/* len should probably be size_t */
71static void tail_xbb_full_write(const char *buf, size_t len)
72{
73 /* If we get a write error, there is really no sense in continuing. */
74 if (bb_full_write(STDOUT_FILENO, buf, len) < 0) {
75 bb_perror_nomsg_and_die();
76 }
77}
78
79static ssize_t tail_read(int fd, char *buf, size_t count)
80{
81 ssize_t r;
82
83 if ((r = safe_read(fd, buf, count)) < 0) {
84 bb_perror_msg("read");
85 status = EXIT_FAILURE;
86 }
87
88 return r;
89}
90
91static const char tail_opts[] =
92 "fn:c:"
93#ifdef CONFIG_FEATURE_FANCY_TAIL
94 "qs:v"
95#endif
96 ;
97
98static const char header_fmt[] = "\n==> %s <==\n";
99
100int tail_main(int argc, char **argv)
101{
102 long count = 10;
103 unsigned int sleep_period = 1;
104 int from_top = 0;
105 int follow = 0;
106 int header_threshhold = 1;
107 int count_bytes = 0;
108
109 char *tailbuf;
110 size_t tailbufsize;
111 int taillen = 0;
112 int newline = 0;
113
114 int *fds, nfiles, nread, nwrite, seen, i, opt;
115 char *s, *buf;
116 const char *fmt;
117
118 /* Allow legacy syntax of an initial numeric option without -n. */
119 if (argc >=2 && ((argv[1][0] == '+') || ((argv[1][0] == '-')
120 /* && (isdigit)(argv[1][1]) */
121 && (((unsigned int)(argv[1][1] - '0')) <= 9))))
122 {
123 optind = 2;
124 optarg = argv[1];
125 goto GET_COUNT;
126 }
127
128 while ((opt = getopt(argc, argv, tail_opts)) > 0) {
129 switch (opt) {
130 case 'f':
131 follow = 1;
132 break;
133 case 'c':
134 count_bytes = 1;
135 /* FALLS THROUGH */
136 case 'n':
137 GET_COUNT:
138 count = bb_xgetlarg10_sfx(optarg, tail_suffixes);
139 /* Note: Leading whitespace is an error trapped above. */
140 if (*optarg == '+') {
141 from_top = 1;
142 } else {
143 from_top = 0;
144 }
145 if (count < 0) {
146 count = -count;
147 }
148 break;
149#ifdef CONFIG_FEATURE_FANCY_TAIL
150 case 'q':
151 header_threshhold = INT_MAX;
152 break;
153 case 's':
154 sleep_period =bb_xgetularg10_bnd(optarg, 0, UINT_MAX);
155 break;
156 case 'v':
157 header_threshhold = 0;
158 break;
159#endif
160 default:
161 bb_show_usage();
162 }
163 }
164
165 /* open all the files */
166 fds = (int *)xmalloc(sizeof(int) * (argc - optind + 1));
167
168 argv += optind;
169 nfiles = i = 0;
170
171 if ((argc -= optind) == 0) {
172 struct stat statbuf;
173
174 if (!fstat(STDIN_FILENO, &statbuf) && S_ISFIFO(statbuf.st_mode)) {
175 follow = 0;
176 }
177 /* --argv; */
178 *argv = (char *) bb_msg_standard_input;
179 goto DO_STDIN;
180 }
181
182 do {
183 if ((argv[i][0] == '-') && !argv[i][1]) {
184 DO_STDIN:
185 fds[nfiles] = STDIN_FILENO;
186 } else if ((fds[nfiles] = open(argv[i], O_RDONLY)) < 0) {
187 bb_perror_msg("%s", argv[i]);
188 status = EXIT_FAILURE;
189 continue;
190 }
191 argv[nfiles] = argv[i];
192 ++nfiles;
193 } while (++i < argc);
194
195 if (!nfiles) {
196 bb_error_msg_and_die("no files");
197 }
198
199 tailbufsize = BUFSIZ;
200
201 /* tail the files */
202 if (from_top < count_bytes) { /* Each is 0 or 1, so true iff 0 < 1. */
203 /* Hence, !from_top && count_bytes */
204 if (tailbufsize < count) {
205 tailbufsize = count + BUFSIZ;
206 }
207 }
208
209 buf = tailbuf = xmalloc(tailbufsize);
210
211 fmt = header_fmt + 1; /* Skip header leading newline on first output. */
212 i = 0;
213 do {
214 /* Be careful. It would be possible to optimize the count-bytes
215 * case if the file is seekable. If you do though, remember that
216 * starting file position may not be the beginning of the file.
217 * Beware of backing up too far. See example in wc.c.
218 */
219 if ((!(count|from_top)) && (lseek(fds[i], 0, SEEK_END) >= 0)) {
220 continue;
221 }
222
223 if (nfiles > header_threshhold) {
224 tail_xprint_header(fmt, argv[i]);
225 fmt = header_fmt;
226 }
227
228 buf = tailbuf;
229 taillen = 0;
230 seen = 1;
231 newline = 0;
232
233 while ((nread = tail_read(fds[i], buf, tailbufsize-taillen)) > 0) {
234 if (from_top) {
235 nwrite = nread;
236 if (seen < count) {
237 if (count_bytes) {
238 nwrite -= (count - seen);
239 seen = count;
240 } else {
241 s = buf;
242 do {
243 --nwrite;
244 if ((*s++ == '\n') && (++seen == count)) {
245 break;
246 }
247 } while (nwrite);
248 }
249 }
250 tail_xbb_full_write(buf + nread - nwrite, nwrite);
251 } else if (count) {
252 if (count_bytes) {
253 taillen += nread;
254 if (taillen > count) {
255 memmove(tailbuf, tailbuf + taillen - count, count);
256 taillen = count;
257 }
258 } else {
259 int k = nread;
260 int nbuf = 0;
261
262 while (k) {
263 --k;
264 if (buf[k] == '\n') {
265 ++nbuf;
266 }
267 }
268
269 if (newline + nbuf < count) {
270 newline += nbuf;
271 taillen += nread;
272
273 } else {
274 int extra = 0;
275 if (buf[nread-1] != '\n') {
276 extra = 1;
277 }
278
279 k = newline + nbuf + extra - count;
280 s = tailbuf;
281 while (k) {
282 if (*s == '\n') {
283 --k;
284 }
285 ++s;
286 }
287
288 taillen += nread - (s - tailbuf);
289 memmove(tailbuf, s, taillen);
290 newline = count - extra;
291 }
292 if (tailbufsize < taillen + BUFSIZ) {
293 tailbufsize = taillen + BUFSIZ;
294 tailbuf = xrealloc(tailbuf, tailbufsize);
295 }
296 }
297 buf = tailbuf + taillen;
298 }
299 }
300
301 if (!from_top) {
302 tail_xbb_full_write(tailbuf, taillen);
303 }
304
305 taillen = 0;
306 } while (++i < nfiles);
307
308 buf = xrealloc(tailbuf, BUFSIZ);
309
310 fmt = NULL;
311
312 while (follow) {
313 sleep(sleep_period);
314 i = 0;
315 do {
316 if (nfiles > header_threshhold) {
317 fmt = header_fmt;
318 }
319 while ((nread = tail_read(fds[i], buf, sizeof(buf))) > 0) {
320 if (fmt) {
321 tail_xprint_header(fmt, argv[i]);
322 fmt = NULL;
323 }
324 tail_xbb_full_write(buf, nread);
325 }
326 } while (++i < nfiles);
327 }
328
329 return status;
330}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * tee implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/tee.html */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <signal.h>
29#include <unistd.h>
30#include "busybox.h"
31
32int tee_main(int argc, char **argv)
33{
34 const char *mode = "w\0a";
35 FILE **files;
36 FILE **p;
37 char **filenames;
38 int flags;
39 int retval = EXIT_SUCCESS;
40#ifdef CONFIG_FEATURE_TEE_USE_BLOCK_IO
41 ssize_t c;
42 RESERVE_CONFIG_BUFFER(buf, BUFSIZ);
43#else
44 int c;
45#endif
46
47 flags = bb_getopt_ulflags(argc, argv, "ia"); /* 'a' must be 2nd */
48
49 mode += (flags & 2); /* Since 'a' is the 2nd option... */
50
51 if (flags & 1) {
52 signal(SIGINT, SIG_IGN); /* TODO - switch to sigaction.*/
53 }
54
55 /* gnu tee ignores SIGPIPE in case one of the output files is a pipe
56 * that doesn't consume all its input. Good idea... */
57 signal(SIGPIPE, SIG_IGN); /* TODO - switch to sigaction.*/
58
59 /* Allocate an array of FILE *'s, with one extra for a sentinal. */
60 p = files = (FILE **)xmalloc(sizeof(FILE *) * (argc - optind + 2));
61 *p = stdout;
62 argv += optind - 1;
63 filenames = argv - 1;
64 *filenames = (char *) bb_msg_standard_input; /* for later */
65 goto GOT_NEW_FILE;
66
67 do {
68 if ((*p = bb_wfopen(*argv, mode)) == NULL) {
69 retval = EXIT_FAILURE;
70 continue;
71 }
72 filenames[(int)(p - files)] = *argv;
73 GOT_NEW_FILE:
74 setbuf(*p, NULL); /* tee must not buffer output. */
75 ++p;
76 } while (*++argv);
77
78 *p = NULL; /* Store the sentinal value. */
79
80#ifdef CONFIG_FEATURE_TEE_USE_BLOCK_IO
81 while ((c = safe_read(STDIN_FILENO, buf, BUFSIZ)) > 0) {
82 for (p=files ; *p ; p++) {
83 fwrite(buf, 1, c, *p);
84 }
85 }
86
87 if (c < 0) { /* Make sure read errors are signaled. */
88 retval = EXIT_FAILURE;
89 }
90
91#ifdef CONFIG_FEATURE_CLEAN_UP
92 RELEASE_CONFIG_BUFFER(buf);
93#endif
94
95#else
96 setvbuf(stdout, NULL, _IONBF, 0);
97 while ((c = getchar()) != EOF) {
98 for (p=files ; *p ; p++) {
99 putc(c, *p);
100 }
101 }
102#endif
103
104 /* Now we need to check for i/o errors on stdin and the various
105 * output files. Since we know that the first entry in the output
106 * file table is stdout, we can save one "if ferror" test by
107 * setting the first entry to stdin and checking stdout error
108 * status with bb_fflush_stdout_and_exit()... although fflush()ing
109 * is unnecessary here. */
110
111 p = files;
112 *p = stdin;
113 do { /* Now check for (input and) output errors. */
114 /* Checking ferror should be sufficient, but we may want to fclose.
115 * If we do, remember not to close stdin! */
116 bb_xferror(*p, filenames[(int)(p - files)]);
117 } while (*++p);
118
119 bb_fflush_stdout_and_exit(retval);
120}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * test implementation for busybox
4 *
5 * Copyright (c) by a whole pile of folks:
6 *
7 * test(1); version 7-like -- author Erik Baalbergen
8 * modified by Eric Gisin to be used as built-in.
9 * modified by Arnold Robbins to add SVR3 compatibility
10 * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
11 * modified by J.T. Conklin for NetBSD.
12 * modified by Herbert Xu to be used as built-in in ash.
13 * modified by Erik Andersen <andersen@codepoet.org> to be used
14 * in busybox.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 *
30 * Original copyright notice states:
31 * "This program is in the Public Domain."
32 */
33
34#include <sys/types.h>
35#include <unistd.h>
36#include <ctype.h>
37#include <errno.h>
38#include <stdlib.h>
39#include <string.h>
40#include "busybox.h"
41
42/* test(1) accepts the following grammar:
43 oexpr ::= aexpr | aexpr "-o" oexpr ;
44 aexpr ::= nexpr | nexpr "-a" aexpr ;
45 nexpr ::= primary | "!" primary
46 primary ::= unary-operator operand
47 | operand binary-operator operand
48 | operand
49 | "(" oexpr ")"
50 ;
51 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
52 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
53
54 binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
55 "-nt"|"-ot"|"-ef";
56 operand ::= <any legal UNIX file name>
57*/
58
59enum token {
60 EOI,
61 FILRD,
62 FILWR,
63 FILEX,
64 FILEXIST,
65 FILREG,
66 FILDIR,
67 FILCDEV,
68 FILBDEV,
69 FILFIFO,
70 FILSOCK,
71 FILSYM,
72 FILGZ,
73 FILTT,
74 FILSUID,
75 FILSGID,
76 FILSTCK,
77 FILNT,
78 FILOT,
79 FILEQ,
80 FILUID,
81 FILGID,
82 STREZ,
83 STRNZ,
84 STREQ,
85 STRNE,
86 STRLT,
87 STRGT,
88 INTEQ,
89 INTNE,
90 INTGE,
91 INTGT,
92 INTLE,
93 INTLT,
94 UNOT,
95 BAND,
96 BOR,
97 LPAREN,
98 RPAREN,
99 OPERAND
100};
101
102enum token_types {
103 UNOP,
104 BINOP,
105 BUNOP,
106 BBINOP,
107 PAREN
108};
109
110static const struct t_op {
111 const char *op_text;
112 short op_num, op_type;
113} ops[] = {
114 {
115 "-r", FILRD, UNOP}, {
116 "-w", FILWR, UNOP}, {
117 "-x", FILEX, UNOP}, {
118 "-e", FILEXIST, UNOP}, {
119 "-f", FILREG, UNOP}, {
120 "-d", FILDIR, UNOP}, {
121 "-c", FILCDEV, UNOP}, {
122 "-b", FILBDEV, UNOP}, {
123 "-p", FILFIFO, UNOP}, {
124 "-u", FILSUID, UNOP}, {
125 "-g", FILSGID, UNOP}, {
126 "-k", FILSTCK, UNOP}, {
127 "-s", FILGZ, UNOP}, {
128 "-t", FILTT, UNOP}, {
129 "-z", STREZ, UNOP}, {
130 "-n", STRNZ, UNOP}, {
131 "-h", FILSYM, UNOP}, /* for backwards compat */
132 {
133 "-O", FILUID, UNOP}, {
134 "-G", FILGID, UNOP}, {
135 "-L", FILSYM, UNOP}, {
136 "-S", FILSOCK, UNOP}, {
137 "=", STREQ, BINOP}, {
138 "!=", STRNE, BINOP}, {
139 "<", STRLT, BINOP}, {
140 ">", STRGT, BINOP}, {
141 "-eq", INTEQ, BINOP}, {
142 "-ne", INTNE, BINOP}, {
143 "-ge", INTGE, BINOP}, {
144 "-gt", INTGT, BINOP}, {
145 "-le", INTLE, BINOP}, {
146 "-lt", INTLT, BINOP}, {
147 "-nt", FILNT, BINOP}, {
148 "-ot", FILOT, BINOP}, {
149 "-ef", FILEQ, BINOP}, {
150 "!", UNOT, BUNOP}, {
151 "-a", BAND, BBINOP}, {
152 "-o", BOR, BBINOP}, {
153 "(", LPAREN, PAREN}, {
154 ")", RPAREN, PAREN}, {
155 0, 0, 0}
156};
157
158#ifdef CONFIG_FEATURE_TEST_64
159typedef int64_t arith_t;
160#else
161typedef int arith_t;
162#endif
163
164static char **t_wp;
165static struct t_op const *t_wp_op;
166static gid_t *group_array = NULL;
167static int ngroups;
168
169static enum token t_lex(char *s);
170static arith_t oexpr(enum token n);
171static arith_t aexpr(enum token n);
172static arith_t nexpr(enum token n);
173static int binop(void);
174static arith_t primary(enum token n);
175static int filstat(char *nm, enum token mode);
176static arith_t getn(const char *s);
177static int newerf(const char *f1, const char *f2);
178static int olderf(const char *f1, const char *f2);
179static int equalf(const char *f1, const char *f2);
180static void syntax(const char *op, const char *msg);
181static int test_eaccess(char *path, int mode);
182static int is_a_group_member(gid_t gid);
183static void initialize_group_array(void);
184
185extern int test_main(int argc, char **argv)
186{
187 int res;
188
189 if (strcmp(bb_applet_name, "[") == 0) {
190 if (strcmp(argv[--argc], "]"))
191 bb_error_msg_and_die("missing ]");
192 argv[argc] = NULL;
193 }
194 /* Implement special cases from POSIX.2, section 4.62.4 */
195 switch (argc) {
196 case 1:
197 exit(1);
198 case 2:
199 exit(*argv[1] == '\0');
200 case 3:
201 if (argv[1][0] == '!' && argv[1][1] == '\0') {
202 exit(!(*argv[2] == '\0'));
203 }
204 break;
205 case 4:
206 if (argv[1][0] != '!' || argv[1][1] != '\0') {
207 if (t_lex(argv[2]), t_wp_op && t_wp_op->op_type == BINOP) {
208 t_wp = &argv[1];
209 exit(binop() == 0);
210 }
211 }
212 break;
213 case 5:
214 if (argv[1][0] == '!' && argv[1][1] == '\0') {
215 if (t_lex(argv[3]), t_wp_op && t_wp_op->op_type == BINOP) {
216 t_wp = &argv[2];
217 exit(!(binop() == 0));
218 }
219 }
220 break;
221 }
222
223 t_wp = &argv[1];
224 res = !oexpr(t_lex(*t_wp));
225
226 if (*t_wp != NULL && *++t_wp != NULL)
227 syntax(*t_wp, "unknown operand");
228
229 return (res);
230}
231
232static void syntax(const char *op, const char *msg)
233{
234 if (op && *op) {
235 bb_error_msg_and_die("%s: %s", op, msg);
236 } else {
237 bb_error_msg_and_die("%s", msg);
238 }
239}
240
241static arith_t oexpr(enum token n)
242{
243 arith_t res;
244
245 res = aexpr(n);
246 if (t_lex(*++t_wp) == BOR) {
247 return oexpr(t_lex(*++t_wp)) || res;
248 }
249 t_wp--;
250 return res;
251}
252
253static arith_t aexpr(enum token n)
254{
255 arith_t res;
256
257 res = nexpr(n);
258 if (t_lex(*++t_wp) == BAND)
259 return aexpr(t_lex(*++t_wp)) && res;
260 t_wp--;
261 return res;
262}
263
264static arith_t nexpr(enum token n)
265{
266 if (n == UNOT)
267 return !nexpr(t_lex(*++t_wp));
268 return primary(n);
269}
270
271static arith_t primary(enum token n)
272{
273 arith_t res;
274
275 if (n == EOI) {
276 syntax(NULL, "argument expected");
277 }
278 if (n == LPAREN) {
279 res = oexpr(t_lex(*++t_wp));
280 if (t_lex(*++t_wp) != RPAREN)
281 syntax(NULL, "closing paren expected");
282 return res;
283 }
284 if (t_wp_op && t_wp_op->op_type == UNOP) {
285 /* unary expression */
286 if (*++t_wp == NULL)
287 syntax(t_wp_op->op_text, "argument expected");
288 switch (n) {
289 case STREZ:
290 return strlen(*t_wp) == 0;
291 case STRNZ:
292 return strlen(*t_wp) != 0;
293 case FILTT:
294 return isatty(getn(*t_wp));
295 default:
296 return filstat(*t_wp, n);
297 }
298 }
299
300 if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
301 return binop();
302 }
303
304 return strlen(*t_wp) > 0;
305}
306
307static int binop()
308{
309 const char *opnd1, *opnd2;
310 struct t_op const *op;
311
312 opnd1 = *t_wp;
313 (void) t_lex(*++t_wp);
314 op = t_wp_op;
315
316 if ((opnd2 = *++t_wp) == (char *) 0)
317 syntax(op->op_text, "argument expected");
318
319 switch (op->op_num) {
320 case STREQ:
321 return strcmp(opnd1, opnd2) == 0;
322 case STRNE:
323 return strcmp(opnd1, opnd2) != 0;
324 case STRLT:
325 return strcmp(opnd1, opnd2) < 0;
326 case STRGT:
327 return strcmp(opnd1, opnd2) > 0;
328 case INTEQ:
329 return getn(opnd1) == getn(opnd2);
330 case INTNE:
331 return getn(opnd1) != getn(opnd2);
332 case INTGE:
333 return getn(opnd1) >= getn(opnd2);
334 case INTGT:
335 return getn(opnd1) > getn(opnd2);
336 case INTLE:
337 return getn(opnd1) <= getn(opnd2);
338 case INTLT:
339 return getn(opnd1) < getn(opnd2);
340 case FILNT:
341 return newerf(opnd1, opnd2);
342 case FILOT:
343 return olderf(opnd1, opnd2);
344 case FILEQ:
345 return equalf(opnd1, opnd2);
346 }
347 /* NOTREACHED */
348 return 1;
349}
350
351static int filstat(char *nm, enum token mode)
352{
353 struct stat s;
354 unsigned int i;
355
356 if (mode == FILSYM) {
357#ifdef S_IFLNK
358 if (lstat(nm, &s) == 0) {
359 i = S_IFLNK;
360 goto filetype;
361 }
362#endif
363 return 0;
364 }
365
366 if (stat(nm, &s) != 0)
367 return 0;
368
369 switch (mode) {
370 case FILRD:
371 return test_eaccess(nm, R_OK) == 0;
372 case FILWR:
373 return test_eaccess(nm, W_OK) == 0;
374 case FILEX:
375 return test_eaccess(nm, X_OK) == 0;
376 case FILEXIST:
377 return 1;
378 case FILREG:
379 i = S_IFREG;
380 goto filetype;
381 case FILDIR:
382 i = S_IFDIR;
383 goto filetype;
384 case FILCDEV:
385 i = S_IFCHR;
386 goto filetype;
387 case FILBDEV:
388 i = S_IFBLK;
389 goto filetype;
390 case FILFIFO:
391#ifdef S_IFIFO
392 i = S_IFIFO;
393 goto filetype;
394#else
395 return 0;
396#endif
397 case FILSOCK:
398#ifdef S_IFSOCK
399 i = S_IFSOCK;
400 goto filetype;
401#else
402 return 0;
403#endif
404 case FILSUID:
405 i = S_ISUID;
406 goto filebit;
407 case FILSGID:
408 i = S_ISGID;
409 goto filebit;
410 case FILSTCK:
411 i = S_ISVTX;
412 goto filebit;
413 case FILGZ:
414 return s.st_size > 0L;
415 case FILUID:
416 return s.st_uid == geteuid();
417 case FILGID:
418 return s.st_gid == getegid();
419 default:
420 return 1;
421 }
422
423 filetype:
424 return ((s.st_mode & S_IFMT) == i);
425
426 filebit:
427 return ((s.st_mode & i) != 0);
428}
429
430static enum token t_lex(char *s)
431{
432 struct t_op const *op = ops;
433
434 if (s == 0) {
435 t_wp_op = (struct t_op *) 0;
436 return EOI;
437 }
438 while (op->op_text) {
439 if (strcmp(s, op->op_text) == 0) {
440 t_wp_op = op;
441 return op->op_num;
442 }
443 op++;
444 }
445 t_wp_op = (struct t_op *) 0;
446 return OPERAND;
447}
448
449/* atoi with error detection */
450static arith_t getn(const char *s)
451{
452 char *p;
453#ifdef CONFIG_FEATURE_TEST_64
454 long long r;
455#else
456 long r;
457#endif
458
459 errno = 0;
460#ifdef CONFIG_FEATURE_TEST_64
461 r = strtoll(s, &p, 10);
462#else
463 r = strtol(s, &p, 10);
464#endif
465
466 if (errno != 0)
467 bb_error_msg_and_die("%s: out of range", s);
468
469 /* p = bb_skip_whitespace(p); avoid const warning */
470 if (*(bb_skip_whitespace(p)))
471 bb_error_msg_and_die("%s: bad number", s);
472
473 return r;
474}
475
476static int newerf(const char *f1, const char *f2)
477{
478 struct stat b1, b2;
479
480 return (stat(f1, &b1) == 0 &&
481 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
482}
483
484static int olderf(const char *f1, const char *f2)
485{
486 struct stat b1, b2;
487
488 return (stat(f1, &b1) == 0 &&
489 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
490}
491
492static int equalf(const char *f1, const char *f2)
493{
494 struct stat b1, b2;
495
496 return (stat(f1, &b1) == 0 &&
497 stat(f2, &b2) == 0 &&
498 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
499}
500
501/* Do the same thing access(2) does, but use the effective uid and gid,
502 and don't make the mistake of telling root that any file is
503 executable. */
504static int test_eaccess(char *path, int mode)
505{
506 struct stat st;
507 unsigned int euid = geteuid();
508
509 if (stat(path, &st) < 0)
510 return (-1);
511
512 if (euid == 0) {
513 /* Root can read or write any file. */
514 if (mode != X_OK)
515 return (0);
516
517 /* Root can execute any file that has any one of the execute
518 bits set. */
519 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
520 return (0);
521 }
522
523 if (st.st_uid == euid) /* owner */
524 mode <<= 6;
525 else if (is_a_group_member(st.st_gid))
526 mode <<= 3;
527
528 if (st.st_mode & mode)
529 return (0);
530
531 return (-1);
532}
533
534static void initialize_group_array()
535{
536 ngroups = getgroups(0, NULL);
537 group_array = xrealloc(group_array, ngroups * sizeof(gid_t));
538 getgroups(ngroups, group_array);
539}
540
541/* Return non-zero if GID is one that we have in our groups list. */
542static int is_a_group_member(gid_t gid)
543{
544 register int i;
545
546 /* Short-circuit if possible, maybe saving a call to getgroups(). */
547 if (gid == getgid() || gid == getegid())
548 return (1);
549
550 if (ngroups == 0)
551 initialize_group_array();
552
553 /* Search through the list looking for GID. */
554 for (i = 0; i < ngroups; i++)
555 if (gid == group_array[i])
556 return (1);
557
558 return (0);
559}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini touch implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 _NOT_ compliant -- options -a, -m, -r, -t not supported. */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/touch.html */
25
26/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
27 *
28 * Previous version called open() and then utime(). While this will be
29 * be necessary to implement -r and -t, it currently only makes things bigger.
30 * Also, exiting on a failure was a bug. All args should be processed.
31 */
32
33#include <stdio.h>
34#include <sys/types.h>
35#include <fcntl.h>
36#include <utime.h>
37#include <errno.h>
38#include <unistd.h>
39#include <stdlib.h>
40#include "busybox.h"
41
42extern int touch_main(int argc, char **argv)
43{
44 int fd;
45 int flags;
46 int status = EXIT_SUCCESS;
47
48 flags = bb_getopt_ulflags(argc, argv, "c");
49
50 argv += optind;
51
52 if (!*argv) {
53 bb_show_usage();
54 }
55
56 do {
57 if (utime(*argv, NULL)) {
58 if (errno == ENOENT) { /* no such file*/
59 if (flags & 1) { /* Creation is disabled, so ignore. */
60 continue;
61 }
62 /* Try to create the file. */
63 fd = open(*argv, O_RDWR | O_CREAT,
64 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
65 );
66 if ((fd >= 0) && !close(fd)) {
67 continue;
68 }
69 }
70 status = EXIT_FAILURE;
71 bb_perror_msg("%s", *argv);
72 }
73 } while (*++argv);
74
75 return status;
76}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini tr implementation for busybox
4 *
5 * Copyright (c) Michiel Huisjes
6 *
7 * This version of tr is adapted from Minix tr and was modified
8 * by Erik Andersen <andersen@codepoet.org> to be used in busybox.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 * Original copyright notice is retained at the end of this file.
25 */
26
27#include <stdio.h>
28#include <string.h>
29#include <stdlib.h>
30#include <unistd.h>
31#include <sys/types.h>
32#include "busybox.h"
33
34/* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
35 * enabled, we otherwise get a "storage size isn't constant error. */
36#define ASCII 0377
37
38/* some "globals" shared across this file */
39static char com_fl, del_fl, sq_fl;
40static short in_index, out_index;
41/* these last are pointers to static buffers declared in tr_main */
42static unsigned char *poutput, *pinput;
43static unsigned char *pvector;
44static char *pinvec, *poutvec;
45
46
47static void convert(void)
48{
49 short read_chars = 0;
50 short c, coded;
51 short last = -1;
52
53 for (;;) {
54 if (in_index == read_chars) {
55 if ((read_chars = read(0, (char *) pinput, BUFSIZ)) <= 0) {
56 if (write(1, (char *) poutput, out_index) != out_index)
57 bb_error_msg(bb_msg_write_error);
58 exit(0);
59 }
60 in_index = 0;
61 }
62 c = pinput[in_index++];
63 coded = pvector[c];
64 if (del_fl && pinvec[c])
65 continue;
66 if (sq_fl && last == coded && (pinvec[c] || poutvec[coded]))
67 continue;
68 poutput[out_index++] = last = coded;
69 if (out_index == BUFSIZ) {
70 if (write(1, (char *) poutput, out_index) != out_index)
71 bb_error_msg_and_die(bb_msg_write_error);
72 out_index = 0;
73 }
74 }
75
76 /* NOTREACHED */
77}
78
79static void map(register unsigned char *string1, unsigned int string1_len,
80 register unsigned char *string2, unsigned int string2_len)
81{
82 unsigned char last = '0';
83 unsigned int i, j;
84
85 for (j = 0, i = 0; i < string1_len; i++) {
86 if (string2_len <= j)
87 pvector[string1[i]] = last;
88 else
89 pvector[string1[i]] = last = string2[j++];
90 }
91}
92
93/* supported constructs:
94 * Ranges, e.g., [0-9] ==> 0123456789
95 * Escapes, e.g., \a ==> Control-G
96 */
97static unsigned int expand(const char *arg, register unsigned char *buffer)
98{
99 unsigned char *buffer_start = buffer;
100 int i, ac;
101
102 while (*arg) {
103 if (*arg == '\\') {
104 arg++;
105 *buffer++ = bb_process_escape_sequence(&arg);
106 } else if (*(arg+1) == '-') {
107 ac = *(arg+2);
108 if(ac == 0) {
109 *buffer++ = *arg++;
110 continue;
111 }
112 i = *arg;
113 while (i <= ac)
114 *buffer++ = i++;
115 arg += 3; /* Skip the assumed a-z */
116 } else if (*arg == '[') {
117 arg++;
118 i = *arg++;
119 if (*arg++ != '-') {
120 *buffer++ = '[';
121 arg -= 2;
122 continue;
123 }
124 ac = *arg++;
125 while (i <= ac)
126 *buffer++ = i++;
127 arg++; /* Skip the assumed ']' */
128 } else
129 *buffer++ = *arg++;
130 }
131
132 return (buffer - buffer_start);
133}
134
135static int complement(unsigned char *buffer, int buffer_len)
136{
137 register short i, j, ix;
138 char conv[ASCII + 2];
139
140 ix = 0;
141 for (i = 0; i <= ASCII; i++) {
142 for (j = 0; j < buffer_len; j++)
143 if (buffer[j] == i)
144 break;
145 if (j == buffer_len)
146 conv[ix++] = i & ASCII;
147 }
148 memcpy(buffer, conv, ix);
149 return ix;
150}
151
152extern int tr_main(int argc, char **argv)
153{
154 register unsigned char *ptr;
155 int output_length=0, input_length;
156 int idx = 1;
157 int i;
158 RESERVE_CONFIG_BUFFER(output, BUFSIZ);
159 RESERVE_CONFIG_BUFFER(input, BUFSIZ);
160 RESERVE_CONFIG_UBUFFER(vector, ASCII+1);
161 RESERVE_CONFIG_BUFFER(invec, ASCII+1);
162 RESERVE_CONFIG_BUFFER(outvec, ASCII+1);
163
164 /* ... but make them available globally */
165 poutput = output;
166 pinput = input;
167 pvector = vector;
168 pinvec = invec;
169 poutvec = outvec;
170
171 if (argc > 1 && argv[idx][0] == '-') {
172 for (ptr = (unsigned char *) &argv[idx][1]; *ptr; ptr++) {
173 switch (*ptr) {
174 case 'c':
175 com_fl = TRUE;
176 break;
177 case 'd':
178 del_fl = TRUE;
179 break;
180 case 's':
181 sq_fl = TRUE;
182 break;
183 default:
184 bb_show_usage();
185 }
186 }
187 idx++;
188 }
189 for (i = 0; i <= ASCII; i++) {
190 vector[i] = i;
191 invec[i] = outvec[i] = FALSE;
192 }
193
194 if (argv[idx] != NULL) {
195 input_length = expand(argv[idx++], input);
196 if (com_fl)
197 input_length = complement(input, input_length);
198 if (argv[idx] != NULL) {
199 if (*argv[idx] == '\0')
200 bb_error_msg_and_die("STRING2 cannot be empty");
201 output_length = expand(argv[idx], output);
202 map(input, input_length, output, output_length);
203 }
204 for (i = 0; i < input_length; i++)
205 invec[(unsigned char)input[i]] = TRUE;
206 for (i = 0; i < output_length; i++)
207 outvec[(unsigned char)output[i]] = TRUE;
208 }
209 convert();
210 return (0);
211}
212
213/*
214 * Copyright (c) 1987,1997, Prentice Hall
215 * All rights reserved.
216 *
217 * Redistribution and use of the MINIX operating system in source and
218 * binary forms, with or without modification, are permitted provided
219 * that the following conditions are met:
220 *
221 * Redistributions of source code must retain the above copyright
222 * notice, this list of conditions and the following disclaimer.
223 *
224 * Redistributions in binary form must reproduce the above
225 * copyright notice, this list of conditions and the following
226 * disclaimer in the documentation and/or other materials provided
227 * with the distribution.
228 *
229 * Neither the name of Prentice Hall nor the names of the software
230 * authors or contributors may be used to endorse or promote
231 * products derived from this software without specific prior
232 * written permission.
233 *
234 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, AUTHORS, AND
235 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
236 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
237 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
238 * IN NO EVENT SHALL PRENTICE HALL OR ANY AUTHORS OR CONTRIBUTORS BE
239 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
240 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
241 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
242 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
243 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
244 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
245 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
246 *
247 */
248
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini true implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/true.html */
25
26#include <stdlib.h>
27#include "busybox.h"
28
29extern int true_main(int argc, char **argv)
30{
31 return EXIT_SUCCESS;
32}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * tty implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/tty.html */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include "busybox.h"
30
31extern int tty_main(int argc, char **argv)
32{
33 const char *s;
34 int silent; /* Note: No longer relevant in SUSv3. */
35 int retval;
36
37 bb_default_error_retval = 2; /* SUSv3 requires > 1 for error. */
38
39 silent = bb_getopt_ulflags(argc, argv, "s");
40
41 /* gnu tty outputs a warning that it is ignoring all args. */
42 bb_warn_ignoring_args(argc - optind);
43
44 retval = 0;
45
46 if ((s = ttyname(0)) == NULL) {
47 /* According to SUSv3, ttyname can on fail with EBADF or ENOTTY.
48 * We know the file descriptor is good, so failure means not a tty. */
49 s = "not a tty";
50 retval = 1;
51 }
52
53 if (!silent) {
54 puts(s);
55 }
56
57 bb_fflush_stdout_and_exit(retval);
58}
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 @@
1/* vi: set sw=4 ts=4: */
2/* uname -- print system information
3 Copyright (C) 1989-1999 Free Software Foundation, Inc.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18
19/* BB_AUDIT SUSv3 compliant */
20/* http://www.opengroup.org/onlinepubs/007904975/utilities/uname.html */
21
22/* Option Example
23
24 -s, --sysname SunOS
25 -n, --nodename rocky8
26 -r, --release 4.0
27 -v, --version
28 -m, --machine sun
29 -a, --all SunOS rocky8 4.0 sun
30
31 The default behavior is equivalent to `-s'.
32
33 David MacKenzie <djm@gnu.ai.mit.edu> */
34
35/* Busyboxed by Erik Andersen */
36
37/* Further size reductions by Glenn McGrath and Manuel Novoa III. */
38
39/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
40 *
41 * Now does proper error checking on i/o. Plus some further space savings.
42 */
43
44#include <stdio.h>
45#include <stdlib.h>
46#include <stddef.h>
47#include <string.h>
48#include <unistd.h>
49#include <sys/types.h>
50#include <sys/utsname.h>
51#include "busybox.h"
52
53typedef struct {
54 struct utsname name;
55 char processor[8]; /* for "unknown" */
56} uname_info_t;
57
58static const char options[] = "snrvmpa";
59static const unsigned short int utsname_offset[] = {
60 offsetof(uname_info_t,name.sysname),
61 offsetof(uname_info_t,name.nodename),
62 offsetof(uname_info_t,name.release),
63 offsetof(uname_info_t,name.version),
64 offsetof(uname_info_t,name.machine),
65 offsetof(uname_info_t,processor)
66};
67
68int uname_main(int argc, char **argv)
69{
70 uname_info_t uname_info;
71#if defined(__sparc__) && defined(__linux__)
72 char *fake_sparc = getenv("FAKE_SPARC");
73#endif
74 const unsigned short int *delta;
75 char toprint;
76
77 toprint = bb_getopt_ulflags(argc, argv, options);
78
79 if (argc != optind) {
80 bb_show_usage();
81 }
82
83 if (toprint & (1 << 6)) {
84 toprint = 0x3f;
85 }
86
87 if (toprint == 0) {
88 toprint = 1; /* sysname */
89 }
90
91 if (uname(&uname_info.name) == -1) {
92 bb_error_msg_and_die("cannot get system name");
93 }
94
95#if defined(__sparc__) && defined(__linux__)
96 if ((fake_sparc != NULL)
97 && ((fake_sparc[0] == 'y')
98 || (fake_sparc[0] == 'Y'))) {
99 strcpy(uname_info.name.machine, "sparc");
100 }
101#endif
102
103 strcpy(uname_info.processor, "unknown");
104
105 delta=utsname_offset;
106 do {
107 if (toprint & 1) {
108 bb_printf(((char *)(&uname_info)) + *delta);
109 if (toprint > 1) {
110 putchar(' ');
111 }
112 }
113 ++delta;
114 } while (toprint >>= 1);
115 putchar('\n');
116
117 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
118}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * uniq implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 compliant */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/uniq.html */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <ctype.h>
30#include <unistd.h>
31#include "busybox.h"
32#include "libcoreutils/coreutils.h"
33
34static const char uniq_opts[] = "f:s:cdu\0\7\3\5\1\2\4";
35
36int uniq_main(int argc, char **argv)
37{
38 FILE *in, *out;
39 /* Note: Ignore the warning about dups and e0 being used uninitialized.
40 * They will be initialized on the fist pass of the loop (since s0 is NULL). */
41 unsigned long dups, skip_fields, skip_chars, i;
42 const char *s0, *e0, *s1, *e1, *input_filename;
43 int opt;
44 int uniq_flags = 6; /* -u */
45
46 skip_fields = skip_chars = 0;
47
48 while ((opt = getopt(argc, argv, uniq_opts)) > 0) {
49 if (opt == 'f') {
50 skip_fields = bb_xgetularg10(optarg);
51 } else if (opt == 's') {
52 skip_chars = bb_xgetularg10(optarg);
53 } else if ((s0 = strchr(uniq_opts, opt)) != NULL) {
54 uniq_flags &= s0[4];
55 uniq_flags |= s0[7];
56 } else {
57 bb_show_usage();
58 }
59 }
60
61 input_filename = *(argv += optind);
62
63 in = xgetoptfile_sort_uniq(argv, "r");
64 if (*argv) {
65 ++argv;
66 }
67 out = xgetoptfile_sort_uniq(argv, "w");
68 if (*argv && argv[1]) {
69 bb_show_usage();
70 }
71
72 s0 = NULL;
73
74 /* gnu uniq ignores newlines */
75 while ((s1 = bb_get_chomped_line_from_file(in)) != NULL) {
76 e1 = s1;
77 for (i=skip_fields ; i ; i--) {
78 e1 = bb_skip_whitespace(e1);
79 while (*e1 && !isspace(*e1)) {
80 ++e1;
81 }
82 }
83 for (i = skip_chars ; *e1 && i ; i--) {
84 ++e1;
85 }
86 if (s0) {
87 if (strcmp(e0, e1) == 0) {
88 ++dups; /* Note: Testing for overflow seems excessive. */
89 continue;
90 }
91 DO_LAST:
92 if ((dups && (uniq_flags & 2)) || (!dups && (uniq_flags & 4))) {
93 bb_fprintf(out, "\0%7d\t" + (uniq_flags & 1), dups + 1);
94 bb_fprintf(out, "%s\n", s0);
95 }
96 free((void *)s0);
97 }
98
99 s0 = s1;
100 e0 = e1;
101 dups = 0;
102 }
103
104 if (s0) {
105 e1 = NULL;
106 goto DO_LAST;
107 }
108
109 bb_xferror(in, input_filename);
110
111 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
112}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * usleep implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */
24
25#include <stdlib.h>
26#include <limits.h>
27#include <unistd.h>
28#include "busybox.h"
29
30extern int usleep_main(int argc, char **argv)
31{
32 if (argc != 2) {
33 bb_show_usage();
34 }
35
36 if (usleep(bb_xgetularg10_bnd(argv[1], 0, UINT_MAX))) {
37 bb_perror_nomsg_and_die();
38 }
39
40 return EXIT_SUCCESS;
41}
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 @@
1/*
2 * GPLv2
3 * Copyright 2003, Glenn McGrath <bug1@iinet.net.au>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as published
7 * by the Free Software Foundation; either version 2 of the License.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 *
18 * Based on specification from
19 * http://www.opengroup.org/onlinepubs/007904975/utilities/uuencode.html
20 *
21 * Bugs: the spec doesnt mention anything about "`\n`\n" prior to the "end" line
22 */
23
24
25#include <stdio.h>
26#include <errno.h>
27#include <getopt.h>
28#include <string.h>
29#include <stdlib.h>
30
31#include "libbb.h"
32
33static int read_stduu(FILE *src_stream, FILE *dst_stream)
34{
35 char *line;
36
37 while ((line = bb_get_chomped_line_from_file(src_stream)) != NULL) {
38 int length;
39 char *line_ptr = line;
40
41 if (strcmp(line, "end") == 0) {
42 return(EXIT_SUCCESS);
43 }
44 length = ((*line_ptr - 0x20) & 0x3f)* 4 / 3;
45
46 if (length <= 0) {
47 /* Ignore the "`\n" line, why is it even in the encode file ? */
48 continue;
49 }
50 if (length > 60) {
51 bb_error_msg_and_die("Line too long");
52 }
53
54 line_ptr++;
55 /* Tolerate an overly long line to acomadate a possible exta '`' */
56 if (strlen(line_ptr) < length) {
57 bb_error_msg_and_die("Short file");
58 }
59
60 while (length > 0) {
61 /* Merge four 6 bit chars to three 8 bit chars */
62 fputc(((line_ptr[0] - 0x20) & 077) << 2 | ((line_ptr[1] - 0x20) & 077) >> 4, dst_stream);
63 line_ptr++;
64 length--;
65 if (length == 0) {
66 break;
67 }
68
69 fputc(((line_ptr[0] - 0x20) & 077) << 4 | ((line_ptr[1] - 0x20) & 077) >> 2, dst_stream);
70 line_ptr++;
71 length--;
72 if (length == 0) {
73 break;
74 }
75
76 fputc(((line_ptr[0] - 0x20) & 077) << 6 | ((line_ptr[1] - 0x20) & 077), dst_stream);
77 line_ptr += 2;
78 length -= 2;
79 }
80 free(line);
81 }
82 bb_error_msg_and_die("Short file");
83}
84
85static int read_base64(FILE *src_stream, FILE *dst_stream)
86{
87 const char *base64_table =
88 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n";
89 char term_count = 0;
90
91 while (1) {
92 char translated[4];
93 int count = 0;
94
95 while (count < 4) {
96 char *table_ptr;
97 char ch;
98
99 /* Get next _valid_ character */
100 do {
101 ch = fgetc(src_stream);
102 if (ch == EOF) {
103 bb_error_msg_and_die("Short file");
104 }
105 } while ((table_ptr = strchr(base64_table, ch)) == NULL);
106
107 /* Convert encoded charcter to decimal */
108 ch = table_ptr - base64_table;
109
110 if (*table_ptr == '=') {
111 if (term_count == 0) {
112 translated[count] = 0;
113 break;
114 }
115 term_count++;
116 }
117 else if (*table_ptr == '\n') {
118 /* Check for terminating line */
119 if (term_count == 5) {
120 return(EXIT_SUCCESS);
121 }
122 term_count = 1;
123 continue;
124 } else {
125 translated[count] = ch;
126 count++;
127 term_count = 0;
128 }
129 }
130
131 /* Merge 6 bit chars to 8 bit */
132 fputc(translated[0] << 2 | translated[1] >> 4, dst_stream);
133 if (count > 2) {
134 fputc(translated[1] << 4 | translated[2] >> 2, dst_stream);
135 }
136 if (count > 3) {
137 fputc(translated[2] << 6 | translated[3], dst_stream);
138 }
139 }
140}
141
142extern int uudecode_main(int argc, char **argv)
143{
144 int (*decode_fn_ptr) (FILE * src, FILE * dst);
145 FILE *src_stream;
146 char *outname = NULL;
147 char *line;
148 int opt;
149
150 opt = bb_getopt_ulflags(argc, argv, "o:", &outname);
151
152 if (optind == argc) {
153 src_stream = stdin;
154 } else if (optind + 1 == argc) {
155 src_stream = bb_xfopen(argv[optind], "r");
156 } else {
157 bb_show_usage();
158 }
159
160 /* Search for the start of the encoding */
161 while ((line = bb_get_chomped_line_from_file(src_stream)) != NULL) {
162 char *line_ptr = NULL;
163
164 if (line == NULL) {
165 break;
166 } else if (strncmp(line, "begin-base64 ", 13) == 0) {
167 line_ptr = line + 13;
168 decode_fn_ptr = read_base64;
169 } else if (strncmp(line, "begin ", 6) == 0) {
170 line_ptr = line + 6;
171 decode_fn_ptr = read_stduu;
172 }
173
174 if (line_ptr) {
175 FILE *dst_stream;
176 int mode;
177 int ret;
178
179 mode = strtoul(line_ptr, NULL, 8);
180 if (outname == NULL) {
181 outname = strchr(line_ptr, ' ');
182 if ((outname == NULL) || (*outname == '\0')) {
183 break;
184 }
185 outname++;
186 }
187 if (strcmp(outname, "-") == 0) {
188 dst_stream = stdout;
189 } else {
190 dst_stream = bb_xfopen(outname, "w");
191 chmod(outname, mode & (S_IRWXU | S_IRWXG | S_IRWXO));
192 }
193 free(line);
194 ret = decode_fn_ptr(src_stream, dst_stream);
195 bb_fclose_nonstdin(src_stream);
196 return(ret);
197 }
198 free(line);
199 }
200 bb_error_msg_and_die("No `begin' line");
201}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright (C) 2000 by Glenn McGrath
4 *
5 * based on the function base64_encode from http.c in wget v1.6
6 * Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 */
22#include <getopt.h>
23#include <stdio.h>
24#include <string.h>
25#include <stdlib.h>
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <unistd.h>
29#include "busybox.h"
30
31/* Conversion table. for base 64 */
32static const char tbl_base64[65] = {
33 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
34 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
35 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
36 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
37 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
38 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
39 'w', 'x', 'y', 'z', '0', '1', '2', '3',
40 '4', '5', '6', '7', '8', '9', '+', '/',
41 '=' /* termination character */
42};
43
44static const char tbl_std[65] = {
45 '`', '!', '"', '#', '$', '%', '&', '\'',
46 '(', ')', '*', '+', ',', '-', '.', '/',
47 '0', '1', '2', '3', '4', '5', '6', '7',
48 '8', '9', ':', ';', '<', '=', '>', '?',
49 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
50 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
51 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
52 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
53 '`' /* termination character */
54};
55
56/*
57 * Encode the string S of length LENGTH to base64 format and place it
58 * to STORE. STORE will be 0-terminated, and must point to a writable
59 * buffer of at least 1+BASE64_LENGTH(length) bytes.
60 * where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3))
61 */
62static void uuencode (const unsigned char *s, const char *store, const int length, const char *tbl)
63{
64 int i;
65 unsigned char *p = (unsigned char *)store;
66
67 /* Transform the 3x8 bits to 4x6 bits, as required by base64. */
68 for (i = 0; i < length; i += 3) {
69 *p++ = tbl[s[0] >> 2];
70 *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
71 *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
72 *p++ = tbl[s[2] & 0x3f];
73 s += 3;
74 }
75 /* Pad the result if necessary... */
76 if (i == length + 1) {
77 *(p - 1) = tbl[64];
78 }
79 else if (i == length + 2) {
80 *(p - 1) = *(p - 2) = tbl[64];
81 }
82 /* ...and zero-terminate it. */
83 *p = '\0';
84}
85
86#define SRC_BUF_SIZE 45 // This *MUST* be a multiple of 3
87#define DST_BUF_SIZE 4 * ((SRC_BUF_SIZE + 2) / 3)
88int uuencode_main(int argc, char **argv)
89{
90 const int src_buf_size = SRC_BUF_SIZE;
91 const int dst_buf_size = DST_BUF_SIZE;
92 int write_size = dst_buf_size;
93 struct stat stat_buf;
94 FILE *src_stream = stdin;
95 const char *tbl;
96 size_t size;
97 mode_t mode;
98 RESERVE_CONFIG_BUFFER(src_buf, SRC_BUF_SIZE + 1);
99 RESERVE_CONFIG_BUFFER(dst_buf, DST_BUF_SIZE + 1);
100
101 tbl = tbl_std;
102 if (bb_getopt_ulflags(argc, argv, "m") & 1) {
103 tbl = tbl_base64;
104 }
105
106 switch (argc - optind) {
107 case 2:
108 src_stream = bb_xfopen(argv[optind], "r");
109 if (stat(argv[optind], &stat_buf) < 0) {
110 bb_perror_msg_and_die("stat");
111 }
112 mode = stat_buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
113 if (src_stream == stdout) {
114 puts("NULL");
115 }
116 break;
117 case 1:
118 mode = 0666 & ~umask(0666);
119 break;
120 default:
121 bb_show_usage();
122 }
123
124 bb_printf("begin%s %o %s", tbl == tbl_std ? "" : "-base64", mode, argv[argc - 1]);
125
126 while ((size = fread(src_buf, 1, src_buf_size, src_stream)) > 0) {
127 if (size != src_buf_size) {
128 /* write_size is always 60 until the last line */
129 write_size=(4 * ((size + 2) / 3));
130 /* pad with 0s so we can just encode extra bits */
131 memset(&src_buf[size], 0, src_buf_size - size);
132 }
133 /* Encode the buffer we just read in */
134 uuencode(src_buf, dst_buf, size, tbl);
135
136 putchar('\n');
137 if (tbl == tbl_std) {
138 putchar(tbl[size]);
139 }
140 if (fwrite(dst_buf, 1, write_size, stdout) != write_size) {
141 bb_perror_msg_and_die(bb_msg_write_error);
142 }
143 }
144 bb_printf(tbl == tbl_std ? "\n`\nend\n" : "\n====\n");
145
146 bb_xferror(src_stream, "source"); /* TODO - Fix this! */
147
148 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
149}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini watch implementation for busybox
4 *
5 * Copyright (C) 2001 by Michael Habermann <mhabermann@gmx.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 N/A */
24/* BB_AUDIT GNU defects -- only option -n is supported. */
25
26/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
27 *
28 * Removed dependency on date_main(), added proper error checking, and
29 * reduced size.
30 */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <limits.h>
36#include <time.h>
37#include <assert.h>
38#include <unistd.h>
39#include <sys/wait.h>
40#include "busybox.h"
41
42extern int watch_main(int argc, char **argv)
43{
44 const int header_len = 40;
45 time_t t;
46 pid_t pid;
47 unsigned period = 2;
48 int old_stdout;
49 int len, len2;
50 char **watched_argv;
51 char header[header_len + 1];
52
53 if (argc < 2) {
54 bb_show_usage();
55 }
56
57 /* don't use getopt, because it permutes the arguments */
58 ++argv;
59 if ((argc > 3) && !strcmp(*argv, "-n")
60 ) {
61 period = bb_xgetularg10_bnd(argv[1], 1, UINT_MAX);
62 argv += 2;
63 }
64 watched_argv = argv;
65
66 /* create header */
67
68 len = snprintf(header, header_len, "Every %ds:", period);
69 /* Don't bother checking for len < 0, as it should never happen.
70 * But, just to be prepared... */
71 assert(len >= 0);
72 do {
73 len2 = strlen(*argv);
74 if (len + len2 >= header_len-1) {
75 break;
76 }
77 header[len] = ' ';
78 memcpy(header+len+1, *argv, len2);
79 len += len2+1;
80 } while (*++argv);
81
82 header[len] = 0;
83
84 /* thanks to lye, who showed me how to redirect stdin/stdout */
85 old_stdout = dup(1);
86
87 while (1) {
88 time(&t);
89 /* Use dprintf to avoid fflush()ing stdout. */
90 if (dprintf(1, "\033[H\033[J%-*s%s\n", header_len, header, ctime(&t)) < 0) {
91 bb_perror_msg_and_die("printf");
92 }
93
94 pid = vfork(); /* vfork, because of ucLinux */
95 if (pid > 0) {
96 //parent
97 wait(0);
98 sleep(period);
99 } else if (0 == pid) {
100 //child
101 close(1);
102 dup(old_stdout);
103 if (execvp(*watched_argv, watched_argv)) {
104 bb_error_msg_and_die("Couldn't run command\n");
105 }
106 } else {
107 bb_error_msg_and_die("Couldn't vfork\n");
108 }
109 }
110}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * wc implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 _NOT_ compliant -- option -m is not currently supported. */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/wc.html */
25
26/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
27 *
28 * Rewritten to fix a number of problems and do some size optimizations.
29 * Problems in the previous busybox implementation (besides bloat) included:
30 * 1) broken 'wc -c' optimization (read note below)
31 * 2) broken handling of '-' args
32 * 3) no checking of ferror on EOF returns
33 * 4) isprint() wasn't considered when word counting.
34 *
35 * TODO:
36 *
37 * When locale support is enabled, count multibyte chars in the '-m' case.
38 *
39 * NOTES:
40 *
41 * The previous busybox wc attempted an optimization using stat for the
42 * case of counting chars only. I omitted that because it was broken.
43 * It didn't take into account the possibility of input coming from a
44 * pipe, or input from a file with file pointer not at the beginning.
45 *
46 * To implement such a speed optimization correctly, not only do you
47 * need the size, but also the file position. Note also that the
48 * file position may be past the end of file. Consider the example
49 * (adapted from example in gnu wc.c)
50 *
51 * echo hello > /tmp/testfile &&
52 * (dd ibs=1k skip=1 count=0 &> /dev/null ; wc -c) < /tmp/testfile
53 *
54 * for which 'wc -c' should output '0'.
55 */
56
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <unistd.h>
61#include "busybox.h"
62
63#ifdef CONFIG_LOCALE_SUPPORT
64#include <locale.h>
65#include <ctype.h>
66#define isspace_given_isprint(c) isspace(c)
67#else
68#undef isspace
69#undef isprint
70#define isspace(c) ((((c) == ' ') || (((unsigned int)((c) - 9)) <= (13 - 9))))
71#define isprint(c) (((unsigned int)((c) - 0x20)) <= (0x7e - 0x20))
72#define isspace_given_isprint(c) ((c) == ' ')
73#endif
74
75enum {
76 WC_LINES = 0,
77 WC_WORDS = 1,
78 WC_CHARS = 2,
79 WC_LENGTH = 3
80};
81
82/* Note: If this changes, remember to change the initialization of
83 * 'name' in wc_main. It needs to point to the terminating nul. */
84static const char wc_opts[] = "lwcL"; /* READ THE WARNING ABOVE! */
85
86enum {
87 OP_INC_LINE = 1, /* OP_INC_LINE must be 1. */
88 OP_SPACE = 2,
89 OP_NEWLINE = 4,
90 OP_TAB = 8,
91 OP_NUL = 16,
92};
93
94/* Note: If fmt_str changes, the offsets to 's' in the OUTPUT section
95 * will need to be updated. */
96static const char fmt_str[] = " %7u\0 %s\n";
97static const char total_str[] = "total";
98
99int wc_main(int argc, char **argv)
100{
101 FILE *fp;
102 const char *s;
103 unsigned int *pcounts;
104 unsigned int counts[4];
105 unsigned int totals[4];
106 unsigned int linepos;
107 unsigned int u;
108 int num_files = 0;
109 int c;
110 char status = EXIT_SUCCESS;
111 char in_word;
112 char print_type;
113
114 print_type = bb_getopt_ulflags(argc, argv, wc_opts);
115
116 if (print_type == 0) {
117 print_type = (1 << WC_LINES) | (1 << WC_WORDS) | (1 << WC_CHARS);
118 }
119
120 argv += optind;
121 if (!*argv) {
122 *--argv = (char *) bb_msg_standard_input;
123 }
124
125 memset(totals, 0, sizeof(totals));
126
127 pcounts = counts;
128
129 do {
130 ++num_files;
131 if (!(fp = bb_wfopen_input(*argv))) {
132 status = EXIT_FAILURE;
133 continue;
134 }
135
136 memset(counts, 0, sizeof(counts));
137 linepos = 0;
138 in_word = 0;
139
140 do {
141 ++counts[WC_CHARS];
142 c = getc(fp);
143 if (isprint(c)) {
144 ++linepos;
145 if (!isspace_given_isprint(c)) {
146 in_word = 1;
147 continue;
148 }
149 } else if (((unsigned int)(c - 9)) <= 4) {
150 /* \t 9
151 * \n 10
152 * \v 11
153 * \f 12
154 * \r 13
155 */
156 if (c == '\t') {
157 linepos = (linepos | 7) + 1;
158 } else { /* '\n', '\r', '\f', or '\v' */
159 DO_EOF:
160 if (linepos > counts[WC_LENGTH]) {
161 counts[WC_LENGTH] = linepos;
162 }
163 if (c == '\n') {
164 ++counts[WC_LINES];
165 }
166 if (c != '\v') {
167 linepos = 0;
168 }
169 }
170 } else if (c == EOF) {
171 if (ferror(fp)) {
172 bb_perror_msg("%s", *argv);
173 status = EXIT_FAILURE;
174 }
175 --counts[WC_CHARS];
176 goto DO_EOF; /* Treat an EOF as '\r'. */
177 } else {
178 continue;
179 }
180
181 counts[WC_WORDS] += in_word;
182 in_word = 0;
183 if (c == EOF) {
184 break;
185 }
186 } while (1);
187
188 if (totals[WC_LENGTH] < counts[WC_LENGTH]) {
189 totals[WC_LENGTH] = counts[WC_LENGTH];
190 }
191 totals[WC_LENGTH] -= counts[WC_LENGTH];
192
193 bb_fclose_nonstdin(fp);
194
195 OUTPUT:
196 s = fmt_str + 1; /* Skip the leading space on 1st pass. */
197 u = 0;
198 do {
199 if (print_type & (1 << u)) {
200 bb_printf(s, pcounts[u]);
201 s = fmt_str; /* Ok... restore the leading space. */
202 }
203 totals[u] += pcounts[u];
204 } while (++u < 4);
205
206 s += 8; /* Set the format to the empty string. */
207
208 if (*argv != bb_msg_standard_input) {
209 s -= 3; /* We have a name, so do %s conversion. */
210 }
211 bb_printf(s, *argv);
212
213 } while (*++argv);
214
215 /* If more than one file was processed, we want the totals. To save some
216 * space, we set the pcounts ptr to the totals array. This has the side
217 * effect of trashing the totals array after outputting it, but that's
218 * irrelavent since we no longer need it. */
219 if (num_files > 1) {
220 num_files = 0; /* Make sure we don't get here again. */
221 *--argv = (char *) total_str;
222 pcounts = totals;
223 goto OUTPUT;
224 }
225
226 bb_fflush_stdout_and_exit(status);
227}
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 @@
1/* vi: set sw=4 ts=4: */
2/*----------------------------------------------------------------------
3 * Mini who is used to display user name, login time,
4 * idle time and host name.
5 *
6 * Author: Da Chen <dchen@ayrnetworks.com>
7 *
8 * This is a free document; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation:
11 * http://www.gnu.org/copyleft/gpl.html
12 *
13 * Copyright (c) 2002 AYR Networks, Inc.
14 *----------------------------------------------------------------------
15 */
16
17#include <sys/types.h>
18#include <fcntl.h>
19#include <unistd.h>
20#include <stdlib.h>
21#include <utmp.h>
22#include <sys/stat.h>
23#include <errno.h>
24#include <string.h>
25#include <time.h>
26#include "busybox.h"
27
28extern int who_main(int argc, char **argv)
29{
30 struct utmp *ut;
31 struct stat st;
32 int devlen, len;
33 time_t now, idle;
34
35 if (argc > 1)
36 bb_show_usage();
37
38 setutent();
39 devlen = sizeof("/dev/") - 1;
40 printf("USER TTY IDLE FROM HOST\n");
41
42 while ((ut = getutent()) != NULL) {
43 char name[40];
44
45 if (ut->ut_user[0] && ut->ut_type == USER_PROCESS) {
46 len = strlen(ut->ut_line);
47 if (ut->ut_line[0] == '/') {
48 strncpy(name, ut->ut_line, len);
49 name[len] = '\0';
50 strcpy(ut->ut_line, ut->ut_line + devlen);
51 } else {
52 strcpy(name, "/dev/");
53 strncpy(name+devlen, ut->ut_line, len);
54 name[devlen+len] = '\0';
55 }
56
57 printf("%-10s %-8s ", ut->ut_user, ut->ut_line);
58
59 if (stat(name, &st) == 0) {
60 now = time(NULL);
61 idle = now - st.st_atime;
62
63 if (idle < 60)
64 printf("00:00m ");
65 else if (idle < (60 * 60))
66 printf("00:%02dm ", (int)(idle / 60));
67 else if (idle < (24 * 60 * 60))
68 printf("%02d:%02dm ", (int)(idle / (60 * 60)),
69 (int)(idle % (60 * 60)) / 60);
70 else if (idle < (24 * 60 * 60 * 365))
71 printf("%03ddays ", (int)(idle / (24 * 60 * 60)));
72 else
73 printf("%02dyears ", (int) (idle / (24 * 60 * 60 * 365)));
74 } else
75 printf("%-8s ", "?");
76
77 printf("%-12.12s %s\n", ctime(&(ut->ut_tv.tv_sec)) + 4, ut->ut_host);
78 }
79 }
80 endutent();
81
82 return 0;
83}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini whoami implementation for busybox
4 *
5 * Copyright (C) 2000 Edward Betts <edward@debian.org>.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include "busybox.h"
29
30extern int whoami_main(int argc, char **argv)
31{
32 if (argc > 1)
33 bb_show_usage();
34
35 puts(my_getpwuid(NULL, geteuid(), -1));
36 /* exits on error */
37 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
38}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * yes implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
24
25/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
26 *
27 * Size reductions and removed redundant applet name prefix from error messages.
28 */
29
30#include <stdio.h>
31#include <stdlib.h>
32#include "busybox.h"
33
34extern int yes_main(int argc, char **argv)
35{
36 static const char fmt_str[] = " %s";
37 const char *fmt;
38 char **first_arg;
39
40 *argv = "y";
41 if (argc != 1) {
42 ++argv;
43 }
44
45 first_arg = argv;
46 do {
47 fmt = fmt_str + 1;
48 do {
49 bb_printf(fmt, *argv);
50 fmt = fmt_str;
51 } while (*++argv);
52 argv = first_arg;
53 } while (putchar('\n') != EOF);
54
55 bb_perror_nomsg_and_die();
56}
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 @@
1bin
2usr/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 @@
1busybox-cvs (20031212-3) unstable; urgency=low
2
3 * debian/config-udeb-linux:
4 - Enable freeramdisk. (closes: #225360)
5
6 -- Bastian Blank <waldi@debian.org> Tue, 30 Dec 2003 22:36:50 +0100
7
8busybox-cvs (20031212-2) unstable; urgency=low
9
10 * debian/config-udeb:
11 - Enable freeramdisk
12 * archival/libunarchive/data_extract_to_stdout.c:
13 - Don't extract to much (upstream).
14 * archival/libunarchive/data_extract_all.c:
15 - Don't set permissions on symlinks (upstream).
16 * editors/sed.c:
17 - Fix (upstream). (closes: #224676)
18
19 -- Bastian Blank <waldi@debian.org> Tue, 23 Dec 2003 16:59:13 +0100
20
21busybox-cvs (20031212-1) unstable; urgency=low
22
23 * new cvs version
24 - fixes IOR in fdisk. (closes: #223773)
25 * debian/config*
26 - update
27 - remove mtab support from mount in busybox-static. (closes: #222386)
28 - enable progress bar for wget. (closes: #223770)
29 * modutils/obj/obj_s390{,x}.c
30 - fix abi change, R_390_GOTOFF -> R_390_GOTOFF32. (closes: #216528)
31 * acknowledge nmu. (closes: #216950, #216756, #215169, #215613)
32
33 -- Bastian Blank <waldi@debian.org> Fri, 12 Dec 2003 21:19:41 +0100
34
35busybox-cvs (20030926-2.1) unstable; urgency=low
36
37 * NMU
38 * Remove /sbin/init from the udebs, while still leaving init support
39 compiled in. rootskel takes over providing init, but then calls bb init.
40 Remove linuxrc support from the udebs entirely. Closes: #216756
41 * config-floppy-udeb-linux: add minimal find, grep. Closes: #215169
42 * net-udeb-linux-i386: add loopback mount support. Closes: #215613
43
44 -- Joey Hess <joeyh@debian.org> Tue, 21 Oct 2003 12:47:52 -0400
45
46busybox-cvs (20030926-2) unstable; urgency=low
47
48 * debian/config-floppy-udeb-linux
49 - update for new floppy images (closes: #212986, #214102)
50
51 -- Bastian Blank <waldi@debian.org> Thu, 09 Oct 2003 12:25:49 +0200
52
53busybox-cvs (20030926-1) unstable; urgency=low
54
55 * new cvs version
56 * Makefile
57 - fix libpwdgrp link (upstream) (closes: #211675)
58 * Rules.mak.in
59 - fix optimization (closes: #212485)
60 * debian/config*
61 - update
62 * debian/config*udeb*
63 - move linux-i386 to linux (enable modutils on any linux arch)
64 - rename net to floppy
65 - enable wget status bar (closes: #211457)
66 * init/init.c
67 - workaround race conditions (closes: #212764)
68
69 -- Bastian Blank <waldi@debian.org> Fri, 26 Sep 2003 15:10:14 +0200
70
71busybox-cvs (0.60.99.cvs20030819-3) unstable; urgency=low
72
73 * shell/ash.c
74 - fix signal handling (upstream)
75
76 -- Bastian Blank <waldi@debian.org> Mon, 15 Sep 2003 18:12:09 +0200
77
78busybox-cvs (0.60.99.cvs20030819-2) unstable; urgency=low
79
80 * Fix configure permisions
81 * Set source Section to embedded
82
83 -- Glenn McGrath <bug1@debian.org> Mon, 25 Aug 2003 06:33:19 +0000
84
85busybox-cvs (0.60.99.cvs20030819-1) unstable; urgency=low
86
87 * new cvs version
88
89 -- Bastian Blank <waldi@debian.org> Tue, 19 Aug 2003 13:18:54 +0200
90
91busybox-cvs (0.60.99.cvs20030426-10) unstable; urgency=low
92
93 * archival/libunarchive/*
94 - add hardlink support (pending)
95 * debian/config-*udeb*
96 - add support for oldgnu tar format
97
98 -- Bastian Blank <waldi@debian.org> Tue, 10 Jun 2003 12:06:41 +0200
99
100busybox-cvs (0.60.99.cvs20030426-9) unstable; urgency=low
101
102 * modutils/depmod.c
103 - fix base_dir for modules.dep
104
105 -- Bastian Blank <waldi@debian.org> Sat, 07 Jun 2003 14:52:54 +0200
106
107busybox-cvs (0.60.99.cvs20030426-8) unstable; urgency=low
108
109 * util-linux/fdisk.c
110 - fix syscalls. (pending)
111 * libpwd/*
112 - don't build setgroups.o
113
114 -- Bastian Blank <waldi@debian.org> Tue, 03 Jun 2003 11:26:28 +0200
115
116busybox-cvs (0.60.99.cvs20030426-7) unstable; urgency=low
117
118 * libbb/*syscallc.c
119 - fix syscalls. (upstream) (closes: #194631)
120 * modutils/*
121 - update complete objects code from upstream. (pending)
122 * debian/config-*udeb*
123 - update to busybox-applets.txt:1.14
124
125 -- Bastian Blank <waldi@debian.org> Thu, 29 May 2003 16:17:14 +0200
126
127busybox-cvs (0.60.99.cvs20030426-6) unstable; urgency=low
128
129 * modutils/Makefile.in
130 - don't build anything if deactivated
131
132 -- Bastian Blank <waldi@debian.org> Sat, 24 May 2003 18:57:14 +0200
133
134busybox-cvs (0.60.99.cvs20030426-5) unstable; urgency=low
135
136 * debian/config-*udeb*
137 - revert changes (closes: #192717, #192753)
138 - readd initrd support (workaround)
139 - make init quiet
140 * archival/tar.c
141 - fix usage of tar -O (upstream) (closes: #193788)
142
143 -- Bastian Blank <waldi@debian.org> Sat, 24 May 2003 13:25:36 +0200
144
145busybox-cvs (0.60.99.cvs20030426-4) unstable; urgency=low
146
147 * debian/config-*udeb*
148 - update to busybox-applets.txt:1.12
149 * debian/rules
150 - use system instead of arch to determine which config file to use
151
152 -- Bastian Blank <waldi@debian.org> Fri, 09 May 2003 11:21:13 +0200
153
154busybox-cvs (0.60.99.cvs20030426-3) unstable; urgency=low
155
156 * debian/config-udeb*
157 - update to busybox-applets.txt:1.11
158
159 -- Bastian Blank <waldi@debian.org> Wed, 07 May 2003 10:37:40 +0200
160
161busybox-cvs (0.60.99.cvs20030426-2) unstable; urgency=low
162
163 * modutils/*
164 - modprobe failes gracefully if the module is already loaded. (pending)
165 - implement depmod. (pending)
166 * debian/config*
167 - update
168
169 -- Bastian Blank <waldi@debian.org> Wed, 30 Apr 2003 14:12:36 +0200
170
171busybox-cvs (0.60.99.cvs20030426-1) unstable; urgency=low
172
173 * new cvs version
174
175 -- Bastian Blank <waldi@debian.org> Sat, 26 Apr 2003 18:33:17 +0200
176
177busybox-cvs (0.60.99.cvs20030420-4) unstable; urgency=low
178
179 * network/libiproute/iproute.c
180 - fix usage of ip route flush (upstream)
181
182 -- Bastian Blank <waldi@debian.org> Fri, 25 Apr 2003 16:48:43 +0200
183
184busybox-cvs (0.60.99.cvs20030420-3) unstable; urgency=low
185
186 * debian/config-*udeb*
187 - update to busybox-applets.txt:1.10
188
189 -- Bastian Blank <waldi@debian.org> Fri, 25 Apr 2003 14:03:23 +0200
190
191busybox-cvs (0.60.99.cvs20030420-2) unstable; urgency=low
192
193 * archival/tar.c, archival/libunarchive/data_extract_all.c
194 - unlink files first (upstream patch)
195
196 -- Bastian Blank <waldi@debian.org> Mon, 21 Apr 2003 12:42:06 +0200
197
198busybox-cvs (0.60.99.cvs20030420-1) unstable; urgency=low
199
200 * new cvs version
201 * debian/config*-udeb*
202 - add ifconfig/route for easier transition
203 * debian/config-net-udeb-i386
204 - add logger
205 * debian/config-udeb*
206 - add uniq
207
208 -- Bastian Blank <waldi@debian.org> Sun, 20 Apr 2003 21:02:58 +0200
209
210busybox-cvs (0.60.99.cvs20030405-1) unstable; urgency=low
211
212 * new cvs version
213 * debian/control
214 - add busybox-cvs-net-udeb
215 * debian/rules
216 - arch depend debs
217 - new net-udeb
218 * debian/config*
219 - cleanup applet list
220
221 -- Bastian Blank <waldi@debian.org> Sat, 05 Apr 2003 11:44:50 +0200
222
223busybox-cvs (0.60.99.cvs20030221-1) unstable; urgency=low
224
225 * new cvs version
226 * enable new applets in udeb
227
228 -- Bastian Blank <waldi@debian.org> Fri, 21 Feb 2003 23:15:16 +0100
229
230busybox-cvs (0.60.99.cvs20030114-1) unstable; urgency=low
231
232 * new cvs version
233
234 -- Bastian Blank <waldi@debian.org> Tue, 14 Jan 2003 17:06:43 +0000
235
236busybox-cvs (0.60.99.cvs20030105-1) unstable; urgency=low
237
238 * Fix ip command build failure on ia64 (Closes: #172580
239 * Dont build with BSD partition table support in fdisk, fails on m68k
240
241 -- Glenn McGrath <bug1@home> Sun, 5 Jan 2003 12:48:05 +1100
242
243busybox-cvs (0.60.99.cvs20030104-2) unstable; urgency=low
244
245 * floppy-retriever needs the cut command in the udeb
246
247 -- Glenn McGrath <bug1@debian.org> Sat, 4 Jan 2003 17:13:05 +1100
248
249busybox-cvs (0.60.99.cvs20030104-1) unstable; urgency=low
250
251 * new cvs version
252 * Include new applets in the static package
253 * Include ash in the udeb
254
255 -- Glenn McGrath <bug1@debian.org> Sat, 4 Jan 2003 13:39:04 +1100
256
257busybox-cvs (0.60.99.cvs20021214-1) unstable; urgency=low
258
259 * new cvs version
260 - udhcp merge.
261 * fix location of ip link.
262
263 -- Bastian Blank <waldi@debian.org> Sat, 14 Dec 2002 13:52:15 +0100
264
265busybox-cvs (0.60.99.cvs20021210-1) unstable; urgency=low
266
267 * new cvs version
268 - various upstream fixes found in the last version.
269 * busybox-cvs-udeb
270 - include readlink and realpath.
271
272 -- Bastian Blank <waldi@debian.org> Tue, 10 Dec 2002 21:23:40 +0100
273
274busybox-cvs (0.60.99.cvs20021209-2) unstable; urgency=low
275
276 * cleanup scripts/config/ within make clean (closes: #172413)
277 * busybox-cvs-udeb
278 - include readlink
279 * include manpages within busybox-cvs and busybox-cvs-static
280
281 -- Bastian Blank <waldi@debian.org> Mon, 09 Dec 2002 22:09:52 +0100
282
283busybox-cvs (0.60.99.cvs20021209-1) unstable; urgency=low
284
285 * New cvs version.
286 - fix udhcpc for use with ip.
287 - klogd supports -c.
288 * busybox-cvs-udeb
289 - set priority to extra. (closes: #172302)
290 - don't longer provide busybox-udeb.
291
292 -- Bastian Blank <waldi@debian.org> Mon, 09 Dec 2002 16:22:03 +0100
293
294busybox-cvs (0.60.99.cvs20021203-1) unstable; urgency=low
295
296 * New packages based on busybox cvs.
297 * changes for the udeb
298 - enable ip applet with address, link and route part.
299 - enable udhcpc.
300 - disable ifconfig and route applet.
301 - disable ls color.
302
303 -- Bastian Blank <waldi@debian.org> Tue, 03 Dec 2002 18:51:00 +0100
304
305busybox (1:0.60.0-1) unstable; urgency=low
306
307 * New version released. See changelog for details.
308
309 -- Erik Andersen <andersee@debian.org> Thu, 2 Aug 2001 12:12:37 -0600
310
311busybox (1:0.52-1.1) unstable; urgency=high
312
313 * Non-maintainer upload
314 * Fixed wget -P handling (closes: #106223).
315
316 -- Matt Kraai <kraai@debian.org> Wed, 25 Jul 2001 11:01:38 -0600
317
318busybox (1:0.52-1) unstable; urgency=high
319
320 * New version released. See changelog for details.
321
322 -- Erik Andersen <andersee@debian.org> Sat, 7 Jul 2001 01:23:45 -0600
323
324busybox (1:0.51-10) unstable; urgency=high
325
326 * Fix a compile problem with gcc 3.0 on hppa (closes: #102045)
327
328 -- Erik Andersen <andersee@debian.org> Sat, 23 Jun 2001 23:55:57 -0600
329
330busybox (1:0.51-9) unstable; urgency=high
331
332 * tar was creating leading directories with 0777 permissions as
333 as reult of faulty umask handling. This fixes it, repairing
334 a grave security problem in the woody the boot floppies.
335 (closes: #101169)
336
337 -- Erik Andersen <andersee@debian.org> Wed, 20 Jun 2001 16:17:38 -0600
338
339busybox (1:0.51-8) unstable; urgency=high
340
341 * Fix cp from /proc, where size=0 (closes: #100369)
342 * Add some padding to struct sysinfo for m68k.
343 * Apparently some bugs failed to be closed when master choked
344 (closes: #99627, #99637, #98571)
345 * Disable the busybox shell for the .deb, since it is not needed
346 for the boot floppies.
347
348 -- Erik Andersen <andersee@debian.org> Mon, 11 Jun 2001 13:26:07 -0600
349
350busybox (1:0.51-7) unstable; urgency=high
351
352 * Fix tar permission setting for existing directories (closes: #99627)
353 * Do not remove the .cvsignore files on 'make release' (closes: #99637)
354
355 -- Erik Andersen <andersee@debian.org> Mon, 4 Jun 2001 10:55:19 -0600
356
357busybox (1:0.51-6) testing unstable; urgency=high
358
359 * Update the version in testing so DHCP in the woody boot-floppies will work.
360 * Enable expr for the boot-floppies (closes: #98433)
361 * It builds on arm just fine now (closes: #97510)
362
363 -- Erik Andersen <andersee@debian.org> Wed, 23 May 2001 14:50:13 -0600
364
365busybox (1:0.51-5) unstable; urgency=low
366
367 * Backport a sed fix from 0.52pre
368 * Backport chroot fix from 0.52pre
369
370 -- Erik Andersen <andersee@debian.org> Wed, 16 May 2001 23:50:33 -0600
371
372busybox (1:0.51-4) unstable; urgency=low
373
374 * Backport from 0.52pre an endianness bugfix for md5sum
375 * Backport some updates to grep and sed
376 * Fix 'wget -O -' so it sets the quiet flag
377
378 -- Erik Andersen <andersee@debian.org> Mon, 14 May 2001 14:17:36 -0600
379
380busybox (1:0.51-3) unstable; urgency=low
381
382 * This is the "I am an idiot" release.
383 * Make cp and mv work again (closes: #97290)
384 * Fix the version number.
385
386 -- Erik Andersen <andersee@debian.org> Sat, 12 May 2001 17:35:58 -0600
387
388busybox (0.51-2) unstable; urgency=low
389
390 * Backport several release critical fixes into the 0.51 codebase
391 so the boot-floppies will work again.
392 * Fix a link ordering problem. (closes: #93362)
393 * Fixed gunzip (closes: #94331)
394 * Fixed cp permission setting (closes: #94580)
395
396 -- Erik Andersen <andersee@debian.org> Sat, 12 May 2001 11:22:35 -0600
397
398busybox (0.51-1) unstable; urgency=low
399
400 * Fixes several critical bugs (see the busybox changelog
401 for complete details)
402 * Force USE_SYSTEM_PWD_GRP=false, so busybox bypasses
403 the glibc NSS libraries. (closes: #93362)
404 * Fixed a bug in sed's address range handling (closes: #91758)
405 * Removed irrelevant cruft from the bottem of debian/changelog
406
407 -- Erik Andersen <andersee@debian.org> Tue, 10 Apr 2001 14:07:29 -0600
408
409busybox (0.50-2) unstable; urgency=low
410
411 * Enabled freeramdisk and pivot_root in the udeb (closes: #91336)
412 * Disabled lash (the busybox shell) in the udeb (closes: #91337)
413 * fixed a bug in syslog, a problem with rebooting when booted as
414 an initrd, and a few other minor problems.
415
416 -- Erik Andersen <andersee@debian.org> Sun, 25 Mar 2001 20:59:44 -0700
417
418
419busybox (0.50-2) unstable; urgency=low
420
421 * Enabled freeramdisk and pivot_root in the udeb (closes: #91336)
422 * Disabled lash (the busybox shell) in the udeb (closes: #91337)
423 * fixed a bug in syslog, a problem with rebooting when booted as
424 an initrd, and a few other minor problems.
425
426 -- Erik Andersen <andersee@debian.org> Sun, 25 Mar 2001 20:59:44 -0700
427
428busybox (0.50-1) unstable; urgency=low
429
430 * Tons on improvements all around -- See changelog for details.
431 * Fix malformed Build-Depends (closes: #86930)
432 * grep has worked for some time now (closes: #81084)
433 * init compiles with DEBUG_INIT enabled (closes: #85794)
434 * md5sum no longer displays filename when reading stdin (closes: #81283)
435 * lsmod, rmmod, and insmod are no longer enabled (closes: #85642)
436 * busybox and buxybox-static now conflict/replace each other (closes: #80421)
437
438 -- Erik Andersen <andersee@debian.org> Thu, 15 Mar 2001 14:45:00 -0700
439
440busybox (0.49-1) unstable; urgency=low
441
442 * Lots more source updates and bug fixes. See changelog for details.
443
444 -- Erik Andersen <andersee@debian.org> Sat, 27 Jan 2001 01:45:53 -0700
445
446busybox (0.48-1) unstable; urgency=low
447
448 * Lots more source updates and bug fixes. See changelog for details.
449 * Now includes .udeb support for the debian-installer. The .udeb
450 probably needs some more work, but this should be a good start.
451
452 -- Erik Andersen <andersee@debian.org> Wed, 13 Dec 2000 08:36:07 -0700
453
454busybox (0.47-1) unstable; urgency=low
455
456 * New version released. See changelog for details.
457
458 -- Erik Andersen <andersee@debian.org> Mon, 25 Sep 2000 23:00:56 -0600
459
460busybox (0.46-1) unstable; urgency=low
461
462 * New version released. See changelog for details.
463
464 -- Erik Andersen <andersee@debian.org> Tue, 11 Jul 2000 12:15:44 -0600
465
466busybox (0.45-1) unstable; urgency=low
467
468 * First attempt at packaging BusyBox as a .deb. This has been in
469 in the Debian boot-floppies CVS tree forever. Hopefully, having it as a
470 standalone app will make life easier for me, the debian-installer team, and
471 everyone else as well...
472 * I have created a busybox-static that can be used as a rescue shell when you
473 hose your system. Just invoke "busybox sh" to fir up the shell. This has
474 every app provided by busybox staically linked in. There have been several
475 times in the past that I would have loved to have this sitting on my system
476 (i.e. when libc gets screwed up.)
477
478 -- Erik Andersen <andersee@debian.org> Tue, 27 Jun 2000 12:26:41 -0600
479
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 @@
1#
2# Automatically generated make config: don't edit
3#
4HAVE_DOT_CONFIG=y
5
6#
7# General Configuration
8#
9# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
10CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y
11# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
12# CONFIG_FEATURE_VERBOSE_USAGE is not set
13CONFIG_FEATURE_INSTALLER=y
14# CONFIG_LOCALE_SUPPORT is not set
15# CONFIG_FEATURE_DEVFS is not set
16# CONFIG_FEATURE_DEVPTS is not set
17# CONFIG_FEATURE_CLEAN_UP is not set
18# CONFIG_FEATURE_SUID is not set
19# CONFIG_SELINUX is not set
20
21#
22# Build Options
23#
24# CONFIG_STATIC is not set
25CONFIG_LFS=y
26# USING_CROSS_COMPILER is not set
27EXTRA_CFLAGS_OPTIONS=""
28
29#
30# Installation Options
31#
32# CONFIG_INSTALL_NO_USR is not set
33PREFIX="./_install"
34
35#
36# Archival Utilities
37#
38CONFIG_AR=y
39# CONFIG_FEATURE_AR_LONG_FILENAMES is not set
40# CONFIG_BUNZIP2 is not set
41# CONFIG_CPIO is not set
42# CONFIG_DPKG is not set
43# CONFIG_DPKG_DEB is not set
44CONFIG_GUNZIP=y
45# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set
46CONFIG_GZIP=y
47# CONFIG_RPM2CPIO is not set
48# CONFIG_RPM is not set
49CONFIG_TAR=y
50CONFIG_FEATURE_TAR_CREATE=y
51# CONFIG_FEATURE_TAR_BZIP2 is not set
52# CONFIG_FEATURE_TAR_EXCLUDE is not set
53CONFIG_FEATURE_TAR_GZIP=y
54# CONFIG_FEATURE_TAR_COMPRESS is not set
55# CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY is not set
56CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
57# CONFIG_UNCOMPRESS is not set
58# CONFIG_UNZIP is not set
59
60#
61# Common options for cpio and tar
62#
63# CONFIG_FEATURE_UNARCHIVE_TAPE is not set
64
65#
66# Coreutils
67#
68# CONFIG_BASENAME is not set
69# CONFIG_CAL is not set
70CONFIG_CAT=y
71CONFIG_CHGRP=y
72CONFIG_CHMOD=y
73CONFIG_CHOWN=y
74CONFIG_CHROOT=y
75# CONFIG_CMP is not set
76CONFIG_CP=y
77CONFIG_CUT=y
78# CONFIG_DATE is not set
79# CONFIG_DD is not set
80CONFIG_DF=y
81CONFIG_DIRNAME=y
82# CONFIG_DOS2UNIX is not set
83# CONFIG_DU is not set
84CONFIG_ECHO=y
85CONFIG_FEATURE_FANCY_ECHO=y
86# CONFIG_ENV is not set
87CONFIG_EXPR=y
88CONFIG_FALSE=y
89# CONFIG_FOLD is not set
90CONFIG_HEAD=y
91# CONFIG_FEATURE_FANCY_HEAD is not set
92# CONFIG_HOSTID is not set
93CONFIG_ID=y
94# CONFIG_INSTALL is not set
95# CONFIG_LENGTH is not set
96CONFIG_LN=y
97# CONFIG_LOGNAME is not set
98CONFIG_LS=y
99CONFIG_FEATURE_LS_FILETYPES=y
100CONFIG_FEATURE_LS_FOLLOWLINKS=y
101CONFIG_FEATURE_LS_RECURSIVE=y
102CONFIG_FEATURE_LS_SORTFILES=y
103CONFIG_FEATURE_LS_TIMESTAMPS=y
104CONFIG_FEATURE_LS_USERNAME=y
105CONFIG_FEATURE_LS_COLOR=y
106CONFIG_MD5SUM=y
107CONFIG_MKDIR=y
108# CONFIG_MKFIFO is not set
109CONFIG_MKNOD=y
110CONFIG_MV=y
111# CONFIG_OD is not set
112# CONFIG_PRINTF is not set
113CONFIG_PWD=y
114# CONFIG_REALPATH is not set
115CONFIG_RM=y
116CONFIG_RMDIR=y
117# CONFIG_SHA1SUM is not set
118CONFIG_SLEEP=y
119# CONFIG_FEATURE_FANCY_SLEEP is not set
120CONFIG_SORT=y
121# CONFIG_STTY is not set
122CONFIG_SYNC=y
123CONFIG_TAIL=y
124CONFIG_FEATURE_FANCY_TAIL=y
125# CONFIG_TEE is not set
126CONFIG_TEST=y
127CONFIG_TOUCH=y
128CONFIG_TR=y
129CONFIG_TRUE=y
130CONFIG_TTY=y
131CONFIG_UNAME=y
132CONFIG_UNIQ=y
133# CONFIG_USLEEP is not set
134# CONFIG_UUDECODE is not set
135# CONFIG_UUENCODE is not set
136# CONFIG_WATCH is not set
137CONFIG_WC=y
138# CONFIG_WHO is not set
139CONFIG_WHOAMI=y
140# CONFIG_YES is not set
141
142#
143# Common options for cp and mv
144#
145# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set
146
147#
148# Common options for ls and more
149#
150CONFIG_FEATURE_AUTOWIDTH=y
151
152#
153# Common options for df, du, ls
154#
155CONFIG_FEATURE_HUMAN_READABLE=y
156
157#
158# Common options for md5sum, sha1sum
159#
160# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set
161
162#
163# Console Utilities
164#
165# CONFIG_CHVT is not set
166CONFIG_CLEAR=y
167# CONFIG_DEALLOCVT is not set
168# CONFIG_DUMPKMAP is not set
169# CONFIG_LOADACM is not set
170# CONFIG_LOADFONT is not set
171CONFIG_LOADKMAP=y
172# CONFIG_OPENVT is not set
173CONFIG_RESET=y
174# CONFIG_SETKEYCODES is not set
175
176#
177# Debian Utilities
178#
179# CONFIG_MKTEMP is not set
180# CONFIG_PIPE_PROGRESS is not set
181# CONFIG_READLINK is not set
182# CONFIG_RUN_PARTS is not set
183# CONFIG_START_STOP_DAEMON is not set
184CONFIG_WHICH=y
185
186#
187# Editors
188#
189# CONFIG_AWK is not set
190# CONFIG_PATCH is not set
191CONFIG_SED=y
192# CONFIG_VI is not set
193
194#
195# Finding Utilities
196#
197CONFIG_FIND=y
198CONFIG_FEATURE_FIND_MTIME=y
199CONFIG_FEATURE_FIND_PERM=y
200CONFIG_FEATURE_FIND_TYPE=y
201# CONFIG_FEATURE_FIND_XDEV is not set
202CONFIG_FEATURE_FIND_NEWER=y
203CONFIG_FEATURE_FIND_INUM=y
204CONFIG_GREP=y
205# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set
206CONFIG_FEATURE_GREP_FGREP_ALIAS=y
207# CONFIG_FEATURE_GREP_CONTEXT is not set
208# CONFIG_XARGS is not set
209
210#
211# Init Utilities
212#
213CONFIG_INIT=y
214CONFIG_FEATURE_USE_INITTAB=y
215CONFIG_FEATURE_INITRD=y
216# CONFIG_FEATURE_INIT_COREDUMPS is not set
217# CONFIG_FEATURE_EXTRA_QUIET is not set
218CONFIG_HALT=y
219CONFIG_POWEROFF=y
220CONFIG_REBOOT=y
221# CONFIG_MESG is not set
222
223#
224# Login/Password Management Utilities
225#
226# CONFIG_USE_BB_PWD_GRP is not set
227# CONFIG_ADDGROUP is not set
228# CONFIG_DELGROUP is not set
229# CONFIG_ADDUSER is not set
230# CONFIG_DELUSER is not set
231# CONFIG_GETTY is not set
232# CONFIG_LOGIN is not set
233# CONFIG_PASSWD is not set
234# CONFIG_SU is not set
235# CONFIG_SULOGIN is not set
236# CONFIG_VLOCK is not set
237
238#
239# Miscellaneous Utilities
240#
241# CONFIG_ADJTIMEX is not set
242# CONFIG_CROND is not set
243# CONFIG_CRONTAB is not set
244# CONFIG_DC is not set
245# CONFIG_DEVFSD is not set
246# CONFIG_LAST is not set
247# CONFIG_HDPARM is not set
248# CONFIG_MAKEDEVS is not set
249# CONFIG_MT is not set
250# CONFIG_STRINGS is not set
251# CONFIG_TIME is not set
252# CONFIG_WATCHDOG is not set
253
254#
255# Linux Module Utilities
256#
257# CONFIG_DEPMOD is not set
258# CONFIG_INSMOD is not set
259# CONFIG_LSMOD is not set
260# CONFIG_MODPROBE is not set
261# CONFIG_RMMOD is not set
262
263#
264# Networking Utilities
265#
266# CONFIG_FEATURE_IPV6 is not set
267# CONFIG_ARPING is not set
268# CONFIG_FTPGET is not set
269# CONFIG_FTPPUT is not set
270CONFIG_HOSTNAME=y
271# CONFIG_HTTPD is not set
272CONFIG_IFCONFIG=y
273CONFIG_FEATURE_IFCONFIG_STATUS=y
274# CONFIG_FEATURE_IFCONFIG_SLIP is not set
275# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
276CONFIG_FEATURE_IFCONFIG_HW=y
277# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
278# CONFIG_IFUPDOWN is not set
279# CONFIG_INETD is not set
280# CONFIG_IP is not set
281# CONFIG_IPCALC is not set
282# CONFIG_IPADDR is not set
283# CONFIG_IPLINK is not set
284# CONFIG_IPROUTE is not set
285# CONFIG_IPTUNNEL is not set
286# CONFIG_NAMEIF is not set
287# CONFIG_NC is not set
288# CONFIG_NETSTAT is not set
289# CONFIG_NSLOOKUP is not set
290CONFIG_PING=y
291CONFIG_FEATURE_FANCY_PING=y
292CONFIG_ROUTE=y
293# CONFIG_TELNET is not set
294# CONFIG_TELNETD is not set
295# CONFIG_TFTP is not set
296# CONFIG_TRACEROUTE is not set
297# CONFIG_VCONFIG is not set
298CONFIG_WGET=y
299CONFIG_FEATURE_WGET_STATUSBAR=y
300CONFIG_FEATURE_WGET_AUTHENTICATION=y
301CONFIG_FEATURE_WGET_IP6_LITERAL=y
302
303#
304# udhcp Server/Client
305#
306# CONFIG_UDHCPD is not set
307# CONFIG_UDHCPC is not set
308
309#
310# Process Utilities
311#
312CONFIG_FREE=y
313CONFIG_KILL=y
314CONFIG_KILLALL=y
315# CONFIG_PIDOF is not set
316CONFIG_PS=y
317# CONFIG_RENICE is not set
318# CONFIG_TOP is not set
319CONFIG_UPTIME=y
320
321#
322# Another Bourne-like Shell
323#
324# CONFIG_FEATURE_SH_IS_ASH is not set
325# CONFIG_FEATURE_SH_IS_HUSH is not set
326# CONFIG_FEATURE_SH_IS_LASH is not set
327# CONFIG_FEATURE_SH_IS_MSH is not set
328CONFIG_FEATURE_SH_IS_NONE=y
329# CONFIG_ASH is not set
330# CONFIG_HUSH is not set
331# CONFIG_LASH is not set
332# CONFIG_MSH is not set
333
334#
335# System Logging Utilities
336#
337CONFIG_SYSLOGD=y
338# CONFIG_FEATURE_ROTATE_LOGFILE is not set
339# CONFIG_FEATURE_REMOTE_LOG is not set
340# CONFIG_FEATURE_IPC_SYSLOG is not set
341CONFIG_KLOGD=y
342CONFIG_LOGGER=y
343
344#
345# Linux System Utilities
346#
347CONFIG_DMESG=y
348# CONFIG_FBSET is not set
349# CONFIG_FDFLUSH is not set
350# CONFIG_FDFORMAT is not set
351# CONFIG_FDISK is not set
352# CONFIG_FREERAMDISK is not set
353# CONFIG_FSCK_MINIX is not set
354# CONFIG_MKFS_MINIX is not set
355# CONFIG_GETOPT is not set
356# CONFIG_HEXDUMP is not set
357# CONFIG_HWCLOCK is not set
358CONFIG_LOSETUP=y
359CONFIG_MKSWAP=y
360CONFIG_MORE=y
361CONFIG_FEATURE_USE_TERMIOS=y
362# CONFIG_PIVOT_ROOT is not set
363# CONFIG_RDATE is not set
364CONFIG_SWAPONOFF=y
365CONFIG_MOUNT=y
366CONFIG_NFSMOUNT=y
367CONFIG_UMOUNT=y
368CONFIG_FEATURE_MOUNT_FORCE=y
369
370#
371# Common options for mount/umount
372#
373CONFIG_FEATURE_MOUNT_LOOP=y
374# CONFIG_FEATURE_MTAB_SUPPORT is not set
375
376#
377# Debugging Options
378#
379# 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 @@
1#
2# Automatically generated make config: don't edit
3#
4HAVE_DOT_CONFIG=y
5
6#
7# General Configuration
8#
9# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
10CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y
11# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
12# CONFIG_FEATURE_VERBOSE_USAGE is not set
13# CONFIG_FEATURE_INSTALLER is not set
14# CONFIG_LOCALE_SUPPORT is not set
15CONFIG_FEATURE_DEVFS=y
16CONFIG_FEATURE_DEVPTS=y
17# CONFIG_FEATURE_CLEAN_UP is not set
18# CONFIG_FEATURE_SUID is not set
19# CONFIG_SELINUX is not set
20
21#
22# Build Options
23#
24# CONFIG_STATIC is not set
25CONFIG_LFS=y
26# USING_CROSS_COMPILER is not set
27EXTRA_CFLAGS_OPTIONS=""
28
29#
30# Installation Options
31#
32# CONFIG_INSTALL_NO_USR is not set
33PREFIX="./_install"
34
35#
36# Archival Utilities
37#
38# CONFIG_AR is not set
39# CONFIG_BUNZIP2 is not set
40# CONFIG_CPIO is not set
41# CONFIG_DPKG is not set
42# CONFIG_DPKG_DEB is not set
43CONFIG_GUNZIP=y
44# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set
45# CONFIG_GZIP is not set
46# CONFIG_RPM2CPIO is not set
47# CONFIG_RPM is not set
48# CONFIG_TAR is not set
49# CONFIG_UNCOMPRESS is not set
50# CONFIG_UNZIP is not set
51
52#
53# Coreutils
54#
55# CONFIG_BASENAME is not set
56# CONFIG_CAL is not set
57CONFIG_CAT=y
58# CONFIG_CHGRP is not set
59# CONFIG_CHMOD is not set
60# CONFIG_CHOWN is not set
61CONFIG_CHROOT=y
62# CONFIG_CMP is not set
63CONFIG_CP=y
64# CONFIG_CUT is not set
65# CONFIG_DATE is not set
66# CONFIG_DD is not set
67# CONFIG_DF is not set
68# CONFIG_DIRNAME is not set
69# CONFIG_DOS2UNIX is not set
70# CONFIG_DU is not set
71CONFIG_ECHO=y
72CONFIG_FEATURE_FANCY_ECHO=y
73# CONFIG_ENV is not set
74# CONFIG_EXPR is not set
75# CONFIG_FALSE is not set
76# CONFIG_FOLD is not set
77# CONFIG_HEAD is not set
78# CONFIG_HOSTID is not set
79# CONFIG_ID is not set
80# CONFIG_INSTALL is not set
81# CONFIG_LENGTH is not set
82CONFIG_LN=y
83# CONFIG_LOGNAME is not set
84# CONFIG_LS is not set
85# CONFIG_MD5SUM is not set
86# CONFIG_MKDIR is not set
87# CONFIG_MKFIFO is not set
88# CONFIG_MKNOD is not set
89# CONFIG_MV is not set
90# CONFIG_OD is not set
91# CONFIG_PRINTF is not set
92# CONFIG_PWD is not set
93# CONFIG_REALPATH is not set
94CONFIG_RM=y
95# CONFIG_RMDIR is not set
96# CONFIG_SHA1SUM is not set
97CONFIG_SLEEP=y
98# CONFIG_FEATURE_FANCY_SLEEP is not set
99# CONFIG_SORT is not set
100# CONFIG_STTY is not set
101# CONFIG_SYNC is not set
102# CONFIG_TAIL is not set
103# CONFIG_TEE is not set
104CONFIG_TEST=y
105
106#
107# test (forced enabled for use with shell)
108#
109# CONFIG_TOUCH is not set
110# CONFIG_TR is not set
111# CONFIG_TRUE is not set
112# CONFIG_TTY is not set
113# CONFIG_UNAME is not set
114# CONFIG_UNIQ is not set
115# CONFIG_USLEEP is not set
116# CONFIG_UUDECODE is not set
117# CONFIG_UUENCODE is not set
118# CONFIG_WATCH is not set
119# CONFIG_WC is not set
120# CONFIG_WHO is not set
121# CONFIG_WHOAMI is not set
122# CONFIG_YES is not set
123
124#
125# Common options for cp and mv
126#
127CONFIG_FEATURE_PRESERVE_HARDLINKS=y
128
129#
130# Console Utilities
131#
132# CONFIG_CHVT is not set
133# CONFIG_CLEAR is not set
134# CONFIG_DEALLOCVT is not set
135# CONFIG_DUMPKMAP is not set
136# CONFIG_LOADACM is not set
137# CONFIG_LOADFONT is not set
138# CONFIG_LOADKMAP is not set
139# CONFIG_OPENVT is not set
140# CONFIG_RESET is not set
141# CONFIG_SETKEYCODES is not set
142
143#
144# Debian Utilities
145#
146# CONFIG_MKTEMP is not set
147# CONFIG_PIPE_PROGRESS is not set
148# CONFIG_READLINK is not set
149# CONFIG_RUN_PARTS is not set
150# CONFIG_START_STOP_DAEMON is not set
151# CONFIG_WHICH is not set
152
153#
154# Editors
155#
156# CONFIG_AWK is not set
157# CONFIG_PATCH is not set
158# CONFIG_SED is not set
159# CONFIG_VI is not set
160
161#
162# Finding Utilities
163#
164CONFIG_FIND=y
165# CONFIG_FEATURE_FIND_MTIME is not set
166# CONFIG_FEATURE_FIND_PERM is not set
167# CONFIG_FEATURE_FIND_TYPE is not set
168# CONFIG_FEATURE_FIND_XDEV is not set
169# CONFIG_FEATURE_FIND_NEWER is not set
170# CONFIG_FEATURE_FIND_INUM is not set
171CONFIG_GREP=y
172# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set
173# CONFIG_FEATURE_GREP_FGREP_ALIAS is not set
174# CONFIG_FEATURE_GREP_CONTEXT is not set
175# CONFIG_XARGS is not set
176
177#
178# Init Utilities
179#
180# CONFIG_INIT is not set
181# CONFIG_HALT is not set
182# CONFIG_POWEROFF is not set
183# CONFIG_REBOOT is not set
184# CONFIG_MINIT is not set
185# CONFIG_MESG is not set
186
187#
188# Login/Password Management Utilities
189#
190CONFIG_USE_BB_PWD_GRP=y
191# CONFIG_ADDGROUP is not set
192# CONFIG_DELGROUP is not set
193# CONFIG_ADDUSER is not set
194# CONFIG_DELUSER is not set
195# CONFIG_GETTY is not set
196# CONFIG_LOGIN is not set
197# CONFIG_PASSWD is not set
198# CONFIG_SU is not set
199# CONFIG_SULOGIN is not set
200# CONFIG_VLOCK is not set
201
202#
203# Miscellaneous Utilities
204#
205# CONFIG_ADJTIMEX is not set
206# CONFIG_CROND is not set
207# CONFIG_CRONTAB is not set
208# CONFIG_DC is not set
209# CONFIG_DEVFSD is not set
210# CONFIG_LAST is not set
211# CONFIG_HDPARM is not set
212# CONFIG_MAKEDEVS is not set
213# CONFIG_MT is not set
214# CONFIG_STRINGS is not set
215# CONFIG_TIME is not set
216# CONFIG_WATCHDOG is not set
217
218#
219# Linux Module Utilities
220#
221CONFIG_MODUTILS_OBJ=y
222# CONFIG_DEPMOD is not set
223CONFIG_INSMOD=y
224# CONFIG_FEATURE_2_2_MODULES is not set
225CONFIG_FEATURE_2_4_MODULES=y
226# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
227# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
228# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
229# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
230# CONFIG_LSMOD is not set
231CONFIG_MODPROBE=y
232# CONFIG_RMMOD is not set
233CONFIG_FEATURE_CHECK_TAINTED_MODULE=y
234
235#
236# Networking Utilities
237#
238# CONFIG_FEATURE_IPV6 is not set
239# CONFIG_ARPING is not set
240# CONFIG_FTPGET is not set
241# CONFIG_FTPPUT is not set
242# CONFIG_HOSTNAME is not set
243# CONFIG_HTTPD is not set
244# CONFIG_IFCONFIG is not set
245# CONFIG_IFUPDOWN is not set
246# CONFIG_INETD is not set
247# CONFIG_IP is not set
248# CONFIG_IPCALC is not set
249# CONFIG_IPADDR is not set
250# CONFIG_IPLINK is not set
251# CONFIG_IPROUTE is not set
252# CONFIG_IPTUNNEL is not set
253# CONFIG_NAMEIF is not set
254# CONFIG_NC is not set
255# CONFIG_NETSTAT is not set
256# CONFIG_NSLOOKUP is not set
257# CONFIG_PING is not set
258# CONFIG_ROUTE is not set
259# CONFIG_TELNET is not set
260# CONFIG_TELNETD is not set
261# CONFIG_TFTP is not set
262# CONFIG_TRACEROUTE is not set
263# CONFIG_VCONFIG is not set
264# CONFIG_WGET is not set
265
266#
267# udhcp Server/Client
268#
269# CONFIG_UDHCPD is not set
270# CONFIG_UDHCPC is not set
271
272#
273# Process Utilities
274#
275# CONFIG_FREE is not set
276# CONFIG_KILL is not set
277# CONFIG_PIDOF is not set
278# CONFIG_PS is not set
279# CONFIG_RENICE is not set
280# CONFIG_TOP is not set
281# CONFIG_UPTIME is not set
282
283#
284# Another Bourne-like Shell
285#
286CONFIG_FEATURE_SH_IS_ASH=y
287# CONFIG_FEATURE_SH_IS_HUSH is not set
288# CONFIG_FEATURE_SH_IS_LASH is not set
289# CONFIG_FEATURE_SH_IS_MSH is not set
290# CONFIG_FEATURE_SH_IS_NONE is not set
291CONFIG_ASH=y
292
293#
294# Ash Shell Options
295#
296# CONFIG_ASH_JOB_CONTROL is not set
297# CONFIG_ASH_ALIAS is not set
298# CONFIG_ASH_MATH_SUPPORT is not set
299# CONFIG_ASH_GETOPTS is not set
300# CONFIG_ASH_CMDCMD is not set
301# CONFIG_ASH_MAIL is not set
302CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
303# CONFIG_HUSH is not set
304# CONFIG_LASH is not set
305# CONFIG_MSH is not set
306
307#
308# Bourne Shell Options
309#
310# CONFIG_FEATURE_COMMAND_EDITING is not set
311# CONFIG_FEATURE_COMMAND_SAVEHISTORY is not set
312# CONFIG_FEATURE_COMMAND_TAB_COMPLETION is not set
313# CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION is not set
314CONFIG_FEATURE_COMMAND_HISTORY=15
315# CONFIG_FEATURE_SH_STANDALONE_SHELL is not set
316# CONFIG_FEATURE_SH_FANCY_PROMPT is not set
317# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
318
319#
320# System Logging Utilities
321#
322# CONFIG_SYSLOGD is not set
323# CONFIG_LOGGER is not set
324
325#
326# Linux System Utilities
327#
328# CONFIG_DMESG is not set
329# CONFIG_FBSET is not set
330# CONFIG_FDFLUSH is not set
331# CONFIG_FDFORMAT is not set
332# CONFIG_FDISK is not set
333# CONFIG_FREERAMDISK is not set
334# CONFIG_FSCK_MINIX is not set
335# CONFIG_MKFS_MINIX is not set
336# CONFIG_GETOPT is not set
337# CONFIG_HEXDUMP is not set
338# CONFIG_HWCLOCK is not set
339# CONFIG_LOSETUP is not set
340# CONFIG_MKSWAP is not set
341# CONFIG_MORE is not set
342CONFIG_PIVOT_ROOT=y
343# CONFIG_RDATE is not set
344# CONFIG_SWAPONOFF is not set
345CONFIG_MOUNT=y
346# CONFIG_NFSMOUNT is not set
347CONFIG_UMOUNT=y
348CONFIG_FEATURE_MOUNT_FORCE=y
349
350#
351# Common options for mount/umount
352#
353CONFIG_FEATURE_MOUNT_LOOP=y
354# CONFIG_FEATURE_MTAB_SUPPORT is not set
355
356#
357# Debugging Options
358#
359# 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 @@
1#
2# Automatically generated make config: don't edit
3#
4HAVE_DOT_CONFIG=y
5
6#
7# General Configuration
8#
9# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
10CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y
11# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
12CONFIG_FEATURE_VERBOSE_USAGE=y
13# CONFIG_FEATURE_INSTALLER is not set
14# CONFIG_LOCALE_SUPPORT is not set
15CONFIG_FEATURE_DEVFS=y
16CONFIG_FEATURE_DEVPTS=y
17# CONFIG_FEATURE_CLEAN_UP is not set
18# CONFIG_FEATURE_SUID is not set
19# CONFIG_SELINUX is not set
20
21#
22# Build Options
23#
24CONFIG_STATIC=y
25CONFIG_LFS=y
26# USING_CROSS_COMPILER is not set
27EXTRA_CFLAGS_OPTIONS=""
28
29#
30# Installation Options
31#
32# CONFIG_INSTALL_NO_USR is not set
33PREFIX="./_install"
34
35#
36# Archival Utilities
37#
38CONFIG_AR=y
39CONFIG_FEATURE_AR_LONG_FILENAMES=y
40CONFIG_BUNZIP2=y
41CONFIG_CPIO=y
42CONFIG_DPKG=y
43CONFIG_DPKG_DEB=y
44# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
45CONFIG_FEATURE_DEB_TAR_GZ=y
46CONFIG_FEATURE_DEB_TAR_BZ2=y
47CONFIG_GUNZIP=y
48CONFIG_FEATURE_GUNZIP_UNCOMPRESS=y
49CONFIG_GZIP=y
50CONFIG_RPM2CPIO=y
51CONFIG_RPM=y
52CONFIG_TAR=y
53CONFIG_FEATURE_TAR_CREATE=y
54CONFIG_FEATURE_TAR_BZIP2=y
55CONFIG_FEATURE_TAR_EXCLUDE=y
56CONFIG_FEATURE_TAR_GZIP=y
57# CONFIG_FEATURE_TAR_COMPRESS is not set
58CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY=y
59CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
60CONFIG_UNCOMPRESS=y
61CONFIG_UNZIP=y
62
63#
64# Common options for cpio and tar
65#
66CONFIG_FEATURE_UNARCHIVE_TAPE=y
67
68#
69# Coreutils
70#
71CONFIG_BASENAME=y
72CONFIG_CAL=y
73CONFIG_CAT=y
74CONFIG_CHGRP=y
75CONFIG_CHMOD=y
76CONFIG_CHOWN=y
77CONFIG_CHROOT=y
78CONFIG_CMP=y
79CONFIG_CP=y
80CONFIG_CUT=y
81CONFIG_DATE=y
82
83#
84# date (forced enabled for use with watch)
85#
86CONFIG_FEATURE_DATE_ISOFMT=y
87CONFIG_DD=y
88CONFIG_DF=y
89CONFIG_DIRNAME=y
90CONFIG_DOS2UNIX=y
91CONFIG_UNIX2DOS=y
92CONFIG_DU=y
93CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K=y
94CONFIG_ECHO=y
95CONFIG_FEATURE_FANCY_ECHO=y
96CONFIG_ENV=y
97CONFIG_EXPR=y
98CONFIG_FALSE=y
99CONFIG_FOLD=y
100CONFIG_HEAD=y
101CONFIG_FEATURE_FANCY_HEAD=y
102CONFIG_HOSTID=y
103CONFIG_ID=y
104# CONFIG_INSTALL is not set
105CONFIG_LENGTH=y
106CONFIG_LN=y
107CONFIG_LOGNAME=y
108CONFIG_LS=y
109CONFIG_FEATURE_LS_FILETYPES=y
110CONFIG_FEATURE_LS_FOLLOWLINKS=y
111CONFIG_FEATURE_LS_RECURSIVE=y
112CONFIG_FEATURE_LS_SORTFILES=y
113CONFIG_FEATURE_LS_TIMESTAMPS=y
114CONFIG_FEATURE_LS_USERNAME=y
115CONFIG_FEATURE_LS_COLOR=y
116CONFIG_MD5SUM=y
117CONFIG_MKDIR=y
118CONFIG_MKFIFO=y
119CONFIG_MKNOD=y
120CONFIG_MV=y
121CONFIG_OD=y
122CONFIG_PRINTF=y
123CONFIG_PWD=y
124CONFIG_REALPATH=y
125CONFIG_RM=y
126CONFIG_RMDIR=y
127CONFIG_SHA1SUM=y
128CONFIG_SLEEP=y
129CONFIG_FEATURE_FANCY_SLEEP=y
130CONFIG_SORT=y
131CONFIG_STTY=y
132CONFIG_SYNC=y
133CONFIG_TAIL=y
134CONFIG_FEATURE_FANCY_TAIL=y
135CONFIG_TEE=y
136CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
137CONFIG_TEST=y
138
139#
140# test (forced enabled for use with shell)
141#
142CONFIG_TOUCH=y
143CONFIG_TR=y
144CONFIG_TRUE=y
145CONFIG_TTY=y
146CONFIG_UNAME=y
147CONFIG_UNIQ=y
148CONFIG_USLEEP=y
149CONFIG_UUDECODE=y
150CONFIG_UUENCODE=y
151CONFIG_WATCH=y
152CONFIG_WC=y
153CONFIG_WHO=y
154CONFIG_WHOAMI=y
155CONFIG_YES=y
156
157#
158# Common options for cp and mv
159#
160CONFIG_FEATURE_PRESERVE_HARDLINKS=y
161
162#
163# Common options for ls and more
164#
165CONFIG_FEATURE_AUTOWIDTH=y
166
167#
168# Common options for df, du, ls
169#
170CONFIG_FEATURE_HUMAN_READABLE=y
171
172#
173# Common options for md5sum, sha1sum
174#
175# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set
176
177#
178# Console Utilities
179#
180CONFIG_CHVT=y
181CONFIG_CLEAR=y
182CONFIG_DEALLOCVT=y
183CONFIG_DUMPKMAP=y
184CONFIG_LOADACM=y
185CONFIG_LOADFONT=y
186CONFIG_LOADKMAP=y
187CONFIG_OPENVT=y
188CONFIG_RESET=y
189CONFIG_SETKEYCODES=y
190
191#
192# Debian Utilities
193#
194CONFIG_MKTEMP=y
195# CONFIG_PIPE_PROGRESS is not set
196CONFIG_READLINK=y
197CONFIG_RUN_PARTS=y
198CONFIG_START_STOP_DAEMON=y
199CONFIG_WHICH=y
200
201#
202# Editors
203#
204CONFIG_AWK=y
205CONFIG_FEATURE_AWK_MATH=y
206CONFIG_PATCH=y
207CONFIG_SED=y
208CONFIG_VI=y
209CONFIG_FEATURE_VI_COLON=y
210CONFIG_FEATURE_VI_YANKMARK=y
211CONFIG_FEATURE_VI_SEARCH=y
212CONFIG_FEATURE_VI_USE_SIGNALS=y
213CONFIG_FEATURE_VI_DOT_CMD=y
214CONFIG_FEATURE_VI_READONLY=y
215CONFIG_FEATURE_VI_SETOPTS=y
216CONFIG_FEATURE_VI_SET=y
217CONFIG_FEATURE_VI_WIN_RESIZE=y
218CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y
219
220#
221# Finding Utilities
222#
223CONFIG_FIND=y
224CONFIG_FEATURE_FIND_MTIME=y
225CONFIG_FEATURE_FIND_PERM=y
226CONFIG_FEATURE_FIND_TYPE=y
227CONFIG_FEATURE_FIND_XDEV=y
228CONFIG_FEATURE_FIND_NEWER=y
229CONFIG_FEATURE_FIND_INUM=y
230CONFIG_GREP=y
231CONFIG_FEATURE_GREP_EGREP_ALIAS=y
232CONFIG_FEATURE_GREP_FGREP_ALIAS=y
233CONFIG_FEATURE_GREP_CONTEXT=y
234CONFIG_XARGS=y
235# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set
236# CONFIG_FEATURE_XARGS_SUPPORT_QUOTES is not set
237# CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT is not set
238# CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM is not set
239
240#
241# Init Utilities
242#
243CONFIG_INIT=y
244CONFIG_FEATURE_USE_INITTAB=y
245CONFIG_FEATURE_INITRD=y
246# CONFIG_FEATURE_INIT_COREDUMPS is not set
247# CONFIG_FEATURE_EXTRA_QUIET is not set
248CONFIG_HALT=y
249CONFIG_POWEROFF=y
250CONFIG_REBOOT=y
251CONFIG_MESG=y
252
253#
254# Login/Password Management Utilities
255#
256CONFIG_USE_BB_PWD_GRP=y
257CONFIG_ADDGROUP=y
258CONFIG_DELGROUP=y
259CONFIG_ADDUSER=y
260CONFIG_DELUSER=y
261CONFIG_GETTY=y
262CONFIG_LOGIN=y
263CONFIG_FEATURE_SECURETTY=y
264CONFIG_PASSWD=y
265CONFIG_SU=y
266CONFIG_SULOGIN=y
267CONFIG_VLOCK=y
268
269#
270# Common options for adduser, deluser, login, su
271#
272CONFIG_FEATURE_SHADOWPASSWDS=y
273CONFIG_USE_BB_SHADOW=y
274
275#
276# Miscellaneous Utilities
277#
278CONFIG_ADJTIMEX=y
279CONFIG_CROND=y
280CONFIG_FEATURE_CROND_CALL_SENDMAIL=y
281CONFIG_CRONTAB=y
282CONFIG_DC=y
283# CONFIG_DEVFSD is not set
284CONFIG_LAST=y
285# CONFIG_HDPARM is not set
286CONFIG_MAKEDEVS=y
287CONFIG_MT=y
288CONFIG_STRINGS=y
289CONFIG_TIME=y
290CONFIG_WATCHDOG=y
291
292#
293# Linux Module Utilities
294#
295# CONFIG_DEPMOD is not set
296# CONFIG_INSMOD is not set
297# CONFIG_LSMOD is not set
298# CONFIG_MODPROBE is not set
299# CONFIG_RMMOD is not set
300
301#
302# Networking Utilities
303#
304CONFIG_FEATURE_IPV6=y
305CONFIG_ARPING=y
306CONFIG_FTPGET=y
307CONFIG_FTPPUT=y
308CONFIG_HOSTNAME=y
309CONFIG_HTTPD=y
310# CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY is not set
311CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
312# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
313# CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP is not set
314# CONFIG_FEATURE_HTTPD_SETUID is not set
315# CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES is not set
316# CONFIG_FEATURE_HTTPD_CGI is not set
317# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set
318CONFIG_IFCONFIG=y
319CONFIG_FEATURE_IFCONFIG_STATUS=y
320CONFIG_FEATURE_IFCONFIG_SLIP=y
321CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ=y
322CONFIG_FEATURE_IFCONFIG_HW=y
323CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS=y
324CONFIG_IFUPDOWN=y
325# CONFIG_FEATURE_IFUPDOWN_IP is not set
326CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y
327CONFIG_FEATURE_IFUPDOWN_IPV4=y
328CONFIG_FEATURE_IFUPDOWN_IPV6=y
329CONFIG_FEATURE_IFUPDOWN_IPX=y
330CONFIG_FEATURE_IFUPDOWN_MAPPING=y
331# CONFIG_INETD is not set
332CONFIG_IP=y
333CONFIG_FEATURE_IP_ADDRESS=y
334
335#
336# address (forced enabled for ipaddr)
337#
338CONFIG_FEATURE_IP_LINK=y
339
340#
341# link (forced enabled for iplink)
342#
343CONFIG_FEATURE_IP_ROUTE=y
344
345#
346# route (forced enabled for iproute)
347#
348CONFIG_FEATURE_IP_TUNNEL=y
349
350#
351# tunnel (forced enabled for iptunnel)
352#
353CONFIG_IPCALC=y
354CONFIG_FEATURE_IPCALC_FANCY=y
355CONFIG_IPADDR=y
356CONFIG_IPLINK=y
357CONFIG_IPROUTE=y
358CONFIG_IPTUNNEL=y
359CONFIG_NAMEIF=y
360CONFIG_NC=y
361CONFIG_NETSTAT=y
362CONFIG_NSLOOKUP=y
363CONFIG_PING=y
364CONFIG_FEATURE_FANCY_PING=y
365CONFIG_PING6=y
366CONFIG_FEATURE_FANCY_PING6=y
367CONFIG_ROUTE=y
368CONFIG_TELNET=y
369CONFIG_FEATURE_TELNET_TTYPE=y
370CONFIG_TELNETD=y
371# CONFIG_FEATURE_TELNETD_INETD is not set
372CONFIG_TFTP=y
373CONFIG_FEATURE_TFTP_GET=y
374CONFIG_FEATURE_TFTP_PUT=y
375CONFIG_FEATURE_TFTP_BLOCKSIZE=y
376CONFIG_FEATURE_TFTP_DEBUG=y
377CONFIG_TRACEROUTE=y
378CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
379# CONFIG_VCONFIG is not set
380CONFIG_WGET=y
381CONFIG_FEATURE_WGET_STATUSBAR=y
382CONFIG_FEATURE_WGET_AUTHENTICATION=y
383CONFIG_FEATURE_WGET_IP6_LITERAL=y
384
385#
386# udhcp Server/Client
387#
388CONFIG_UDHCPD=y
389CONFIG_UDHCPC=y
390CONFIG_DUMPLEASES=y
391CONFIG_FEATURE_UDHCP_SYSLOG=y
392CONFIG_FEATURE_UDHCP_DEBUG=y
393
394#
395# Process Utilities
396#
397CONFIG_FREE=y
398CONFIG_KILL=y
399CONFIG_KILLALL=y
400CONFIG_PIDOF=y
401CONFIG_PS=y
402CONFIG_RENICE=y
403CONFIG_TOP=y
404FEATURE_CPU_USAGE_PERCENTAGE=y
405CONFIG_UPTIME=y
406
407#
408# Another Bourne-like Shell
409#
410CONFIG_FEATURE_SH_IS_ASH=y
411# CONFIG_FEATURE_SH_IS_HUSH is not set
412# CONFIG_FEATURE_SH_IS_LASH is not set
413# CONFIG_FEATURE_SH_IS_MSH is not set
414# CONFIG_FEATURE_SH_IS_NONE is not set
415CONFIG_ASH=y
416
417#
418# Ash Shell Options
419#
420CONFIG_ASH_JOB_CONTROL=y
421CONFIG_ASH_ALIAS=y
422CONFIG_ASH_MATH_SUPPORT=y
423CONFIG_ASH_GETOPTS=y
424CONFIG_ASH_CMDCMD=y
425CONFIG_ASH_MAIL=y
426CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
427# CONFIG_HUSH is not set
428# CONFIG_LASH is not set
429# CONFIG_MSH is not set
430
431#
432# Bourne Shell Options
433#
434CONFIG_FEATURE_COMMAND_EDITING=y
435CONFIG_FEATURE_COMMAND_SAVEHISTORY=y
436CONFIG_FEATURE_COMMAND_TAB_COMPLETION=y
437CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION=y
438CONFIG_FEATURE_COMMAND_HISTORY=15
439CONFIG_FEATURE_SH_STANDALONE_SHELL=y
440CONFIG_FEATURE_SH_FANCY_PROMPT=y
441# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
442
443#
444# System Logging Utilities
445#
446CONFIG_SYSLOGD=y
447# CONFIG_FEATURE_ROTATE_LOGFILE is not set
448CONFIG_FEATURE_REMOTE_LOG=y
449CONFIG_FEATURE_IPC_SYSLOG=y
450CONFIG_LOGREAD=y
451CONFIG_KLOGD=y
452CONFIG_LOGGER=y
453
454#
455# Linux System Utilities
456#
457CONFIG_DMESG=y
458CONFIG_FBSET=y
459CONFIG_FEATURE_FBSET_FANCY=y
460CONFIG_FEATURE_FBSET_READMODE=y
461CONFIG_FDFLUSH=y
462# CONFIG_FDFORMAT is not set
463CONFIG_FDISK=y
464CONFIG_FEATURE_FDISK_WRITABLE=y
465CONFIG_FEATURE_AIX_LABEL=y
466CONFIG_FEATURE_SGI_LABEL=y
467CONFIG_FEATURE_SUN_LABEL=y
468# CONFIG_FEATURE_OSF_LABEL is not set
469CONFIG_FEATURE_FDISK_ADVANCED=y
470CONFIG_FREERAMDISK=y
471CONFIG_FSCK_MINIX=y
472CONFIG_MKFS_MINIX=y
473
474#
475# Minix filesystem support
476#
477CONFIG_FEATURE_MINIX2=y
478CONFIG_GETOPT=y
479CONFIG_HEXDUMP=y
480CONFIG_HWCLOCK=y
481CONFIG_FEATURE_HWCLOCK_LONGOPTIONS=y
482CONFIG_LOSETUP=y
483CONFIG_MKSWAP=y
484CONFIG_MORE=y
485CONFIG_FEATURE_USE_TERMIOS=y
486CONFIG_PIVOT_ROOT=y
487CONFIG_RDATE=y
488CONFIG_SWAPONOFF=y
489CONFIG_MOUNT=y
490CONFIG_NFSMOUNT=y
491CONFIG_UMOUNT=y
492CONFIG_FEATURE_MOUNT_FORCE=y
493
494#
495# Common options for mount/umount
496#
497CONFIG_FEATURE_MOUNT_LOOP=y
498# CONFIG_FEATURE_MTAB_SUPPORT is not set
499
500#
501# Debugging Options
502#
503# 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 @@
1#
2# Automatically generated make config: don't edit
3#
4HAVE_DOT_CONFIG=y
5
6#
7# General Configuration
8#
9# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
10CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y
11# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
12# CONFIG_FEATURE_VERBOSE_USAGE is not set
13# CONFIG_FEATURE_INSTALLER is not set
14# CONFIG_LOCALE_SUPPORT is not set
15CONFIG_FEATURE_DEVFS=y
16CONFIG_FEATURE_DEVPTS=y
17# CONFIG_FEATURE_CLEAN_UP is not set
18# CONFIG_FEATURE_SUID is not set
19# CONFIG_SELINUX is not set
20
21#
22# Build Options
23#
24# CONFIG_STATIC is not set
25CONFIG_LFS=y
26# USING_CROSS_COMPILER is not set
27EXTRA_CFLAGS_OPTIONS=""
28
29#
30# Installation Options
31#
32# CONFIG_INSTALL_NO_USR is not set
33PREFIX="./_install"
34
35#
36# Archival Utilities
37#
38CONFIG_AR=y
39# CONFIG_FEATURE_AR_LONG_FILENAMES is not set
40# CONFIG_BUNZIP2 is not set
41# CONFIG_CPIO is not set
42# CONFIG_DPKG is not set
43# CONFIG_DPKG_DEB is not set
44CONFIG_GUNZIP=y
45# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set
46# CONFIG_GZIP is not set
47# CONFIG_RPM2CPIO is not set
48# CONFIG_RPM is not set
49CONFIG_TAR=y
50# CONFIG_FEATURE_TAR_CREATE is not set
51# CONFIG_FEATURE_TAR_BZIP2 is not set
52# CONFIG_FEATURE_TAR_EXCLUDE is not set
53CONFIG_FEATURE_TAR_GZIP=y
54# CONFIG_FEATURE_TAR_COMPRESS is not set
55CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY=y
56CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
57# CONFIG_UNCOMPRESS is not set
58# CONFIG_UNZIP is not set
59
60#
61# Common options for cpio and tar
62#
63# CONFIG_FEATURE_UNARCHIVE_TAPE is not set
64
65#
66# Coreutils
67#
68CONFIG_BASENAME=y
69# CONFIG_CAL is not set
70CONFIG_CAT=y
71# CONFIG_CHGRP is not set
72CONFIG_CHMOD=y
73CONFIG_CHOWN=y
74CONFIG_CHROOT=y
75# CONFIG_CMP is not set
76CONFIG_CP=y
77CONFIG_CUT=y
78# CONFIG_DATE is not set
79# CONFIG_DD is not set
80CONFIG_DF=y
81CONFIG_DIRNAME=y
82# CONFIG_DOS2UNIX is not set
83# CONFIG_DU is not set
84CONFIG_ECHO=y
85CONFIG_FEATURE_FANCY_ECHO=y
86CONFIG_ENV=y
87CONFIG_EXPR=y
88CONFIG_FALSE=y
89# CONFIG_FOLD is not set
90CONFIG_HEAD=y
91# CONFIG_FEATURE_FANCY_HEAD is not set
92# CONFIG_HOSTID is not set
93CONFIG_ID=y
94# CONFIG_INSTALL is not set
95# CONFIG_LENGTH is not set
96CONFIG_LN=y
97# CONFIG_LOGNAME is not set
98CONFIG_LS=y
99# CONFIG_FEATURE_LS_FILETYPES is not set
100CONFIG_FEATURE_LS_FOLLOWLINKS=y
101# CONFIG_FEATURE_LS_RECURSIVE is not set
102CONFIG_FEATURE_LS_SORTFILES=y
103CONFIG_FEATURE_LS_TIMESTAMPS=y
104CONFIG_FEATURE_LS_USERNAME=y
105# CONFIG_FEATURE_LS_COLOR is not set
106CONFIG_MD5SUM=y
107CONFIG_MKDIR=y
108# CONFIG_MKFIFO is not set
109CONFIG_MKNOD=y
110CONFIG_MV=y
111# CONFIG_OD is not set
112CONFIG_PRINTF=y
113CONFIG_PWD=y
114CONFIG_REALPATH=y
115CONFIG_RM=y
116CONFIG_RMDIR=y
117# CONFIG_SHA1SUM is not set
118# CONFIG_SLEEP is not set
119CONFIG_SORT=y
120# CONFIG_STTY is not set
121CONFIG_SYNC=y
122CONFIG_TAIL=y
123# CONFIG_FEATURE_FANCY_TAIL is not set
124# CONFIG_TEE is not set
125CONFIG_TEST=y
126
127#
128# test (forced enabled for use with shell)
129#
130CONFIG_TOUCH=y
131CONFIG_TR=y
132CONFIG_TRUE=y
133# CONFIG_TTY is not set
134CONFIG_UNAME=y
135CONFIG_UNIQ=y
136# CONFIG_USLEEP is not set
137# CONFIG_UUDECODE is not set
138# CONFIG_UUENCODE is not set
139# CONFIG_WATCH is not set
140CONFIG_WC=y
141# CONFIG_WHO is not set
142# CONFIG_WHOAMI is not set
143# CONFIG_YES is not set
144
145#
146# Common options for cp and mv
147#
148CONFIG_FEATURE_PRESERVE_HARDLINKS=y
149
150#
151# Common options for ls and more
152#
153CONFIG_FEATURE_AUTOWIDTH=y
154
155#
156# Common options for df, du, ls
157#
158CONFIG_FEATURE_HUMAN_READABLE=y
159
160#
161# Common options for md5sum, sha1sum
162#
163# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set
164
165#
166# Console Utilities
167#
168# CONFIG_CHVT is not set
169# CONFIG_CLEAR is not set
170# CONFIG_DEALLOCVT is not set
171# CONFIG_DUMPKMAP is not set
172# CONFIG_LOADACM is not set
173# CONFIG_LOADFONT is not set
174# CONFIG_LOADKMAP is not set
175# CONFIG_OPENVT is not set
176# CONFIG_RESET is not set
177# CONFIG_SETKEYCODES is not set
178
179#
180# Debian Utilities
181#
182# CONFIG_MKTEMP is not set
183# CONFIG_PIPE_PROGRESS is not set
184CONFIG_READLINK=y
185# CONFIG_RUN_PARTS is not set
186# CONFIG_START_STOP_DAEMON is not set
187# CONFIG_WHICH is not set
188
189#
190# Editors
191#
192# CONFIG_AWK is not set
193# CONFIG_PATCH is not set
194CONFIG_SED=y
195# CONFIG_VI is not set
196
197#
198# Finding Utilities
199#
200CONFIG_FIND=y
201CONFIG_FEATURE_FIND_MTIME=y
202CONFIG_FEATURE_FIND_PERM=y
203CONFIG_FEATURE_FIND_TYPE=y
204CONFIG_FEATURE_FIND_XDEV=y
205CONFIG_FEATURE_FIND_NEWER=y
206CONFIG_FEATURE_FIND_INUM=y
207CONFIG_GREP=y
208# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set
209# CONFIG_FEATURE_GREP_FGREP_ALIAS is not set
210# CONFIG_FEATURE_GREP_CONTEXT is not set
211# CONFIG_XARGS is not set
212
213#
214# Init Utilities
215#
216CONFIG_INIT=y
217CONFIG_FEATURE_USE_INITTAB=y
218# CONFIG_FEATURE_INITRD is not set
219# CONFIG_FEATURE_INIT_COREDUMPS is not set
220CONFIG_FEATURE_EXTRA_QUIET=y
221CONFIG_HALT=y
222# CONFIG_POWEROFF is not set
223CONFIG_REBOOT=y
224# CONFIG_MESG is not set
225
226#
227# Login/Password Management Utilities
228#
229CONFIG_USE_BB_PWD_GRP=y
230# CONFIG_ADDGROUP is not set
231# CONFIG_DELGROUP is not set
232# CONFIG_ADDUSER is not set
233# CONFIG_DELUSER is not set
234# CONFIG_GETTY is not set
235# CONFIG_LOGIN is not set
236# CONFIG_PASSWD is not set
237# CONFIG_SU is not set
238# CONFIG_SULOGIN is not set
239# CONFIG_VLOCK is not set
240
241#
242# Miscellaneous Utilities
243#
244# CONFIG_ADJTIMEX is not set
245# CONFIG_CROND is not set
246# CONFIG_CRONTAB is not set
247# CONFIG_DC is not set
248# CONFIG_DEVFSD is not set
249# CONFIG_LAST is not set
250# CONFIG_HDPARM is not set
251# CONFIG_MAKEDEVS is not set
252# CONFIG_MT is not set
253# CONFIG_STRINGS is not set
254# CONFIG_TIME is not set
255# CONFIG_WATCHDOG is not set
256
257#
258# Linux Module Utilities
259#
260# CONFIG_DEPMOD is not set
261# CONFIG_INSMOD is not set
262# CONFIG_LSMOD is not set
263# CONFIG_MODPROBE is not set
264# CONFIG_RMMOD is not set
265
266#
267# Networking Utilities
268#
269# CONFIG_FEATURE_IPV6 is not set
270# CONFIG_ARPING is not set
271# CONFIG_FTPGET is not set
272# CONFIG_FTPPUT is not set
273# CONFIG_HOSTNAME is not set
274# CONFIG_HTTPD is not set
275CONFIG_IFCONFIG=y
276CONFIG_FEATURE_IFCONFIG_STATUS=y
277# CONFIG_FEATURE_IFCONFIG_SLIP is not set
278# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
279# CONFIG_FEATURE_IFCONFIG_HW is not set
280# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
281# CONFIG_IFUPDOWN is not set
282# CONFIG_INETD is not set
283CONFIG_IP=y
284CONFIG_FEATURE_IP_ADDRESS=y
285CONFIG_FEATURE_IP_LINK=y
286CONFIG_FEATURE_IP_ROUTE=y
287# CONFIG_FEATURE_IP_TUNNEL is not set
288# CONFIG_IPCALC is not set
289# CONFIG_IPADDR is not set
290# CONFIG_IPLINK is not set
291# CONFIG_IPROUTE is not set
292# CONFIG_IPTUNNEL is not set
293# CONFIG_NAMEIF is not set
294# CONFIG_NC is not set
295# CONFIG_NETSTAT is not set
296# CONFIG_NSLOOKUP is not set
297# CONFIG_PING is not set
298CONFIG_ROUTE=y
299# CONFIG_TELNET is not set
300# CONFIG_TELNETD is not set
301# CONFIG_TFTP is not set
302# CONFIG_TRACEROUTE is not set
303# CONFIG_VCONFIG is not set
304CONFIG_WGET=y
305CONFIG_FEATURE_WGET_STATUSBAR=y
306CONFIG_FEATURE_WGET_AUTHENTICATION=y
307CONFIG_FEATURE_WGET_IP6_LITERAL=y
308
309#
310# udhcp Server/Client
311#
312# CONFIG_UDHCPD is not set
313CONFIG_UDHCPC=y
314CONFIG_FEATURE_UDHCP_SYSLOG=y
315# CONFIG_FEATURE_UDHCP_DEBUG is not set
316
317#
318# Process Utilities
319#
320CONFIG_FREE=y
321CONFIG_KILL=y
322# CONFIG_KILLALL is not set
323CONFIG_PIDOF=y
324CONFIG_PS=y
325# CONFIG_RENICE is not set
326# CONFIG_TOP is not set
327# CONFIG_UPTIME is not set
328
329#
330# Another Bourne-like Shell
331#
332CONFIG_FEATURE_SH_IS_ASH=y
333# CONFIG_FEATURE_SH_IS_HUSH is not set
334# CONFIG_FEATURE_SH_IS_LASH is not set
335# CONFIG_FEATURE_SH_IS_MSH is not set
336# CONFIG_FEATURE_SH_IS_NONE is not set
337CONFIG_ASH=y
338
339#
340# Ash Shell Options
341#
342# CONFIG_ASH_JOB_CONTROL is not set
343# CONFIG_ASH_ALIAS is not set
344CONFIG_ASH_MATH_SUPPORT=y
345# CONFIG_ASH_GETOPTS is not set
346# CONFIG_ASH_CMDCMD is not set
347# CONFIG_ASH_MAIL is not set
348CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
349# CONFIG_HUSH is not set
350# CONFIG_LASH is not set
351# CONFIG_MSH is not set
352
353#
354# Bourne Shell Options
355#
356CONFIG_FEATURE_COMMAND_EDITING=y
357# CONFIG_FEATURE_COMMAND_SAVEHISTORY is not set
358CONFIG_FEATURE_COMMAND_TAB_COMPLETION=y
359# CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION is not set
360CONFIG_FEATURE_COMMAND_HISTORY=15
361CONFIG_FEATURE_SH_STANDALONE_SHELL=y
362CONFIG_FEATURE_SH_FANCY_PROMPT=y
363# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
364
365#
366# System Logging Utilities
367#
368CONFIG_SYSLOGD=y
369# CONFIG_FEATURE_ROTATE_LOGFILE is not set
370# CONFIG_FEATURE_REMOTE_LOG is not set
371# CONFIG_FEATURE_IPC_SYSLOG is not set
372CONFIG_KLOGD=y
373CONFIG_LOGGER=y
374
375#
376# Linux System Utilities
377#
378CONFIG_DMESG=y
379# CONFIG_FBSET is not set
380# CONFIG_FDFLUSH is not set
381# CONFIG_FDFORMAT is not set
382# CONFIG_FDISK is not set
383CONFIG_FREERAMDISK=y
384# CONFIG_FSCK_MINIX is not set
385# CONFIG_MKFS_MINIX is not set
386# CONFIG_GETOPT is not set
387# CONFIG_HEXDUMP is not set
388# CONFIG_HWCLOCK is not set
389# CONFIG_LOSETUP is not set
390CONFIG_MKSWAP=y
391CONFIG_MORE=y
392CONFIG_FEATURE_USE_TERMIOS=y
393CONFIG_PIVOT_ROOT=y
394# CONFIG_RDATE is not set
395CONFIG_SWAPONOFF=y
396CONFIG_MOUNT=y
397# CONFIG_NFSMOUNT is not set
398CONFIG_UMOUNT=y
399CONFIG_FEATURE_MOUNT_FORCE=y
400
401#
402# Common options for mount/umount
403#
404CONFIG_FEATURE_MOUNT_LOOP=y
405# CONFIG_FEATURE_MTAB_SUPPORT is not set
406
407#
408# Debugging Options
409#
410# 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 @@
1#
2# Automatically generated make config: don't edit
3#
4HAVE_DOT_CONFIG=y
5
6#
7# General Configuration
8#
9# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
10CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y
11# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
12# CONFIG_FEATURE_VERBOSE_USAGE is not set
13# CONFIG_FEATURE_INSTALLER is not set
14# CONFIG_LOCALE_SUPPORT is not set
15CONFIG_FEATURE_DEVFS=y
16CONFIG_FEATURE_DEVPTS=y
17# CONFIG_FEATURE_CLEAN_UP is not set
18# CONFIG_FEATURE_SUID is not set
19# CONFIG_SELINUX is not set
20
21#
22# Build Options
23#
24# CONFIG_STATIC is not set
25CONFIG_LFS=y
26# USING_CROSS_COMPILER is not set
27EXTRA_CFLAGS_OPTIONS=""
28
29#
30# Installation Options
31#
32# CONFIG_INSTALL_NO_USR is not set
33PREFIX="./_install"
34
35#
36# Archival Utilities
37#
38CONFIG_AR=y
39# CONFIG_FEATURE_AR_LONG_FILENAMES is not set
40# CONFIG_BUNZIP2 is not set
41# CONFIG_CPIO is not set
42# CONFIG_DPKG is not set
43# CONFIG_DPKG_DEB is not set
44CONFIG_GUNZIP=y
45# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set
46# CONFIG_GZIP is not set
47# CONFIG_RPM2CPIO is not set
48# CONFIG_RPM is not set
49CONFIG_TAR=y
50# CONFIG_FEATURE_TAR_CREATE is not set
51# CONFIG_FEATURE_TAR_BZIP2 is not set
52# CONFIG_FEATURE_TAR_EXCLUDE is not set
53CONFIG_FEATURE_TAR_GZIP=y
54# CONFIG_FEATURE_TAR_COMPRESS is not set
55CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY=y
56CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
57# CONFIG_UNCOMPRESS is not set
58# CONFIG_UNZIP is not set
59
60#
61# Common options for cpio and tar
62#
63# CONFIG_FEATURE_UNARCHIVE_TAPE is not set
64
65#
66# Coreutils
67#
68CONFIG_BASENAME=y
69# CONFIG_CAL is not set
70CONFIG_CAT=y
71# CONFIG_CHGRP is not set
72CONFIG_CHMOD=y
73CONFIG_CHOWN=y
74CONFIG_CHROOT=y
75# CONFIG_CMP is not set
76CONFIG_CP=y
77CONFIG_CUT=y
78# CONFIG_DATE is not set
79# CONFIG_DD is not set
80CONFIG_DF=y
81CONFIG_DIRNAME=y
82# CONFIG_DOS2UNIX is not set
83# CONFIG_DU is not set
84CONFIG_ECHO=y
85CONFIG_FEATURE_FANCY_ECHO=y
86CONFIG_ENV=y
87CONFIG_EXPR=y
88CONFIG_FALSE=y
89# CONFIG_FOLD is not set
90CONFIG_HEAD=y
91# CONFIG_FEATURE_FANCY_HEAD is not set
92# CONFIG_HOSTID is not set
93CONFIG_ID=y
94# CONFIG_INSTALL is not set
95# CONFIG_LENGTH is not set
96CONFIG_LN=y
97# CONFIG_LOGNAME is not set
98CONFIG_LS=y
99# CONFIG_FEATURE_LS_FILETYPES is not set
100CONFIG_FEATURE_LS_FOLLOWLINKS=y
101# CONFIG_FEATURE_LS_RECURSIVE is not set
102CONFIG_FEATURE_LS_SORTFILES=y
103CONFIG_FEATURE_LS_TIMESTAMPS=y
104CONFIG_FEATURE_LS_USERNAME=y
105# CONFIG_FEATURE_LS_COLOR is not set
106CONFIG_MD5SUM=y
107CONFIG_MKDIR=y
108# CONFIG_MKFIFO is not set
109CONFIG_MKNOD=y
110CONFIG_MV=y
111# CONFIG_OD is not set
112CONFIG_PRINTF=y
113CONFIG_PWD=y
114CONFIG_REALPATH=y
115CONFIG_RM=y
116CONFIG_RMDIR=y
117# CONFIG_SHA1SUM is not set
118# CONFIG_SLEEP is not set
119CONFIG_SORT=y
120# CONFIG_STTY is not set
121CONFIG_SYNC=y
122CONFIG_TAIL=y
123# CONFIG_FEATURE_FANCY_TAIL is not set
124# CONFIG_TEE is not set
125CONFIG_TEST=y
126
127#
128# test (forced enabled for use with shell)
129#
130CONFIG_TOUCH=y
131CONFIG_TR=y
132CONFIG_TRUE=y
133# CONFIG_TTY is not set
134CONFIG_UNAME=y
135CONFIG_UNIQ=y
136# CONFIG_USLEEP is not set
137# CONFIG_UUDECODE is not set
138# CONFIG_UUENCODE is not set
139# CONFIG_WATCH is not set
140CONFIG_WC=y
141# CONFIG_WHO is not set
142# CONFIG_WHOAMI is not set
143# CONFIG_YES is not set
144
145#
146# Common options for cp and mv
147#
148CONFIG_FEATURE_PRESERVE_HARDLINKS=y
149
150#
151# Common options for ls and more
152#
153CONFIG_FEATURE_AUTOWIDTH=y
154
155#
156# Common options for df, du, ls
157#
158CONFIG_FEATURE_HUMAN_READABLE=y
159
160#
161# Common options for md5sum, sha1sum
162#
163# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set
164
165#
166# Console Utilities
167#
168# CONFIG_CHVT is not set
169# CONFIG_CLEAR is not set
170# CONFIG_DEALLOCVT is not set
171# CONFIG_DUMPKMAP is not set
172# CONFIG_LOADACM is not set
173# CONFIG_LOADFONT is not set
174# CONFIG_LOADKMAP is not set
175# CONFIG_OPENVT is not set
176# CONFIG_RESET is not set
177# CONFIG_SETKEYCODES is not set
178
179#
180# Debian Utilities
181#
182# CONFIG_MKTEMP is not set
183# CONFIG_PIPE_PROGRESS is not set
184CONFIG_READLINK=y
185# CONFIG_RUN_PARTS is not set
186# CONFIG_START_STOP_DAEMON is not set
187# CONFIG_WHICH is not set
188
189#
190# Editors
191#
192# CONFIG_AWK is not set
193# CONFIG_PATCH is not set
194CONFIG_SED=y
195# CONFIG_VI is not set
196
197#
198# Finding Utilities
199#
200CONFIG_FIND=y
201CONFIG_FEATURE_FIND_MTIME=y
202CONFIG_FEATURE_FIND_PERM=y
203CONFIG_FEATURE_FIND_TYPE=y
204CONFIG_FEATURE_FIND_XDEV=y
205CONFIG_FEATURE_FIND_NEWER=y
206CONFIG_FEATURE_FIND_INUM=y
207CONFIG_GREP=y
208# CONFIG_FEATURE_GREP_EGREP_ALIAS is not set
209# CONFIG_FEATURE_GREP_FGREP_ALIAS is not set
210# CONFIG_FEATURE_GREP_CONTEXT is not set
211# CONFIG_XARGS is not set
212
213#
214# Init Utilities
215#
216CONFIG_INIT=y
217CONFIG_FEATURE_USE_INITTAB=y
218# CONFIG_FEATURE_INITRD is not set
219# CONFIG_FEATURE_INIT_COREDUMPS is not set
220CONFIG_FEATURE_EXTRA_QUIET=y
221CONFIG_HALT=y
222# CONFIG_POWEROFF is not set
223CONFIG_REBOOT=y
224# CONFIG_MESG is not set
225
226#
227# Login/Password Management Utilities
228#
229CONFIG_USE_BB_PWD_GRP=y
230# CONFIG_ADDGROUP is not set
231# CONFIG_DELGROUP is not set
232# CONFIG_ADDUSER is not set
233# CONFIG_DELUSER is not set
234# CONFIG_GETTY is not set
235# CONFIG_LOGIN is not set
236# CONFIG_PASSWD is not set
237# CONFIG_SU is not set
238# CONFIG_SULOGIN is not set
239# CONFIG_VLOCK is not set
240
241#
242# Miscellaneous Utilities
243#
244# CONFIG_ADJTIMEX is not set
245# CONFIG_CROND is not set
246# CONFIG_CRONTAB is not set
247# CONFIG_DC is not set
248# CONFIG_DEVFSD is not set
249# CONFIG_LAST is not set
250# CONFIG_HDPARM is not set
251# CONFIG_MAKEDEVS is not set
252# CONFIG_MT is not set
253# CONFIG_STRINGS is not set
254# CONFIG_TIME is not set
255# CONFIG_WATCHDOG is not set
256
257#
258# Linux Module Utilities
259#
260CONFIG_MODUTILS_OBJ=y
261CONFIG_DEPMOD=y
262CONFIG_INSMOD=y
263# CONFIG_FEATURE_2_2_MODULES is not set
264CONFIG_FEATURE_2_4_MODULES=y
265# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
266# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
267# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
268# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
269CONFIG_LSMOD=y
270CONFIG_FEATURE_QUERY_MODULE_INTERFACE=y
271CONFIG_MODPROBE=y
272# CONFIG_RMMOD is not set
273CONFIG_FEATURE_CHECK_TAINTED_MODULE=y
274
275#
276# Networking Utilities
277#
278# CONFIG_FEATURE_IPV6 is not set
279# CONFIG_ARPING is not set
280# CONFIG_FTPGET is not set
281# CONFIG_FTPPUT is not set
282# CONFIG_HOSTNAME is not set
283# CONFIG_HTTPD is not set
284CONFIG_IFCONFIG=y
285CONFIG_FEATURE_IFCONFIG_STATUS=y
286# CONFIG_FEATURE_IFCONFIG_SLIP is not set
287# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
288# CONFIG_FEATURE_IFCONFIG_HW is not set
289# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
290# CONFIG_IFUPDOWN is not set
291# CONFIG_INETD is not set
292CONFIG_IP=y
293CONFIG_FEATURE_IP_ADDRESS=y
294CONFIG_FEATURE_IP_LINK=y
295CONFIG_FEATURE_IP_ROUTE=y
296# CONFIG_FEATURE_IP_TUNNEL is not set
297# CONFIG_IPCALC is not set
298# CONFIG_IPADDR is not set
299# CONFIG_IPLINK is not set
300# CONFIG_IPROUTE is not set
301# CONFIG_IPTUNNEL is not set
302# CONFIG_NAMEIF is not set
303# CONFIG_NC is not set
304# CONFIG_NETSTAT is not set
305# CONFIG_NSLOOKUP is not set
306# CONFIG_PING is not set
307CONFIG_ROUTE=y
308# CONFIG_TELNET is not set
309# CONFIG_TELNETD is not set
310# CONFIG_TFTP is not set
311# CONFIG_TRACEROUTE is not set
312# CONFIG_VCONFIG is not set
313CONFIG_WGET=y
314CONFIG_FEATURE_WGET_STATUSBAR=y
315CONFIG_FEATURE_WGET_AUTHENTICATION=y
316CONFIG_FEATURE_WGET_IP6_LITERAL=y
317
318#
319# udhcp Server/Client
320#
321# CONFIG_UDHCPD is not set
322CONFIG_UDHCPC=y
323CONFIG_FEATURE_UDHCP_SYSLOG=y
324# CONFIG_FEATURE_UDHCP_DEBUG is not set
325
326#
327# Process Utilities
328#
329CONFIG_FREE=y
330CONFIG_KILL=y
331# CONFIG_KILLALL is not set
332CONFIG_PIDOF=y
333CONFIG_PS=y
334# CONFIG_RENICE is not set
335# CONFIG_TOP is not set
336# CONFIG_UPTIME is not set
337
338#
339# Another Bourne-like Shell
340#
341CONFIG_FEATURE_SH_IS_ASH=y
342# CONFIG_FEATURE_SH_IS_HUSH is not set
343# CONFIG_FEATURE_SH_IS_LASH is not set
344# CONFIG_FEATURE_SH_IS_MSH is not set
345# CONFIG_FEATURE_SH_IS_NONE is not set
346CONFIG_ASH=y
347
348#
349# Ash Shell Options
350#
351# CONFIG_ASH_JOB_CONTROL is not set
352# CONFIG_ASH_ALIAS is not set
353CONFIG_ASH_MATH_SUPPORT=y
354# CONFIG_ASH_GETOPTS is not set
355# CONFIG_ASH_CMDCMD is not set
356# CONFIG_ASH_MAIL is not set
357CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
358# CONFIG_HUSH is not set
359# CONFIG_LASH is not set
360# CONFIG_MSH is not set
361
362#
363# Bourne Shell Options
364#
365CONFIG_FEATURE_COMMAND_EDITING=y
366# CONFIG_FEATURE_COMMAND_SAVEHISTORY is not set
367CONFIG_FEATURE_COMMAND_TAB_COMPLETION=y
368# CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION is not set
369CONFIG_FEATURE_COMMAND_HISTORY=15
370CONFIG_FEATURE_SH_STANDALONE_SHELL=y
371CONFIG_FEATURE_SH_FANCY_PROMPT=y
372# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
373
374#
375# System Logging Utilities
376#
377CONFIG_SYSLOGD=y
378# CONFIG_FEATURE_ROTATE_LOGFILE is not set
379# CONFIG_FEATURE_REMOTE_LOG is not set
380# CONFIG_FEATURE_IPC_SYSLOG is not set
381CONFIG_KLOGD=y
382CONFIG_LOGGER=y
383
384#
385# Linux System Utilities
386#
387CONFIG_DMESG=y
388# CONFIG_FBSET is not set
389# CONFIG_FDFLUSH is not set
390# CONFIG_FDFORMAT is not set
391# CONFIG_FDISK is not set
392CONFIG_FREERAMDISK=y
393# CONFIG_FSCK_MINIX is not set
394# CONFIG_MKFS_MINIX is not set
395# CONFIG_GETOPT is not set
396# CONFIG_HEXDUMP is not set
397# CONFIG_HWCLOCK is not set
398# CONFIG_LOSETUP is not set
399CONFIG_MKSWAP=y
400CONFIG_MORE=y
401CONFIG_FEATURE_USE_TERMIOS=y
402CONFIG_PIVOT_ROOT=y
403# CONFIG_RDATE is not set
404CONFIG_SWAPONOFF=y
405CONFIG_MOUNT=y
406# CONFIG_NFSMOUNT is not set
407CONFIG_UMOUNT=y
408CONFIG_FEATURE_MOUNT_FORCE=y
409
410#
411# Common options for mount/umount
412#
413CONFIG_FEATURE_MOUNT_LOOP=y
414# CONFIG_FEATURE_MTAB_SUPPORT is not set
415
416#
417# Debugging Options
418#
419# 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 @@
1Source: busybox-cvs
2Priority: optional
3Maintainer: Debian Install System Team <debian-boot@lists.debian.org>
4Uploaders: Erik Andersen <andersee@debian.org>, Bastian Blank <waldi@debian.org>, Tollef Fog Heen <tfheen@debian.org>, Glenn McGrath <bug1@debian.org>
5Build-Depends: debhelper (>= 4.0.0)
6Standards-Version: 3.6.0
7
8Package: busybox-cvs
9Architecture: any
10Depends: ${shlibs:Depends}
11Conflicts: busybox-cvs-static, busybox, busybox-static
12Replaces: busybox-cvs-static, busybox, busybox-static
13Section: utils
14Description: Tiny utilities for small and embedded systems
15 BusyBox combines tiny versions of many common UNIX utilities into a single
16 small executable. It provides minimalist replacements for the most common
17 utilities you would usually find on your desktop system (i.e., ls, cp, mv,
18 mount, tar, etc.). The utilities in BusyBox generally have fewer options than
19 their full-featured GNU cousins; however, the options that are included
20 provide the expected functionality and behave very much like their GNU
21 counterparts.
22 .
23 This package installs the BusyBox binary but does not install symlinks
24 for any of the supported utilities. You can use /bin/busybox --install
25 to install BusyBox to the current directory (you do not want to do this
26 in / on your Debian system!).
27
28Package: busybox-cvs-static
29Architecture: any
30Depends: ${shlibs:Depends}
31Conflicts: busybox-cvs, busybox-static, busybox
32Replaces: busybox-cvs, busybox-static, busybox
33Section: shells
34Description: Standalone rescue shell with tons of builtin utilities
35 BusyBox combines tiny versions of many common UNIX utilities into a single
36 small executable. It provides minimalist replacements for the most common
37 utilities you would usually find on your desktop system (i.e., ls, cp, mv,
38 mount, tar, etc.). The utilities in BusyBox generally have fewer options than
39 their full-featured GNU cousins; however, the options that are included
40 provide the expected functionality and behave very much like their GNU
41 counterparts.
42 .
43 BusyBox-static provides you with a statically linked simple stand alone shell
44 that provides all the utilities available in BusyBox. This package is
45 intended to be used as a rescue shell, in the event that you screw up your
46 system. Invoke "busybox sh" and you have a standalone shell ready to save
47 your system from certain destruction. Invoke "busybox", and it will list the
48 available builtin commands.
49
50Package: busybox-cvs-udeb
51Architecture: any
52Depends: ${shlibs:Depends}
53Section: debian-installer
54Conflicts: busybox-udeb
55Enhances: busybox-cvs-floppy-udeb
56Priority: extra
57Description: Tiny utilities for the debian-installer
58 BusyBox combines tiny versions of many common UNIX utilities into a single
59 small executable. It provides minimalist replacements for the most common
60 utilities you would usually find on your desktop system (i.e., ls, cp, mv,
61 mount, tar, etc.). The utilities in BusyBox generally have fewer options than
62 their full-featured GNU cousins; however, the options that are included
63 provide the expected functionality and behave very much like their GNU
64 counterparts.
65 .
66 busybox-udeb is used by the debian-installer, so unless you are working
67 on the debian-installer, this package is not for you. Installing this
68 on your Debian system is a very, very bad idea. You have been warned.
69
70Package: busybox-cvs-floppy-udeb
71Architecture: i386
72Depends: ${shlibs:Depends}
73Section: debian-installer
74Conflicts: busybox-udeb
75Priority: extra
76Description: Tiny utilities for the debian-installer floppy images
77 BusyBox combines tiny versions of many common UNIX utilities into a single
78 small executable. It provides minimalist replacements for the most common
79 utilities you would usually find on your desktop system (i.e., ls, cp, mv,
80 mount, tar, etc.). The utilities in BusyBox generally have fewer options than
81 their full-featured GNU cousins; however, the options that are included
82 provide the expected functionality and behave very much like their GNU
83 counterparts.
84 .
85 busybox-udeb is used by the debian-installer, so unless you are working
86 on the debian-installer, this package is not for you. Installing this
87 on your Debian system is a very, very bad idea. You have been warned.
88
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 @@
1#!/bin/sh
2awk "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 @@
1This package was debianized by Erik Andersen <andersee@debian.org> on
2Sun, 18 Jun 2000 23:31:02 -0600
3
4It was downloaded from ftp://ftp.busybox.net/busybox
5
6Copyright:
7
8 This package is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; version 2 dated June, 1991.
11
12 This package is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this package; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21
22On Debian GNU/Linux systems, the complete text of the GNU General
23Public License can be found in `/usr/share/common-licenses/GPL'.
24
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 @@
1#!/usr/bin/make -f
2
3# Uncomment this to turn on verbose mode.
4#export DH_VERBOSE=1
5
6DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH)
7DEB_HOST_GNU_CPU ?= $(shell dpkg-architecture -qDEB_HOST_GNU_CPU)
8DEB_HOST_GNU_SYSTEM ?= $(shell dpkg-architecture -qDEB_HOST_GNU_SYSTEM)
9
10VERSION = $(shell dpkg-parsechangelog | grep ^Version: | cut -d ' ' -f 2)
11
12ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
13 CONFIG_DEBUG = true
14endif
15ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
16endif
17
18PACKAGE_PREFIX = busybox-cvs
19
20ARCH_FLOPPY_UDEB = $(shell sh debian/control-extract Architecture $(PACKAGE_PREFIX)-floppy-udeb)
21
22PACKAGES_DEB = $(PACKAGE_PREFIX) $(PACKAGE_PREFIX)-static
23PACKAGES_UDEB = $(PACKAGE_PREFIX)-udeb
24ifneq ($(filter $(DEB_HOST_ARCH),$(ARCH_FLOPPY_UDEB)),)
25PACKAGES_UDEB += $(PACKAGE_PREFIX)-floppy-udeb
26endif
27PACKAGES = $(PACKAGES_DEB) $(PACKAGES_UDEB)
28
29DEBHELPER_PACKAGES_DEB = $(patsubst %,-p%,$(PACKAGES_DEB))
30DEBHELPER_PACKAGES_UDEB = $(patsubst %,-p%,$(PACKAGES_UDEB))
31
32CONFIG = $(firstword $(wildcard ./debian/config-$(1)-$(DEB_HOST_GNU_SYSTEM)-$(DEB_HOST_GNU_CPU) ./debian/config-$(1)-$(DEB_HOST_GNU_SYSTEM) ./debian/config-$(1)))
33CONFIG_DEB = $(call CONFIG,deb)
34CONFIG_STATIC = $(call CONFIG,static)
35CONFIG_UDEB = $(call CONFIG,udeb)
36CONFIG_FLOPPY_UDEB = $(call CONFIG,floppy-udeb)
37
38configure: configure-stamp
39configure-stamp:
40 sh ./configure
41
42 touch $@
43
44build-arch: build-arch-deb-all build-arch-udeb-all
45build-arch-deb-all: build-arch-deb build-arch-static build-arch-doc
46build-arch-udeb-all: build-arch-udeb build-arch-floppy-udeb
47
48build-arch-deb: build-arch-deb-stamp
49build-arch-deb-stamp: configure-stamp
50 $(MAKE) clean
51
52 cp $(CONFIG_DEB) .config
53
54 $(MAKE) dep
55 $(MAKE) CONFIG_DEBUG=$(CONFIG_DEBUG)
56
57 install -d install-$(PACKAGE_PREFIX)/bin
58 install busybox install-$(PACKAGE_PREFIX)/bin/busybox
59
60 touch $@
61
62build-arch-static: build-arch-static-stamp
63build-arch-static-stamp: configure-stamp
64 $(MAKE) clean
65
66 cp $(CONFIG_STATIC) .config
67
68 $(MAKE) dep
69 $(MAKE) CONFIG_DEBUG=$(CONFIG_DEBUG)
70
71 install -d install-$(PACKAGE_PREFIX)-static/bin
72 install busybox install-$(PACKAGE_PREFIX)-static/bin/busybox
73
74 touch $@
75
76build-arch-udeb: build-arch-udeb-stamp
77build-arch-udeb-stamp: configure-stamp
78 $(MAKE) clean
79
80 cp $(CONFIG_UDEB) .config
81
82 $(MAKE) dep
83 $(MAKE) CONFIG_DEBUG=$(CONFIG_DEBUG)
84
85 $(MAKE) PREFIX=$(CURDIR)/install-$(PACKAGE_PREFIX)-udeb install
86 # Remove init link, but init support is still compiled in to be
87 # used.
88 rm -f $(CURDIR)/install-$(PACKAGE_PREFIX)-udeb/sbin/init
89
90 touch $@
91
92build-arch-floppy-udeb: build-arch-floppy-udeb-stamp
93build-arch-floppy-udeb-stamp: configure-stamp
94ifneq ($(filter $(DEB_HOST_ARCH),$(ARCH_FLOPPY_UDEB)),)
95 $(MAKE) clean
96
97 cp $(CONFIG_FLOPPY_UDEB) .config
98
99 $(MAKE) dep
100 $(MAKE) CONFIG_DEBUG=$(CONFIG_DEBUG)
101
102 $(MAKE) PREFIX=$(CURDIR)/install-$(PACKAGE_PREFIX)-floppy-udeb install
103endif
104
105 touch $@
106
107build-arch-doc: build-arch-doc-stamp
108build-arch-doc-stamp: configure-stamp
109 $(MAKE) docs/BusyBox.1
110
111 cp docs/BusyBox.1 busybox.1
112
113 touch $@
114
115build: build-arch
116
117clean:
118 dh_testdir
119 dh_testroot
120 rm -f build-*-stamp configure-stamp debian/files~
121
122 -$(MAKE) distclean
123 -rm -rf busybox-deb busybox-static install* busybox.1
124
125 dh_clean
126
127install-deb: build-arch-deb-all
128 dh_testdir
129 dh_testroot
130 dh_clean -k $(DEBHELPER_PACKAGES_DEB)
131 dh_installdirs $(DEBHELPER_PACKAGES_DEB)
132
133 for i in $(PACKAGES_DEB); do \
134 ( \
135 cd install-$$i; \
136 find -type d -exec install -d $(CURDIR)/debian/$$i/{} \;; \
137 find \( -type f -o -type l \) -exec cp -a {} $(CURDIR)/debian/$$i/{} \;; \
138 ); \
139 done
140
141 install -m644 debian/$(PACKAGE_PREFIX)-static.override \
142 debian/$(PACKAGE_PREFIX)-static/usr/share/lintian/overrides/$(PACKAGE_PREFIX)-static
143
144install-udeb: build-arch-udeb-all
145 dh_testdir
146 dh_testroot
147 dh_clean -k $(DEBHELPER_PACKAGES_UDEB)
148 dh_installdirs $(DEBHELPER_PACKAGES_UDEB)
149
150 for i in $(PACKAGES_UDEB); do \
151 ( \
152 cd install-$$i; \
153 find -type d -exec install -d $(CURDIR)/debian/$$i/{} \;; \
154 find \( -type f -o -type l \) -exec cp -a {} $(CURDIR)/debian/$$i/{} \;; \
155 ); \
156 done
157
158binary-arch: binary-arch-deb binary-arch-udeb
159
160# Build architecture-dependent files here.
161binary-arch-deb: build-arch-deb-all install-deb
162 dh_testdir
163 dh_testroot
164 dh_installdocs $(DEBHELPER_PACKAGES_DEB)
165 dh_installman $(DEBHELPER_PACKAGES_DEB)
166 dh_installchangelogs Changelog $(DEBHELPER_PACKAGES_DEB)
167 dh_strip $(DEBHELPER_PACKAGES_DEB)
168 dh_link $(DEBHELPER_PACKAGES_DEB)
169 dh_compress $(DEBHELPER_PACKAGES_DEB)
170 dh_fixperms $(DEBHELPER_PACKAGES_DEB)
171 dh_installdeb $(DEBHELPER_PACKAGES_DEB)
172 dh_shlibdeps $(DEBHELPER_PACKAGES_DEB)
173 dh_gencontrol $(DEBHELPER_PACKAGES_DEB)
174 dh_md5sums $(DEBHELPER_PACKAGES_DEB)
175 dh_builddeb $(DEBHELPER_PACKAGES_DEB)
176
177# Build architecture-dependent files here.
178binary-arch-udeb: build-arch-udeb-all install-udeb
179 dh_testdir
180 dh_testroot
181 dh_strip $(DEBHELPER_PACKAGES_UDEB)
182 dh_link $(DEBHELPER_PACKAGES_UDEB)
183 dh_compress $(DEBHELPER_PACKAGES_UDEB)
184 dh_fixperms $(DEBHELPER_PACKAGES_UDEB)
185 dh_installdeb $(DEBHELPER_PACKAGES_UDEB)
186 dh_shlibdeps $(DEBHELPER_PACKAGES_UDEB)
187 dh_gencontrol $(DEBHELPER_PACKAGES_DEB)
188
189 $(foreach PACKAGE, $(PACKAGES_UDEB), \
190 dh_gencontrol -p$(PACKAGE) -- -fdebian/files~; \
191 dpkg-distaddfile $(PACKAGE)_$(VERSION)_$(DEB_HOST_ARCH).udeb debian-installer extra; \
192 dh_builddeb -p$(PACKAGE) --filename=$(PACKAGE)_$(VERSION)_$(DEB_HOST_ARCH).udeb; \
193 )
194
195binary: binary-arch
196.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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Debian Utilities"
7
8config CONFIG_MKTEMP
9 bool "mktemp"
10 default n
11 help
12 mktemp is used to create unique temporary files
13
14config CONFIG_PIPE_PROGRESS
15 bool "pipe_progress"
16 default n
17 help
18 Display a dot to indicate pipe activity.
19
20config CONFIG_READLINK
21 bool "readlink"
22 default n
23 help
24 This program reads a symbolic link and returns the name
25 of the file it points to
26
27config CONFIG_RUN_PARTS
28 bool "run-parts"
29 default n
30 help
31 run-parts is a utility designed to run all the scripts in a directory.
32
33 It is useful to set up a directory like cron.daily, where you need to
34 execute all the scripts in that directory.
35
36 In this implementation of run-parts some features (such as report mode)
37 are not implemented.
38
39 Unless you know that run-parts is used in some of your scripts
40 you can safely say N here.
41
42config CONFIG_START_STOP_DAEMON
43 bool "start-stop-daemon"
44 default y
45 help
46 start-stop-daemon is used to control the creation and
47 termination of system-level processes, usually the ones
48 started during the startup of the system.
49
50config CONFIG_WHICH
51 bool "which"
52 default n
53 help
54 which is used to find programs in your PATH and
55 print out their pathnames.
56
57endmenu
58
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/debianutils
23DEBIANUTILS_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include $(srcdir)/Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20DEBIANUTILS_AR:=debianutils.a
21ifndef $(DEBIANUTILS_DIR)
22DEBIANUTILS_DIR:=$(top_builddir)/debianutils/
23endif
24srcdir=$(top_srcdir)/debianutils
25
26DEBIANUTILS-y:=
27DEBIANUTILS-$(CONFIG_MKTEMP) += mktemp.o
28DEBIANUTILS-$(CONFIG_PIPE_PROGRESS) += pipe_progress.o
29DEBIANUTILS-$(CONFIG_READLINK) += readlink.o
30DEBIANUTILS-$(CONFIG_RUN_PARTS) += run_parts.o
31DEBIANUTILS-$(CONFIG_START_STOP_DAEMON) += start_stop_daemon.o
32DEBIANUTILS-$(CONFIG_WHICH) += which.o
33
34libraries-y+=$(DEBIANUTILS_DIR)$(DEBIANUTILS_AR)
35
36$(DEBIANUTILS_DIR)$(DEBIANUTILS_AR): $(patsubst %,$(DEBIANUTILS_DIR)%, $(DEBIANUTILS-y))
37 $(AR) -ro $@ $(patsubst %,$(DEBIANUTILS_DIR)%, $(DEBIANUTILS-y))
38
39$(DEBIANUTILS_DIR)%.o: $(srcdir)/%.c
40 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
41
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini mktemp implementation for busybox
4 *
5 *
6 * Copyright (C) 2000 by Daniel Jacobowitz
7 * Written by Daniel Jacobowitz <dan@debian.org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
24
25#include <stdio.h>
26#include <errno.h>
27#include <string.h>
28#include <unistd.h>
29#include <stdlib.h>
30#include "busybox.h"
31
32extern int mktemp_main(int argc, char **argv)
33{
34 unsigned char dir_flag = 0;
35 int opt;
36
37 while ((opt = getopt(argc, argv, "qd")) != -1) {
38 if (opt == 'd') {
39 dir_flag = 1;
40 }
41 else if (opt != 'q') {
42 bb_show_usage();
43 }
44 }
45
46 if (optind + 1 != argc) {
47 bb_show_usage();
48 }
49
50 if (dir_flag) {
51 if (mkdtemp(argv[argc-1]) == NULL) {
52 return EXIT_FAILURE;
53 }
54 } else {
55 if (mkstemp(argv[argc-1]) < 0) {
56 return EXIT_FAILURE;
57 }
58 }
59
60 (void) puts(argv[argc-1]);
61
62 return EXIT_SUCCESS;
63}
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 @@
1/*
2 * Monitor a pipe with a simple progress display.
3 *
4 * Copyright (C) 2003 by Rob Landley <rob@landley.net>, Joey Hess
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <unistd.h>
25#include <time.h>
26
27#include "busybox.h"
28
29#define PIPE_PROGRESS_SIZE 4096
30
31/* Read a block of data from stdin, write it to stdout.
32 * Activity is indicated by a '.' to stderr
33 */
34extern int pipe_progress_main(int argc, char **argv)
35{
36 RESERVE_CONFIG_BUFFER(buf, PIPE_PROGRESS_SIZE);
37 time_t t = time(NULL);
38 size_t len;
39
40 while ((len = fread(buf, 1, PIPE_PROGRESS_SIZE, stdin)) > 0) {
41 time_t new_time = time(NULL);
42 if (new_time != t) {
43 t = new_time;
44 fputc('.', stderr);
45 }
46 fwrite(buf, len, 1, stdout);
47 }
48
49 fputc('\n', stderr);
50
51#ifdef CONFIG_FEATURE_CLEAN_UP
52 RELEASE_CONFIG_BUFFER(buf);
53#endif
54 return 0;
55}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini readlink implementation for busybox
4 *
5 * Copyright (C) 2000,2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <errno.h>
24#include <unistd.h>
25#include <stdlib.h>
26#include "busybox.h"
27
28int readlink_main(int argc, char **argv)
29{
30 char *buf = NULL;
31
32 /* no options, no getopt */
33
34 if (argc != 2)
35 bb_show_usage();
36
37 buf = xreadlink(argv[1]);
38 if (!buf)
39 return EXIT_FAILURE;
40 puts(buf);
41#ifdef CONFIG_FEATURE_CLEAN_UP
42 free(buf);
43#endif
44
45 return EXIT_SUCCESS;
46}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini run-parts implementation for busybox
4 *
5 *
6 * Copyright (C) 2001 by Emanuele Aina <emanuele.aina@tiscali.it>
7 *
8 * Based on the Debian run-parts program, version 1.15
9 * Copyright (C) 1996 Jeff Noxon <jeff@router.patch.net>,
10 * Copyright (C) 1996-1999 Guy Maor <maor@debian.org>
11 *
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 * 02111-1307 USA
27 *
28 */
29
30/* This is my first attempt to write a program in C (well, this is my first
31 * attempt to write a program! :-) . */
32
33/* This piece of code is heavily based on the original version of run-parts,
34 * taken from debian-utils. I've only removed the long options and a the
35 * report mode. As the original run-parts support only long options, I've
36 * broken compatibility because the BusyBox policy doesn't allow them.
37 * The supported options are:
38 * -t test. Print the name of the files to be executed, without
39 * execute them.
40 * -a ARG argument. Pass ARG as an argument the program executed. It can
41 * be repeated to pass multiple arguments.
42 * -u MASK umask. Set the umask of the program executed to MASK. */
43
44/* TODO
45 * done - convert calls to error in perror... and remove error()
46 * done - convert malloc/realloc to their x... counterparts
47 * done - remove catch_sigchld
48 * done - use bb's concat_path_file()
49 * done - declare run_parts_main() as extern and any other function as static?
50 */
51
52#include <getopt.h>
53#include <stdlib.h>
54
55#include "libbb.h"
56
57static const struct option runparts_long_options[] = {
58 { "test", 0, NULL, 't' },
59 { "umask", 1, NULL, 'u' },
60 { "arg", 1, NULL, 'a' },
61 { 0, 0, 0, 0 }
62};
63
64extern char **environ;
65
66/* run_parts_main */
67/* Process options */
68int run_parts_main(int argc, char **argv)
69{
70 char **args = xmalloc(2 * sizeof(char *));
71 unsigned char test_mode = 0;
72 unsigned short argcount = 1;
73 int opt;
74
75 umask(022);
76
77 while ((opt = getopt_long (argc, argv, "tu:a:",
78 runparts_long_options, NULL)) > 0)
79 {
80 switch (opt) {
81 /* Enable test mode */
82 case 't':
83 test_mode++;
84 break;
85 /* Set the umask of the programs executed */
86 case 'u':
87 /* Check and set the umask of the program executed. As stated in the original
88 * run-parts, the octal conversion in libc is not foolproof; it will take the
89 * 8 and 9 digits under some circumstances. We'll just have to live with it.
90 */
91 umask(bb_xgetlarg(optarg, 8, 0, 07777));
92 break;
93 /* Pass an argument to the programs */
94 case 'a':
95 /* Add an argument to the commands that we will call.
96 * Called once for every argument. */
97 args = xrealloc(args, (argcount + 2) * (sizeof(char *)));
98 args[argcount++] = optarg;
99 break;
100 default:
101 bb_show_usage();
102 }
103 }
104
105 /* We require exactly one argument: the directory name */
106 if (optind != (argc - 1)) {
107 bb_show_usage();
108 }
109
110 args[0] = argv[optind];
111 args[argcount] = 0;
112
113 return(run_parts(args, test_mode, environ));
114}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini start-stop-daemon implementation(s) for busybox
4 *
5 * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
6 * public domain.
7 * Adapted for busybox David Kimdon <dwhedon@gordian.com>
8 */
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <stdarg.h>
14#include <signal.h>
15#include <errno.h>
16#include <sys/stat.h>
17#include <dirent.h>
18#include <unistd.h>
19#include <getopt.h>
20
21#include "busybox.h"
22#include "pwd_.h"
23
24static int signal_nr = 15;
25static int user_id = -1;
26static int quiet = 0;
27static char *userspec = NULL;
28static char *cmdname = NULL;
29static char *execname = NULL;
30static char *pidfile = NULL;
31
32struct pid_list {
33 struct pid_list *next;
34 pid_t pid;
35};
36
37static struct pid_list *found = NULL;
38
39static inline void
40push(pid_t pid)
41{
42 struct pid_list *p;
43
44 p = xmalloc(sizeof(*p));
45 p->next = found;
46 p->pid = pid;
47 found = p;
48}
49
50static int
51pid_is_exec(pid_t pid, const char *name)
52{
53 char buf[32];
54 struct stat sb, exec_stat;
55
56 if (name && stat(name, &exec_stat))
57 bb_perror_msg_and_die("stat %s", name);
58
59 sprintf(buf, "/proc/%d/exe", pid);
60 if (stat(buf, &sb) != 0)
61 return 0;
62 return (sb.st_dev == exec_stat.st_dev && sb.st_ino == exec_stat.st_ino);
63}
64
65static int
66pid_is_user(int pid, int uid)
67{
68 struct stat sb;
69 char buf[32];
70
71 sprintf(buf, "/proc/%d", pid);
72 if (stat(buf, &sb) != 0)
73 return 0;
74 return (sb.st_uid == uid);
75}
76
77static int
78pid_is_cmd(pid_t pid, const char *name)
79{
80 char buf[32];
81 FILE *f;
82 int c;
83
84 sprintf(buf, "/proc/%d/stat", pid);
85 f = fopen(buf, "r");
86 if (!f)
87 return 0;
88 while ((c = getc(f)) != EOF && c != '(')
89 ;
90 if (c != '(') {
91 fclose(f);
92 return 0;
93 }
94 /* this hopefully handles command names containing ')' */
95 while ((c = getc(f)) != EOF && c == *name)
96 name++;
97 fclose(f);
98 return (c == ')' && *name == '\0');
99}
100
101
102static void
103check(int pid)
104{
105 if (execname && !pid_is_exec(pid, execname)) {
106 return;
107 }
108 if (userspec && !pid_is_user(pid, user_id)) {
109 return;
110 }
111 if (cmdname && !pid_is_cmd(pid, cmdname)) {
112 return;
113 }
114 push(pid);
115}
116
117
118static void
119do_pidfile(void)
120{
121 FILE *f;
122 pid_t pid;
123
124 f = fopen(pidfile, "r");
125 if (f) {
126 if (fscanf(f, "%d", &pid) == 1)
127 check(pid);
128 fclose(f);
129 } else if (errno != ENOENT)
130 bb_perror_msg_and_die("open pidfile %s", pidfile);
131
132}
133
134static void
135do_procinit(void)
136{
137 DIR *procdir;
138 struct dirent *entry;
139 int foundany, pid;
140
141 if (pidfile) {
142 do_pidfile();
143 return;
144 }
145
146 procdir = opendir("/proc");
147 if (!procdir)
148 bb_perror_msg_and_die ("opendir /proc");
149
150 foundany = 0;
151 while ((entry = readdir(procdir)) != NULL) {
152 if (sscanf(entry->d_name, "%d", &pid) != 1)
153 continue;
154 foundany++;
155 check(pid);
156 }
157 closedir(procdir);
158 if (!foundany)
159 bb_error_msg_and_die ("nothing in /proc - not mounted?");
160}
161
162
163static void
164do_stop(void)
165{
166 char what[1024];
167 struct pid_list *p;
168 int killed = 0;
169
170 do_procinit();
171
172 if (cmdname)
173 strcpy(what, cmdname);
174 else if (execname)
175 strcpy(what, execname);
176 else if (pidfile)
177 sprintf(what, "process in pidfile `%.200s'", pidfile);
178 else if (userspec)
179 sprintf(what, "process(es) owned by `%s'", userspec);
180 else
181 bb_error_msg_and_die ("internal error, please report");
182
183 if (!found) {
184 if (!quiet)
185 printf("no %s found; none killed.\n", what);
186 return;
187 }
188 for (p = found; p; p = p->next) {
189 if (kill(p->pid, signal_nr) == 0) {
190 p->pid = -p->pid;
191 killed++;
192 } else {
193 bb_perror_msg("warning: failed to kill %d", p->pid);
194 }
195 }
196 if (!quiet && killed) {
197 printf("stopped %s (pid", what);
198 for (p = found; p; p = p->next)
199 if(p->pid < 0)
200 printf(" %d", -p->pid);
201 printf(").\n");
202 }
203}
204
205
206static const struct option ssd_long_options[] = {
207 { "stop", 0, NULL, 'K' },
208 { "start", 0, NULL, 'S' },
209 { "background", 0, NULL, 'b' },
210 { "quiet", 0, NULL, 'q' },
211 { "make-pidfile", 0, NULL, 'm' },
212 { "startas", 1, NULL, 'a' },
213 { "name", 1, NULL, 'n' },
214 { "signal", 1, NULL, 's' },
215 { "user", 1, NULL, 'u' },
216 { "exec", 1, NULL, 'x' },
217 { "pidfile", 1, NULL, 'p' },
218 { 0, 0, 0, 0 }
219};
220
221#define SSD_CTX_STOP 1
222#define SSD_CTX_START 2
223#define SSD_OPT_BACKGROUND 4
224#define SSD_OPT_QUIET 8
225#define SSD_OPT_MAKEPID 16
226
227int
228start_stop_daemon_main(int argc, char **argv)
229{
230 unsigned long opt;
231 char *signame = NULL;
232 char *startas = NULL;
233
234 bb_applet_long_options = ssd_long_options;
235
236 bb_opt_complementaly = "K~S:S~K";
237 opt = bb_getopt_ulflags(argc, argv, "KSbqma:n:s:u:x:p:",
238 &startas, &cmdname, &signame, &userspec, &execname, &pidfile);
239
240 /* Check one and only one context option was given */
241 if ((opt & 0x80000000UL) || (opt & (SSD_CTX_STOP | SSD_CTX_START)) == 0) {
242 bb_show_usage();
243 }
244
245 if (signame) {
246 signal_nr = bb_xgetlarg(signame, 10, 0, NSIG);
247 }
248
249 if (!execname && !pidfile && !userspec && !cmdname)
250 bb_error_msg_and_die ("need at least one of -x, -p, -u, or -n");
251
252 if (!startas)
253 startas = execname;
254
255 if ((opt & SSD_CTX_START) && !startas)
256 bb_error_msg_and_die ("-S needs -x or -a");
257
258 if ((opt & SSD_OPT_MAKEPID) && pidfile == NULL)
259 bb_error_msg_and_die ("-m needs -p");
260
261 argc -= optind;
262 argv += optind;
263
264 if (userspec && sscanf(userspec, "%d", &user_id) != 1)
265 user_id = my_getpwnam(userspec);
266
267 if (opt & SSD_CTX_STOP) {
268 do_stop();
269 return EXIT_SUCCESS;
270 }
271
272 do_procinit();
273
274 if (found) {
275 if (!quiet)
276 printf("%s already running.\n%d\n", execname ,found->pid);
277 return EXIT_SUCCESS;
278 }
279 *--argv = startas;
280 if (opt & SSD_OPT_BACKGROUND) {
281 if (daemon(0, 0) == -1)
282 bb_perror_msg_and_die ("unable to fork");
283 setsid();
284 }
285 if (opt & SSD_OPT_MAKEPID) {
286 /* user wants _us_ to make the pidfile */
287 FILE *pidf = fopen(pidfile, "w");
288 pid_t pidt = getpid();
289 if (pidf == NULL)
290 bb_perror_msg_and_die("Unable to write pidfile '%s'", pidfile);
291 fprintf(pidf, "%d\n", pidt);
292 fclose(pidf);
293 }
294 execv(startas, argv);
295 bb_perror_msg_and_die ("unable to start %s", startas);
296}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Which implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 * Based on which from debianutils
22 */
23
24
25#include <string.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
29
30#include "busybox.h"
31
32
33extern int which_main(int argc, char **argv)
34{
35 char *path_list;
36 int i, count=1, status = EXIT_SUCCESS;
37
38 if (argc <= 1 || **(argv + 1) == '-') {
39 bb_show_usage();
40 }
41 argc--;
42
43 path_list = getenv("PATH");
44 if (path_list != NULL) {
45 for (i=strlen(path_list); i > 0; i--) {
46 if (path_list[i]==':') {
47 path_list[i]=0;
48 count++;
49 }
50 }
51 } else {
52 path_list = "/bin\0/sbin\0/usr/bin\0/usr/sbin\0/usr/local/bin";
53 count = 5;
54 }
55
56 while (argc-- > 0) {
57 char *buf;
58 char *path_n;
59 char found = 0;
60 argv++;
61
62 /*
63 * Check if we were given the full path, first.
64 * Otherwise see if the file exists in our $PATH.
65 */
66 path_n = path_list;
67 buf = *argv;
68 if (access(buf, X_OK) == 0) {
69 found = 1;
70 } else {
71 for (i = 0; i < count; i++) {
72 buf = concat_path_file(path_n, *argv);
73 if (access(buf, X_OK) == 0) {
74 found = 1;
75 break;
76 }
77 free(buf);
78 path_n += (strlen(path_n) + 1);
79 }
80 }
81 if (found) {
82 puts(buf);
83 } else {
84 status = EXIT_FAILURE;
85 }
86 }
87 return status;
88}
89
90/*
91Local Variables:
92c-file-style: "linux"
93c-basic-offset: 4
94tab-width: 4
95End:
96*/
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 @@
1BusyBox.txt
2BusyBox.1
3BusyBox.html
4busybox.txt
5busybox.ps
6busybox.pdf
7busybox
8busybox.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 @@
1#!/usr/bin/perl -w
2
3use strict;
4use Getopt::Long;
5
6# collect lines continued with a '\' into an array
7sub continuation {
8 my $fh = shift;
9 my @line;
10
11 while (<$fh>) {
12 my $s = $_;
13 $s =~ s/\\\s*$//;
14 #$s =~ s/#.*$//;
15 push @line, $s;
16 last unless (/\\\s*$/);
17 }
18 return @line;
19}
20
21# regex && eval away unwanted strings from documentation
22sub beautify {
23 my $text = shift;
24 $text =~ s/USAGE_NOT\w+\(.*?"\s*\)//sxg;
25 $text =~ s/USAGE_\w+\(\s*?(.*?)"\s*\)/$1"/sxg;
26 $text =~ s/"\s*"//sg;
27 my @line = split("\n", $text);
28 $text = join('',
29 map {
30 s/^\s*"//;
31 s/"\s*$//;
32 s/%/%%/g;
33 s/\$/\\\$/g;
34 eval qq[ sprintf(qq{$_}) ]
35 } @line
36 );
37 return $text;
38}
39
40# generate POD for an applet
41sub pod_for_usage {
42 my $name = shift;
43 my $usage = shift;
44
45 # Sigh. Fixup the known odd-name applets.
46 $name =~ s/dpkg_deb/dpkg-deb/g;
47 $name =~ s/fsck_minix/fsck.minix/g;
48 $name =~ s/mkfs_minix/mkfs.minix/g;
49 $name =~ s/run_parts/run-parts/g;
50 $name =~ s/start_stop_daemon/start-stop-daemon/g;
51
52 # make options bold
53 my $trivial = $usage->{trivial};
54 $trivial =~ s/(?<!\w)(-\w+)/B<$1>/sxg;
55 my @f0 =
56 map { $_ !~ /^\s/ && s/(?<!\w)(-\w+)/B<$1>/g; $_ }
57 split("\n", $usage->{full});
58
59 # add "\n" prior to certain lines to make indented
60 # lines look right
61 my @f1;
62 my $len = @f0;
63 for (my $i = 0; $i < $len; $i++) {
64 push @f1, $f0[$i];
65 if (($i+1) != $len && $f0[$i] !~ /^\s/ && $f0[$i+1] =~ /^\s/) {
66 next if ($f0[$i] =~ /^$/);
67 push(@f1, "") unless ($f0[$i+1] =~ /^\s*$/s);
68 }
69 }
70 my $full = join("\n", @f1);
71
72 # prepare notes if they exist
73 my $notes = (defined $usage->{notes})
74 ? "$usage->{notes}\n\n"
75 : "";
76
77 # prepare examples if they exist
78 my $example = (defined $usage->{example})
79 ?
80 "Example:\n\n" .
81 join ("\n",
82 map { "\t$_" }
83 split("\n", $usage->{example})) . "\n\n"
84 : "";
85
86 return
87 "=item B<$name>".
88 "\n\n$name $trivial\n\n".
89 "$full\n\n" .
90 "$notes" .
91 "$example" .
92 "-------------------------------".
93 "\n\n"
94 ;
95}
96
97# the keys are applet names, and
98# the values will contain hashrefs of the form:
99#
100# {
101# trivial => "...",
102# full => "...",
103# notes => "...",
104# example => "...",
105# }
106my %docs;
107
108
109# get command-line options
110
111my %opt;
112
113GetOptions(
114 \%opt,
115 "help|h",
116 "pod|p",
117 "verbose|v",
118);
119
120if (defined $opt{help}) {
121 print
122 "$0 [OPTION]... [FILE]...\n",
123 "\t--help\n",
124 "\t--pod\n",
125 "\t--verbose\n",
126 ;
127 exit 1;
128}
129
130
131# collect documenation into %docs
132
133foreach (@ARGV) {
134 open(USAGE, $_) || die("$0: $_: $!");
135 my $fh = *USAGE;
136 my ($applet, $type, @line);
137 while (<$fh>) {
138 if (/^#define (\w+)_(\w+)_usage/) {
139 $applet = $1;
140 $type = $2;
141 @line = continuation($fh);
142 my $doc = $docs{$applet} ||= { };
143 my $text = join("\n", @line);
144 $doc->{$type} = beautify($text);
145 }
146 }
147}
148
149
150# generate structured documentation
151
152my $generator = \&pod_for_usage;
153foreach my $applet (sort keys %docs) {
154 print $generator->($applet, $docs{$applet});
155}
156
157exit 0;
158
159__END__
160
161=head1 NAME
162
163autodocifier.pl - generate docs for busybox based on usage.h
164
165=head1 SYNOPSIS
166
167autodocifier.pl [OPTION]... [FILE]...
168
169Example:
170
171 ( cat docs/busybox_header.pod; \
172 docs/autodocifier.pl usage.h; \
173 cat docs/busybox_footer.pod ) > docs/busybox.pod
174
175=head1 DESCRIPTION
176
177The purpose of this script is to automagically generate
178documentation for busybox using its usage.h as the original source
179for content. It used to be that same content has to be duplicated
180in 3 places in slightly different formats -- F<usage.h>,
181F<docs/busybox.pod>. This was tedious and error-prone, so it was
182decided that F<usage.h> would contain all the text in a
183machine-readable form, and scripts could be used to transform this
184text into other forms if necessary.
185
186F<autodocifier.pl> is one such script. It is based on a script by
187Erik Andersen <andersen@codepoet.org> which was in turn based on a
188script by Mark Whitley <markw@codepoet.org>
189
190=head1 OPTIONS
191
192=over 4
193
194=item B<--help>
195
196This displays the help message.
197
198=item B<--pod>
199
200Generate POD (this is the default)
201
202=item B<--verbose>
203
204Be verbose (not implemented)
205
206=back
207
208=head1 FORMAT
209
210The following is an example of some data this script might parse.
211
212 #define length_trivial_usage \
213 "STRING"
214 #define length_full_usage \
215 "Prints out the length of the specified STRING."
216 #define length_example_usage \
217 "$ length Hello\n" \
218 "5\n"
219
220Each entry is a cpp macro that defines a string. The macros are
221named systematically in the form:
222
223 $name_$type_usage
224
225$name is the name of the applet. $type can be "trivial", "full", "notes",
226or "example". Every documentation macro must end with "_usage".
227
228The definition of the types is as follows:
229
230=over 4
231
232=item B<trivial>
233
234This should be a brief, one-line description of parameters that
235the command expects. This will be displayed when B<-h> is issued to
236a command. I<REQUIRED>
237
238=item B<full>
239
240This should contain descriptions of each option. This will also
241be displayed along with the trivial help if CONFIG_FEATURE_TRIVIAL_HELP
242is disabled. I<REQUIRED>
243
244=item B<notes>
245
246This is documentation that is intended to go in the POD or SGML, but
247not be printed when a B<-h> is given to a command. To see an example
248of notes being used, see init_notes_usage in F<usage.h>. I<OPTIONAL>
249
250=item B<example>
251
252This should be an example of how the command is actually used.
253This will not be printed when a B<-h> is given to a command -- it
254will only be included in the POD or SGML documentation. I<OPTIONAL>
255
256=back
257
258=head1 FILES
259
260F<usage.h>
261
262=head1 COPYRIGHT
263
264Copyright (c) 2001 John BEPPU. All rights reserved. This program is
265free software; you can redistribute it and/or modify it under the same
266terms as Perl itself.
267
268=head1 AUTHOR
269
270John BEPPU <b@ax9.org>
271
272=cut
273
274# $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 @@
1BusyBox.html
2busybox.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 @@
1<!--#include file="header.html" -->
2
3
4<h3>Frequently Asked Questions</h3>
5
6This is a collection of some of the more frequently asked questions
7about BusyBox. Some of the questions even have answers. If you
8have additions to this FAQ document, we would love to add them,
9
10<ol>
11<li><a href="#kernel">Which Linux kernel versions are supported?</a>
12<li><a href="#arch">Which architectures does BusyBox run on?</a>
13<li><a href="#libc">Which C libraries are supported?</a>
14<li><a href="#commercial">Can I include BusyBox as part of the software on my device?</a>
15<li><a href="#bugs">I think I found a bug in BusyBox! What should I do?!</a>
16<li><a href="#job_control">Why do I keep getting "sh: can't access tty; job control
17 turned off" errors? Why doesn't Control-C work within my shell?</a>
18<li><a href="#demanding">I demand that you to add &lt;favorite feature&gt; right now! How come
19 you don't answer all my questions on the mailing list instantly? I demand
20 that you help me with all of my problems <em>Right Now</em>!</a>
21<li><a href="#getting_started">How can I get started using BusyBox?</a>
22<li><a href="#helpme">I need help with BusyBox! What should I do?</a>
23<li><a href="#contracts">I need you to add &lt;favorite feature&gt;! Are the BusyBox developers willing to
24 be paid in order to fix bugs or add in &lt;favorite feature&gt;? Are you willing to provide
25 support contracts?</a>
26<li><a href="#support">I think you guys are great and I want to help support your work!</a>
27
28
29</ol>
30
31
32<hr />
33<p>
34<h2><a name="kernel">Which Linux kernel versions are supported?</a></h2>
35<p>
36
37
38 Full functionality requires Linux 2.2.x or better. A large fraction of the
39 code should run on just about anything. While the current code is fairly
40 Linux specific, it should be fairly easy to port the majority of the code
41 to support, say, FreeBSD or Solaris, or Mac OS X, or even Windows (if you
42 are into that sort of thing).
43
44
45<hr />
46<p>
47<h2><a name="arch">Which architectures does BusyBox run on?</a></h2>
48<p>
49
50
51 BusyBox in general will build on any architecture supported by gcc.
52 Kernel module loading for 2.2 and 2.4 Linux kernels is currently
53 limited to ARM, CRIS, H8/300, x86, ia64, x86_64, m68k, MIPS, PowerPC,
54 S390, SH3/4/5, Sparc, v850e, and x86_64 for 2.4.x kernels.
55
56 With 2.6.x kernels, module loading support should work on all architectures.
57
58
59<hr />
60<p>
61<h2><a name="libc">Which C libraries are supported?</a></h2>
62<p>
63
64
65 uClibc and glibc are supported. People have been looking at newlib and
66 dietlibc, but they are currently considered unsupported, untested, or
67 worse. Linux-libc5 is no longer supported. If you require a small C
68 library, you should probably use uClibc.
69
70
71<hr />
72<p>
73<h2><a name="commercial">Can I include BusyBox as part of the software on my device?</h2>
74
75 Yes. As long as you <a href="http://busybox.net/license.html">fully comply
76 with the generous terms of the GPL BusyBox license</a> you can ship BusyBox
77 as part of the software on your device.
78
79 <a href="#support">Please consider sharing some of the money you make.</a>
80
81
82<hr />
83<p>
84<h2><a name="bugs">I think I found a bug in BusyBox! What should I do?</h2>
85<p>
86
87 If you find a problem with BusyBox, please submit a detailed bug report to
88 the BusyBox mailing list at <a href="mailto:busybox@mail.busybox.net">
89 busybox@mail.busybox.net</a>. Please do not send private email to Erik
90 (the maintainer of BusyBox) asking for private help unless you are planning
91 on paying for consulting services. When we answer questions on the BusyBox
92 mailing list, it helps everyone, while private answers help only you...
93
94 <p>
95
96 If you find bugs, please submit a detailed bug report to the BusyBox mailing
97 list at busybox@mail.busybox.net. A well-written bug report should include a
98 transcript of a shell session that demonstrates the bad behavior and enables
99 anyone else to duplicate the bug on their own machine. The following is such
100 an example:
101
102<pre>
103 To: busybox@mail.busybox.net
104 From: diligent@testing.linux.org
105 Subject: /bin/date doesn't work
106
107 Package: BusyBox
108 Version: 1.00
109
110 When I execute BusyBox 'date' it produces unexpected results.
111 With GNU date I get the following output:
112
113 $ date
114 Fri Oct 8 14:19:41 MDT 2004
115
116 But when I use BusyBox date I get this instead:
117
118 $ date
119 illegal instruction
120
121 I am using Debian unstable, kernel version 2.4.27 on a x86 system,
122 and the latest uClibc from CVS. Thanks for the wonderful program!
123
124 -Diligent
125</pre>
126
127 Note the careful description and use of examples showing not only what BusyBox
128 does, but also a counter example showing what an equivalent GNU app does. Bug
129 reports lacking proper detail may never be fixed... Thanks for understanding.
130
131<hr />
132<p>
133<h2><a name="job_control">Why do I keep getting "sh: can't access tty; job control
134 turned off" errors? Why doesn't Control-C work within my shell?</a></h2>
135<p>
136
137 Job control will be turned off since your shell can not obtain a controlling
138 terminal. This typically happens when you run your shell on /dev/console.
139 The kernel will not provide a controlling terminal on the /dev/console
140 device. Your should run your shell on a normal tty such as tty1 or ttyS0
141 and everything will work perfectly. If you <em>REALLY</em> want your shell
142 to run on /dev/console, then you can hack your kernel (if you are into that
143 sortof thing) by changing drivers/char/tty_io.c to change the lines where
144 it sets "noctty = 1;" to instead set it to "0". I recommend you instead
145 run your shell on a real console...
146
147
148<hr />
149<p>
150<h2><a name="getting_started">How can I get started using BusyBox?</a></h2>
151<p>
152
153 An easy method to build your own basic BusyBox based system, is to
154 follow these simple steps:
155 <ul>
156 <li> Point your web browser <a href="/cgi-bin/cvsweb/buildroot/">here</a>
157 <li> Click on "Download tarball"
158 <li> Unpack the tarball on your Linux system somewhere
159 <li> run 'make' and configure things to taste.
160 <li> run 'unset CC'. Some Linux systems (i.e. Gentoo) set 'CC'
161 in the system environment which messes up cross compiles.
162 <li> run 'make'
163 <li> go have lunch, drink a pop, call a friend, play a video game, etc
164 till it finishes downloading software and compiling things.
165 <li> You should now have a shiny new BusyBox based system.
166 </ul>
167
168
169<hr />
170<p>
171<h2><a name="demanding">I demand that you to add &lt;favorite feature&gt; right now! How come
172 you don't answer all my questions on the mailing list instantly? I demand
173 that you help me with all of my problems <em>Right Now</em>!</a></h2>
174<p>
175
176 You have not paid us a single cent and yet you still have the product of
177 many years of our work. We are not your slaves! We work on BusyBox
178 because we find it useful and interesting. If you go off flaming us, we
179 will ignore you.
180
181
182<hr />
183<p>
184<h2><a name="helpme">I need help with BusyBox! What should I do?</a></h2>
185<p>
186
187 If you find that you need help with BusyBox, you can ask for help on the
188 BusyBox mailing list at busybox@mail.busybox.net. In addition to the BusyBox
189 mailing list, Erik (andersee), Manuel (mjn3) and others are known to hang out
190 on the uClibc IRC channel: #uclibc on irc.freenode.net.
191
192 <p>
193
194 <b>Please do not send private email to Erik, Manuel, or the other BusyBox
195 contributors asking for private help unless you are planning on paying for
196 consulting services.</b>
197
198 <p>
199
200 When we answer questions on the BusyBox mailing list, it helps everyone
201 since people with similar problems in the future will be able to get help
202 by searching the mailing list archives. Private help is reserved as a paid
203 service. If you need to use private communication, or if you are serious
204 about getting timely assistance with BusyBox, you should seriously consider
205 paying for consulting services.
206
207 <p>
208
209
210
211<hr />
212<p>
213<h2><a name="contracts">I need you to add &lt;favorite feature&gt;! Are the BusyBox
214 developers willing to be paid in order to fix bugs or add in &lt;favorite feature&gt;?
215 Are you willing to provide support contracts?</a></h2>
216<p>
217
218 Sure! Now you have our attention! What you should do is contact <a
219 href="mailto:andersen@codepoet.org">Erik Andersen</a> of <a
220 href="http://codepoet-consulting.com/">CodePoet Consulting</a> to bid
221 on your project. If Erik is too busy to personally add your feature, there
222 are many other active BusyBox contributors who will almost certainly be able
223 to help you out. Erik can contact them privatly, and may even let you to
224 post your request for services on the mailing list.
225
226
227<hr />
228<p>
229<h2><a name="support">I think you guys are great and I want to help support your work!</a></h2>
230<p>
231
232 Wow, that would be great! Erik personally pays for all the bandwidth, and
233 all servers used for busybox.net out of his own pocket. If you would like
234 to make a donation to help support BusyBox, and/or request features, you
235 can click here:
236
237 <!-- Begin PayPal Logo -->
238 <center>
239 <form action="https://www.paypal.com/cgi-bin/webscr" method="post">
240 <input type="hidden" name="cmd" value="_xclick">
241 <input type="hidden" name="business" value="andersen@codepoet.org">
242 <input type="hidden" name="item_name" value="Support BusyBox">
243 <input type="hidden" name="image_url" value="http://codepoet-consulting.com/images/codepoet.png">
244 <input type="hidden" name="no_shipping" value="1">
245 <input type="image" src="images/donate.png" name="submit" alt="Make donation using PayPal">
246 </form>
247 </center>
248 <!-- End PayPal Logo -->
249
250 If you prefer to contact Erik directly to make a donation, donate hardware,
251 request support, etc, you can contact
252 <a href="http://codepoet-consulting.com/">CodePoet Consulting</a> here.
253 CodePoet Consulting can accept both Visa and MasterCard for those that do not
254 trust PayPal...
255
256<hr />
257
258<br>
259<br>
260<br>
261<br>
262<br>
263<br>
264<br>
265<br>
266<br>
267<br>
268<br>
269<br>
270<br>
271<br>
272<br>
273<br>
274<br>
275<br>
276<br>
277<br>
278<br>
279<br>
280<br>
281<br>
282<br>
283<br>
284<br>
285<br>
286<br>
287<br>
288<br>
289<br>
290<br>
291<br>
292<br>
293<br>
294<br>
295<br>
296<br>
297<br>
298<br>
299<br>
300<br>
301<br>
302<br>
303<br>
304<br>
305<br>
306<br>
307<br>
308<br>
309<br>
310<br>
311<br>
312<br>
313<br>
314<br>
315<br>
316<br>
317<br>
318<br>
319<br>
320<br>
321<br>
322
323<!--#include file="footer.html" -->
324
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 @@
1<!--#include file="header.html" -->
2
3
4<!-- Begin Introduction section -->
5
6<h3>BusyBox: The Swiss Army Knife of Embedded Linux</h3>
7
8
9BusyBox combines tiny versions of many common UNIX utilities into a single
10small executable. It provides replacements for most of the utilities you
11usually find in GNU fileutils, shellutils, etc. The utilities in BusyBox
12generally have fewer options than their full-featured GNU cousins; however,
13the options that are included provide the expected functionality and behave
14very much like their GNU counterparts. BusyBox provides a fairly complete
15environment for any small or embedded system.
16
17<p>
18
19BusyBox has been written with size-optimization and limited resources in
20mind. It is also extremely modular so you can easily include or exclude
21commands (or features) at compile time. This makes it easy to customize
22your embedded systems. To create a working system, just add some device
23nodes in /dev, a few configuration files in /etc, and a Linux kernel.
24
25<p>
26
27BusyBox is maintained by <a href=
28"http://codepoet.org/andersen/erik/erik.html">Erik Andersen</a>, and
29licensed under the
30<a href= "http://www.gnu.org/copyleft/gpl.html">GNU GENERAL PUBLIC LICENSE</a>
31
32<p>
33<p>
34
35<h3>Sponsors</h3>
36
37Please visit our sponsors and thank them for their
38support! They have provided money for equipment and
39bandwidth. Next time you need help with a project,
40consider these fine companies!
41
42
43<ul>
44 <li><a href="http://www.penguru.net">Penguru Consulting</a><br>
45 Custom development for embedded Linux systems and multimedia platforms
46 </li>
47
48 <li><a href="http://opensource.se/">opensource.se</a><br>
49 Embedded open source consulting in Europe.
50 </li>
51
52 <li><a href="http://www.codepoet-consulting.com">Codepoet Consulting</a><br>
53 Custom Linux, embedded Linux, BusyBox, and uClibc
54 development.
55 </li>
56
57</ul>
58
59If you wish to be a sponsor, or if you have already contributed and would like
60your name added here, email <a href= "mailto:andersen@codepoet.org">Erik</a>.
61
62
63<!--#include file="footer.html" -->
diff --git a/busybox/docs/busybox.net/busybox-growth.ps b/busybox/docs/busybox.net/busybox-growth.ps
new file mode 100644
index 000000000..2379defa4
--- /dev/null
+++ b/busybox/docs/busybox.net/busybox-growth.ps
@@ -0,0 +1,404 @@
1%!PS-Adobe-2.0
2%%Title: busybox-growth.ps
3%%Creator: gnuplot 3.5 (pre 3.6) patchlevel beta 347
4%%CreationDate: Tue Apr 10 14:03:36 2001
5%%DocumentFonts: (atend)
6%%BoundingBox: 50 40 554 770
7%%Orientation: Landscape
8%%Pages: (atend)
9%%EndComments
10/gnudict 120 dict def
11gnudict begin
12/Color true def
13/Solid true def
14/gnulinewidth 5.000 def
15/userlinewidth gnulinewidth def
16/vshift -46 def
17/dl {10 mul} def
18/hpt_ 31.5 def
19/vpt_ 31.5 def
20/hpt hpt_ def
21/vpt vpt_ def
22/M {moveto} bind def
23/L {lineto} bind def
24/R {rmoveto} bind def
25/V {rlineto} bind def
26/vpt2 vpt 2 mul def
27/hpt2 hpt 2 mul def
28/Lshow { currentpoint stroke M
29 0 vshift R show } def
30/Rshow { currentpoint stroke M
31 dup stringwidth pop neg vshift R show } def
32/Cshow { currentpoint stroke M
33 dup stringwidth pop -2 div vshift R show } def
34/UP { dup vpt_ mul /vpt exch def hpt_ mul /hpt exch def
35 /hpt2 hpt 2 mul def /vpt2 vpt 2 mul def } def
36/DL { Color {setrgbcolor Solid {pop []} if 0 setdash }
37 {pop pop pop Solid {pop []} if 0 setdash} ifelse } def
38/BL { stroke gnulinewidth 2 mul setlinewidth } def
39/AL { stroke gnulinewidth 2 div setlinewidth } def
40/UL { gnulinewidth mul /userlinewidth exch def } def
41/PL { stroke userlinewidth setlinewidth } def
42/LTb { BL [] 0 0 0 DL } def
43/LTa { AL [1 dl 2 dl] 0 setdash 0 0 0 setrgbcolor } def
44/LT0 { PL [] 1 0 0 DL } def
45/LT1 { PL [4 dl 2 dl] 0 1 0 DL } def
46/LT2 { PL [2 dl 3 dl] 0 0 1 DL } def
47/LT3 { PL [1 dl 1.5 dl] 1 0 1 DL } def
48/LT4 { PL [5 dl 2 dl 1 dl 2 dl] 0 1 1 DL } def
49/LT5 { PL [4 dl 3 dl 1 dl 3 dl] 1 1 0 DL } def
50/LT6 { PL [2 dl 2 dl 2 dl 4 dl] 0 0 0 DL } def
51/LT7 { PL [2 dl 2 dl 2 dl 2 dl 2 dl 4 dl] 1 0.3 0 DL } def
52/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
53/Pnt { stroke [] 0 setdash
54 gsave 1 setlinecap M 0 0 V stroke grestore } def
55/Dia { stroke [] 0 setdash 2 copy vpt add M
56 hpt neg vpt neg V hpt vpt neg V
57 hpt vpt V hpt neg vpt V closepath stroke
58 Pnt } def
59/Pls { stroke [] 0 setdash vpt sub M 0 vpt2 V
60 currentpoint stroke M
61 hpt neg vpt neg R hpt2 0 V stroke
62 } def
63/Box { stroke [] 0 setdash 2 copy exch hpt sub exch vpt add M
64 0 vpt2 neg V hpt2 0 V 0 vpt2 V
65 hpt2 neg 0 V closepath stroke
66 Pnt } def
67/Crs { stroke [] 0 setdash exch hpt sub exch vpt add M
68 hpt2 vpt2 neg V currentpoint stroke M
69 hpt2 neg 0 R hpt2 vpt2 V stroke } def
70/TriU { stroke [] 0 setdash 2 copy vpt 1.12 mul add M
71 hpt neg vpt -1.62 mul V
72 hpt 2 mul 0 V
73 hpt neg vpt 1.62 mul V closepath stroke
74 Pnt } def
75/Star { 2 copy Pls Crs } def
76/BoxF { stroke [] 0 setdash exch hpt sub exch vpt add M
77 0 vpt2 neg V hpt2 0 V 0 vpt2 V
78 hpt2 neg 0 V closepath fill } def
79/TriUF { stroke [] 0 setdash vpt 1.12 mul add M
80 hpt neg vpt -1.62 mul V
81 hpt 2 mul 0 V
82 hpt neg vpt 1.62 mul V closepath fill } def
83/TriD { stroke [] 0 setdash 2 copy vpt 1.12 mul sub M
84 hpt neg vpt 1.62 mul V
85 hpt 2 mul 0 V
86 hpt neg vpt -1.62 mul V closepath stroke
87 Pnt } def
88/TriDF { stroke [] 0 setdash vpt 1.12 mul sub M
89 hpt neg vpt 1.62 mul V
90 hpt 2 mul 0 V
91 hpt neg vpt -1.62 mul V closepath fill} def
92/DiaF { stroke [] 0 setdash vpt add M
93 hpt neg vpt neg V hpt vpt neg V
94 hpt vpt V hpt neg vpt V closepath fill } def
95/Pent { stroke [] 0 setdash 2 copy gsave
96 translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
97 closepath stroke grestore Pnt } def
98/PentF { stroke [] 0 setdash gsave
99 translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
100 closepath fill grestore } def
101/Circle { stroke [] 0 setdash 2 copy
102 hpt 0 360 arc stroke Pnt } def
103/CircleF { stroke [] 0 setdash hpt 0 360 arc fill } def
104/C0 { BL [] 0 setdash 2 copy moveto vpt 90 450 arc } bind def
105/C1 { BL [] 0 setdash 2 copy moveto
106 2 copy vpt 0 90 arc closepath fill
107 vpt 0 360 arc closepath } bind def
108/C2 { BL [] 0 setdash 2 copy moveto
109 2 copy vpt 90 180 arc closepath fill
110 vpt 0 360 arc closepath } bind def
111/C3 { BL [] 0 setdash 2 copy moveto
112 2 copy vpt 0 180 arc closepath fill
113 vpt 0 360 arc closepath } bind def
114/C4 { BL [] 0 setdash 2 copy moveto
115 2 copy vpt 180 270 arc closepath fill
116 vpt 0 360 arc closepath } bind def
117/C5 { BL [] 0 setdash 2 copy moveto
118 2 copy vpt 0 90 arc
119 2 copy moveto
120 2 copy vpt 180 270 arc closepath fill
121 vpt 0 360 arc } bind def
122/C6 { BL [] 0 setdash 2 copy moveto
123 2 copy vpt 90 270 arc closepath fill
124 vpt 0 360 arc closepath } bind def
125/C7 { BL [] 0 setdash 2 copy moveto
126 2 copy vpt 0 270 arc closepath fill
127 vpt 0 360 arc closepath } bind def
128/C8 { BL [] 0 setdash 2 copy moveto
129 2 copy vpt 270 360 arc closepath fill
130 vpt 0 360 arc closepath } bind def
131/C9 { BL [] 0 setdash 2 copy moveto
132 2 copy vpt 270 450 arc closepath fill
133 vpt 0 360 arc closepath } bind def
134/C10 { BL [] 0 setdash 2 copy 2 copy moveto vpt 270 360 arc closepath fill
135 2 copy moveto
136 2 copy vpt 90 180 arc closepath fill
137 vpt 0 360 arc closepath } bind def
138/C11 { BL [] 0 setdash 2 copy moveto
139 2 copy vpt 0 180 arc closepath fill
140 2 copy moveto
141 2 copy vpt 270 360 arc closepath fill
142 vpt 0 360 arc closepath } bind def
143/C12 { BL [] 0 setdash 2 copy moveto
144 2 copy vpt 180 360 arc closepath fill
145 vpt 0 360 arc closepath } bind def
146/C13 { BL [] 0 setdash 2 copy moveto
147 2 copy vpt 0 90 arc closepath fill
148 2 copy moveto
149 2 copy vpt 180 360 arc closepath fill
150 vpt 0 360 arc closepath } bind def
151/C14 { BL [] 0 setdash 2 copy moveto
152 2 copy vpt 90 360 arc closepath fill
153 vpt 0 360 arc } bind def
154/C15 { BL [] 0 setdash 2 copy vpt 0 360 arc closepath fill
155 vpt 0 360 arc closepath } bind def
156/Rec { newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto
157 neg 0 rlineto closepath } bind def
158/Square { dup Rec } bind def
159/Bsquare { vpt sub exch vpt sub exch vpt2 Square } bind def
160/S0 { BL [] 0 setdash 2 copy moveto 0 vpt rlineto BL Bsquare } bind def
161/S1 { BL [] 0 setdash 2 copy vpt Square fill Bsquare } bind def
162/S2 { BL [] 0 setdash 2 copy exch vpt sub exch vpt Square fill Bsquare } bind def
163/S3 { BL [] 0 setdash 2 copy exch vpt sub exch vpt2 vpt Rec fill Bsquare } bind def
164/S4 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt Square fill Bsquare } bind def
165/S5 { BL [] 0 setdash 2 copy 2 copy vpt Square fill
166 exch vpt sub exch vpt sub vpt Square fill Bsquare } bind def
167/S6 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill Bsquare } bind def
168/S7 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt vpt2 Rec fill
169 2 copy vpt Square fill
170 Bsquare } bind def
171/S8 { BL [] 0 setdash 2 copy vpt sub vpt Square fill Bsquare } bind def
172/S9 { BL [] 0 setdash 2 copy vpt sub vpt vpt2 Rec fill Bsquare } bind def
173/S10 { BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt Square fill
174 Bsquare } bind def
175/S11 { BL [] 0 setdash 2 copy vpt sub vpt Square fill 2 copy exch vpt sub exch vpt2 vpt Rec fill
176 Bsquare } bind def
177/S12 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill Bsquare } bind def
178/S13 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill
179 2 copy vpt Square fill Bsquare } bind def
180/S14 { BL [] 0 setdash 2 copy exch vpt sub exch vpt sub vpt2 vpt Rec fill
181 2 copy exch vpt sub exch vpt Square fill Bsquare } bind def
182/S15 { BL [] 0 setdash 2 copy Bsquare fill Bsquare } bind def
183/D0 { gsave translate 45 rotate 0 0 S0 stroke grestore } bind def
184/D1 { gsave translate 45 rotate 0 0 S1 stroke grestore } bind def
185/D2 { gsave translate 45 rotate 0 0 S2 stroke grestore } bind def
186/D3 { gsave translate 45 rotate 0 0 S3 stroke grestore } bind def
187/D4 { gsave translate 45 rotate 0 0 S4 stroke grestore } bind def
188/D5 { gsave translate 45 rotate 0 0 S5 stroke grestore } bind def
189/D6 { gsave translate 45 rotate 0 0 S6 stroke grestore } bind def
190/D7 { gsave translate 45 rotate 0 0 S7 stroke grestore } bind def
191/D8 { gsave translate 45 rotate 0 0 S8 stroke grestore } bind def
192/D9 { gsave translate 45 rotate 0 0 S9 stroke grestore } bind def
193/D10 { gsave translate 45 rotate 0 0 S10 stroke grestore } bind def
194/D11 { gsave translate 45 rotate 0 0 S11 stroke grestore } bind def
195/D12 { gsave translate 45 rotate 0 0 S12 stroke grestore } bind def
196/D13 { gsave translate 45 rotate 0 0 S13 stroke grestore } bind def
197/D14 { gsave translate 45 rotate 0 0 S14 stroke grestore } bind def
198/D15 { gsave translate 45 rotate 0 0 S15 stroke grestore } bind def
199/DiaE { stroke [] 0 setdash vpt add M
200 hpt neg vpt neg V hpt vpt neg V
201 hpt vpt V hpt neg vpt V closepath stroke } def
202/BoxE { stroke [] 0 setdash exch hpt sub exch vpt add M
203 0 vpt2 neg V hpt2 0 V 0 vpt2 V
204 hpt2 neg 0 V closepath stroke } def
205/TriUE { stroke [] 0 setdash vpt 1.12 mul add M
206 hpt neg vpt -1.62 mul V
207 hpt 2 mul 0 V
208 hpt neg vpt 1.62 mul V closepath stroke } def
209/TriDE { stroke [] 0 setdash vpt 1.12 mul sub M
210 hpt neg vpt 1.62 mul V
211 hpt 2 mul 0 V
212 hpt neg vpt -1.62 mul V closepath stroke } def
213/PentE { stroke [] 0 setdash gsave
214 translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
215 closepath stroke grestore } def
216/CircE { stroke [] 0 setdash
217 hpt 0 360 arc stroke } def
218/Opaque { gsave closepath 1 setgray fill grestore 0 setgray closepath } def
219/DiaW { stroke [] 0 setdash vpt add M
220 hpt neg vpt neg V hpt vpt neg V
221 hpt vpt V hpt neg vpt V Opaque stroke } def
222/BoxW { stroke [] 0 setdash exch hpt sub exch vpt add M
223 0 vpt2 neg V hpt2 0 V 0 vpt2 V
224 hpt2 neg 0 V Opaque stroke } def
225/TriUW { stroke [] 0 setdash vpt 1.12 mul add M
226 hpt neg vpt -1.62 mul V
227 hpt 2 mul 0 V
228 hpt neg vpt 1.62 mul V Opaque stroke } def
229/TriDW { stroke [] 0 setdash vpt 1.12 mul sub M
230 hpt neg vpt 1.62 mul V
231 hpt 2 mul 0 V
232 hpt neg vpt -1.62 mul V Opaque stroke } def
233/PentW { stroke [] 0 setdash gsave
234 translate 0 hpt M 4 {72 rotate 0 hpt L} repeat
235 Opaque stroke grestore } def
236/CircW { stroke [] 0 setdash
237 hpt 0 360 arc Opaque stroke } def
238/BoxFill { gsave Rec 1 setgray fill grestore } def
239end
240%%EndProlog
241%%Page: 1 1
242gnudict begin
243gsave
24450 50 translate
2450.100 0.100 scale
24690 rotate
2470 -5040 translate
2480 setgray
249newpath
250(Helvetica) findfont 140 scalefont setfont
2511.000 UL
252LTb
253560 420 M
25463 0 V
2556409 0 R
256-63 0 V
257476 420 M
258(0) Rshow
259560 1056 M
26063 0 V
2616409 0 R
262-63 0 V
263-6493 0 R
264(100) Rshow
265560 1692 M
26663 0 V
2676409 0 R
268-63 0 V
269-6493 0 R
270(200) Rshow
271560 2328 M
27263 0 V
2736409 0 R
274-63 0 V
275-6493 0 R
276(300) Rshow
277560 2964 M
27863 0 V
2796409 0 R
280-63 0 V
281-6493 0 R
282(400) Rshow
283560 3600 M
28463 0 V
2856409 0 R
286-63 0 V
287-6493 0 R
288(500) Rshow
289560 4236 M
29063 0 V
2916409 0 R
292-63 0 V
293-6493 0 R
294(600) Rshow
295560 4872 M
29663 0 V
2976409 0 R
298-63 0 V
299-6493 0 R
300(700) Rshow
3011531 420 M
3020 63 V
3030 4389 R
3040 -63 V
3050 -4529 R
306(400) Cshow
3072825 420 M
3080 63 V
3090 4389 R
3100 -63 V
3110 -4529 R
312(600) Cshow
3134120 420 M
3140 63 V
3150 4389 R
3160 -63 V
3170 -4529 R
318(800) Cshow
3195414 420 M
3200 63 V
3210 4389 R
3220 -63 V
3230 -4529 R
324(1000) Cshow
3256708 420 M
3260 63 V
3270 4389 R
3280 -63 V
3290 -4529 R
330(1200) Cshow
3311.000 UL
332LTb
333560 420 M
3346472 0 V
3350 4452 V
336-6472 0 V
337560 420 L
3380 2646 M
339currentpoint gsave translate 90 rotate 0 0 M
340(tar.gz size \(Kb\)) Cshow
341grestore
3423796 140 M
343(time \(days since Jan 1, 1998\)) Cshow
3441.000 UL
345LT0
346696 420 M
3470 593 V
3481255 0 V
3490 15 V
350214 0 V
3510 6 V
352958 0 V
3530 1 V
354-84 0 V
3550 37 V
356168 0 V
3570 262 V
35813 0 V
3590 56 V
36091 0 V
3610 33 V
3626 0 V
3630 1 V
36419 0 V
3650 11 V
36620 0 V
3670 13 V
36832 0 V
3690 104 V
37052 0 V
3710 27 V
37265 0 V
3730 15 V
37439 0 V
3750 126 V
376174 0 V
3770 103 V
37852 0 V
3790 49 V
380175 0 V
3810 56 V
382433 0 V
3830 661 V
384415 0 V
3850 857 V
386123 0 V
3870 -291 V
388498 0 V
3890 208 V
390505 0 V
3910 66 V
392291 0 V
3930 115 V
394311 0 V
3950 449 V
396162 0 V
3970 309 V
398stroke
399grestore
400end
401showpage
402%%Trailer
403%%DocumentFonts: Helvetica
404%%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 @@
1
2The code and graphics on this website (and it's mirror sites, if any) are
3Copyright (c) 1999-2004 by Erik Andersen. All rights reserved.
4
5Documents on this Web site including their graphical elements, design, and
6layout are protected by trade dress and other laws and MAY BE COPIED OR
7IMITATED IN WHOLE OR IN PART. THIS WEBSITE IS LICENSED FREE OF CHARGE, THERE
8IS NO WARRANTY FOR THE WEBSITE TO THE EXTENT PERMITTED BY APPLICABLE LAW.
9SHOULD THIS WEBSITE PROVE DEFECTIVE, YOU MAY ASSUME THAT SOMEONE MIGHT GET
10AROUND TO SERVICING, REPAIRING OR CORRECTING IT SOMETIME WHEN THEY HAVE NOTHING
11BETTER TO DO. REGARDLESS, YOU GET TO KEEP BOTH PIECES.
12
13IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
14COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THIS
15WEBSITE AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
16GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
17INABILITY TO USE THIS WEBSITE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
18LOSS OF HAIR, LOSS OF LIFE, LOSS OF MEMORY, LOSS OF YOUR CARKEYS, MISPLACEMENT
19OF YOUR PAYCHECK, OR COMMANDER DATA BEING RENDERED UNABLE TO ASSIST THE
20STARFLEET OFFICERS ABORD THE STARSHIP ENTERPRISE TO RECALIBRATE THE MAIN
21DEFLECTOR ARRAY, LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
22WEBSITE TO OPERATE WITH YOUR WEBBROWSER), EVEN IF SUCH HOLDER OR OTHER PARTY
23HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
24
25You have been warned.
26
27You can contact the webmaster at <andersen@codepoet.org> if you have some sort
28of problem with this.
29
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 @@
1<!--#include file="header.html" -->
2
3
4<h3>Anonymous CVS</h3>
5
6We allow anonymous (read-only) CVS access to everyone. The first command you
7need to run for anonymous CVS access is:
8<pre>
9cvs -d:pserver:anonymous@busybox.net:/var/cvs login</pre>
10<p>
11CVS will prompt you for a password. Just press the Enter key (there is no
12password for anonymous access). This step only needs to be done once, the first
13time you attempt to access CVS.
14<p>
15Once the login is complete, you can then check the list of available
16CVS modules by running the following command (all on one line):
17<pre>
18cvs -z3 -d:pserver:anonymous@busybox.net:/var/cvs co -c </pre>
19
20<p>
21If you wish, you can then check out a local copy of any of the
22available modules. The following is an example of how to grab
23a copy of busybox and tinylogin:
24<pre>
25 cvs -z3 -d:pserver:anonymous@busybox.net:/var/cvs co -P busybox tinylogin</pre>
26This will create a directory called <b>busybox</b> and a directory called
27<b>tinylogin</b> in the current directory. These directories contain the
28latest and greatest source code for busybox and tinylogin.
29
30<p>
31If you are not already familiar with using CVS, I recommend you visit
32this quick <a href="/cvs_howto.html">Introduction to CVS</a>.
33
34<p>
35I usually create a ~/.cvsrc file with the following things in it, and I
36recommend you should use the same:
37<pre>
38 -z3
39 update -dP
40 rdiff -u
41 diff -ubBwpN
42 checkout -P</pre>
43
44<p>
45Once you've checked out a copy of the source tree, you can update your
46source tree at any time so it is in sync with the latest and greatest by
47running the command:
48<pre>
49cvs update</pre>
50
51Because you've only been granted anonymous access to the tree, you won't be
52able to commit any changes. Changes can be submitted for inclusion by posting
53them to the appropriate mailing list. For those that are actively contributing
54<a href="cvs_write.html">CVS write access</a> can be made available.
55
56<!--#include file="footer.html" -->
57
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 @@
1<!--#include file="header.html" -->
2
3
4<h3>How to use CVS</h3>
5
6
7If you want to know all the gory details, you will want to visit
8<a href="http://www.cvshome.org/">the CVS main web page</a>.<p>
9For the impatient, the following is probably about all you need to know:
10<p>
11
12<dl>
13 <dt><pre>cvs checkout -c</pre>
14 <dd>Will list the modules available for checkout
15 <dt><pre>cvs checkout &lt module name &gt</pre>
16 <dd>Will checkout the named module
17 <dt><pre>cvs co &lt module name &gt</pre>
18 <dd>Same thing
19 <dt><pre>cvs update</pre>
20
21 <dd>Updates your local archive so it is in sync with the repository
22 -- your local updates are left intact. Tries to merge upstream updates
23 into your local updates. You will see the following tags when it is
24 updating your local repository: C means conflict, U means update,
25 P means patched, and M means modified.
26 <dt><pre>cvs up</pre>
27 <dd>Same thing
28 <dt><pre>cvs update &lt file name &gt</pre>
29 <dd>Same thing but for just the named file(s)/directory(s).
30 <dt><pre>cvs commit</pre>
31 <dd>Will check in all your work.
32 <dt><pre>cvs add &lt file name &gt</pre>
33
34 <dd>Adds the named file/directory into CVS
35 <dt><pre>cvs remove &lt file name &gt</pre>
36 <dd>Removes the named file/directory from the upstream repository.
37 <dt><pre>cvs rm &lt file name &gt</pre>
38 <dd>Same thing
39 <dt><pre>cvs log &lt file name &gt</pre>
40</dl>
41
42
43<!--#include file="footer.html" -->
44
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 @@
1<!--#include file="header.html" -->
2
3
4<h3>CVS Read/Write Access</h3>
5
6If you want to be able to commit things to CVS, first contribute some
7stuff to show you are serious. Then, very nicely ask
8<a href="mailto:andersen@codepoet.org">Erik Andersen</a> if he will set you up with
9an account. To access CVS, you will want to add the following to set up your environment:
10<pre>
11$ export CVS_RSH=/usr/bin/ssh
12$ export CVSROOT='username@cvs.busybox.net:/var/cvs'</pre>
13<br>
14It goes without saying you must change <em>username</em> to your own
15username...
16<p>
17
18To obtain commit access, you will need to demonstrate you are
19serious by submitting a few good patches first. Then, you will need to
20select a user-name to use when committing stuff, and finally, you will
21need to send me the username you have selected, an ssh key, and the email
22address where you prefer email to be sent (I will forward any email sent
23to you, but not store it).
24
25<p>
26Note that if you would prefer to keep your communications with me
27private, you can encrypt your email using my
28<a href="http://www.codepoet.org/andersen/erik/gpg.asc">public key</a>.
29
30<!--#include file="footer.html" -->
31
32
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 @@
1<!--#include file="header.html" -->
2
3
4<h3>Documentation</h3>
5Current documentation for BusyBox includes:
6
7<ul>
8 <li><a href=
9 "downloads/BusyBox.html">BusyBox.html</a>. This is a
10 list of the all the available commands in BusyBox
11 with complete usage information and examples of how
12 to use each app. I have spent a <em>lot</em> of time
13 updating these docs and trying to make them fairly
14 comprehensive. If you find any errors (factual,
15 grammatical, whatever) please let me know.</li>
16
17 <li><a href="downloads/README">README</a>. This is
18 the README file included in the busybox source
19 release.</li>
20
21 <li>If you need more help, the BusyBox <a href=
22 "lists/busybox/">mailing list</a> is a good place to
23 start.</li>
24</ul>
25
26<!--#include file="footer.html" -->
27
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 @@
1<!--#include file="header.html" -->
2
3
4
5<h3>Download</h3>
6
7Source for the latest release can always be
8downloaded from <a href="downloads">http://www.busybox.net/downloads</a>.
9
10<p>
11You can also obtain <a href= "downloads/snapshots/">Daily Snapshots</a> of
12the latest stable, and the latest development CVS source trees.
13
14<p>
15BusyBox now has <b>two</b> CVS trees. The "busybox-stable" tree
16contains the older 0.60.x stable series. The "busybox" tree contains
17the latest 1.0.0-preX development version of busybox.<br>
18
19<ul>
20 <li> Click here to browse the <a href="/cgi-bin/cvsweb/busybox/">
21 CVS tree for the 1.0.0-preX development version of BusyBox</a>
22 </li>
23
24 <li>Click here to browse the <a href="/cgi-bin/cvsweb/busybox.stable/">
25 CVS tree for the stable 0.60.x version of BusyBox</a>.
26 </li>
27
28 <li>Anonymous <a href="cvs_anon.html">CVS access</a> is available.
29 </li>
30
31 <li>For those that are actively contributing there is
32 even <a href="cvs_write.html">CVS write access</a>.
33 </li>
34
35</ul>
36
37<!--#include file="footer.html" -->
38
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 @@
1<!-- Footer -->
2
3
4 </td>
5 </tr>
6 </table>
7
8<hr />
9
10 <p>
11 <font face="arial, helvetica, sans-serif" size="-1">
12 <a HREF="/copyright.txt">Copyright &copy; 1999-2003 Erik Andersen</a>
13 <br>
14 Mail all comments, insults, suggestions and bribes to
15 <br>
16 Erik Andersen <A HREF="mailto:andersen@codepoet.org">andersen@codepoet.org</A><BR>
17 </font>
18
19 </body>
20</html>
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 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
2"http://www.w3.org/TR/REC-html40/loose.dtd">
3
4<html>
5 <head>
6 <title>BusyBox</title>
7 <style type="text/css">
8 body {
9 background-color: #DEE2DE;
10 color: #000000;
11 }
12 :link { color: #660000 }
13 :visited { color: #660000 }
14 :active { color: #660000 }
15 td.c2 {font-family: arial, helvetica, sans-serif; font-size: 80%}
16 td.c1 {font-family: lucida, helvetica; font-size: 248%}
17 </style>
18 </head>
19
20 <body>
21 <basefont face="lucida, helvetica, arial" size="3">
22
23
24
25
26<table border="0" cellpadding="0" cellspacing="0">
27
28
29<tr>
30<td>
31 <div class="c3">
32 <table border="0" cellspacing="1" cellpadding="2">
33 <tr>
34 <td class="c1">BUSYBOX</td>
35 </tr>
36 </table>
37 </div>
38
39 <a href="/"><IMG SRC="images/busybox1.png" alt="BusyBox" border="0"></a><BR>
40</td>
41</tr>
42
43<tr>
44
45<td valign="TOP">
46 <br><a href="/about.html">About</a>
47 <br><a href="/screenshot.html">Screenshot</a>
48 <br><a href="/lists.html">Mailing Lists</a>
49 <br><a href="/news.html">Latest News</a>
50 <br><a href="/download.html">Download</a>
51 <br><a href="/FAQ.html">FAQ</a>
52 <br><a href="/cvs_anon.html">Accessing CVS</a>
53 <br><a href="/cgi-bin/cvsweb/busybox/">Browse CVS</a>
54 <br><a href="/docs.html">Documentation</a>
55 <br><a href="/products.html">Products</a>
56 <br><a href="/shame.html">Hall of Shame</a>
57 <br><a href="/license.html">License</a>
58
59 <p><b>Related Sites</b>
60 <br><a href="http://uclibc.org/">uClibc.org</a>
61 <br><a href="http://udhcp.busybox.net/">udhcp</a>
62 <br><a href="http://tinylogin.busybox.net/">tinylogin</a>
63 <br><a href="http://www.ucdot.org/">uCdot</a>
64 <br><a href="http://www.linuxdevices.com">LinuxDevices</a>
65 <br><a href="http://slashdot.org/">Slashdot</a>
66 <br><a href="http://freshmeat.net/">Freshmeat</a>
67 <br><a href="http://linuxtoday.com/">Linux Today</a>
68 <br><a href="http://lwn.net/">Linux Weekly News</a>
69 <br><a href="http://www.tldp.org/HOWTO">Linux HOWTOs</a>
70
71<!--
72 <a href="http://validator.w3.org/check/referer"><img
73 src="/images/vh40.gif" height=31 width=88
74 align=left border=0 alt="Valid HTML 4.0!"></a>
75-->
76
77</td>
78
79
80<td Valign="TOP">
81
diff --git a/busybox/docs/busybox.net/images/back.png b/busybox/docs/busybox.net/images/back.png
new file mode 100644
index 000000000..79923869b
--- /dev/null
+++ b/busybox/docs/busybox.net/images/back.png
Binary files differ
diff --git a/busybox/docs/busybox.net/images/busybox.jpeg b/busybox/docs/busybox.net/images/busybox.jpeg
new file mode 100644
index 000000000..37edc9614
--- /dev/null
+++ b/busybox/docs/busybox.net/images/busybox.jpeg
Binary files differ
diff --git a/busybox/docs/busybox.net/images/busybox.png b/busybox/docs/busybox.net/images/busybox.png
new file mode 100644
index 000000000..b1eb92f38
--- /dev/null
+++ b/busybox/docs/busybox.net/images/busybox.png
Binary files differ
diff --git a/busybox/docs/busybox.net/images/busybox1.png b/busybox/docs/busybox.net/images/busybox1.png
new file mode 100644
index 000000000..4d3126a52
--- /dev/null
+++ b/busybox/docs/busybox.net/images/busybox1.png
Binary files differ
diff --git a/busybox/docs/busybox.net/images/busybox2.jpg b/busybox/docs/busybox.net/images/busybox2.jpg
new file mode 100644
index 000000000..abf8f0610
--- /dev/null
+++ b/busybox/docs/busybox.net/images/busybox2.jpg
Binary files differ
diff --git a/busybox/docs/busybox.net/images/busybox3.jpg b/busybox/docs/busybox.net/images/busybox3.jpg
new file mode 100644
index 000000000..0fab84cf9
--- /dev/null
+++ b/busybox/docs/busybox.net/images/busybox3.jpg
Binary files differ
diff --git a/busybox/docs/busybox.net/images/dir.png b/busybox/docs/busybox.net/images/dir.png
new file mode 100644
index 000000000..1d633ce4a
--- /dev/null
+++ b/busybox/docs/busybox.net/images/dir.png
Binary files differ
diff --git a/busybox/docs/busybox.net/images/donate.png b/busybox/docs/busybox.net/images/donate.png
new file mode 100644
index 000000000..b55621bb9
--- /dev/null
+++ b/busybox/docs/busybox.net/images/donate.png
Binary files differ
diff --git a/busybox/docs/busybox.net/images/fm.mini.png b/busybox/docs/busybox.net/images/fm.mini.png
new file mode 100644
index 000000000..c0883cd34
--- /dev/null
+++ b/busybox/docs/busybox.net/images/fm.mini.png
Binary files differ
diff --git a/busybox/docs/busybox.net/images/gfx_by_gimp.png b/busybox/docs/busybox.net/images/gfx_by_gimp.png
new file mode 100644
index 000000000..d58314034
--- /dev/null
+++ b/busybox/docs/busybox.net/images/gfx_by_gimp.png
Binary files differ
diff --git a/busybox/docs/busybox.net/images/ltbutton2.png b/busybox/docs/busybox.net/images/ltbutton2.png
new file mode 100644
index 000000000..9bad9496a
--- /dev/null
+++ b/busybox/docs/busybox.net/images/ltbutton2.png
Binary files differ
diff --git a/busybox/docs/busybox.net/images/sdsmall.png b/busybox/docs/busybox.net/images/sdsmall.png
new file mode 100644
index 000000000..b1024501b
--- /dev/null
+++ b/busybox/docs/busybox.net/images/sdsmall.png
Binary files differ
diff --git a/busybox/docs/busybox.net/images/text.png b/busybox/docs/busybox.net/images/text.png
new file mode 100644
index 000000000..6034f899f
--- /dev/null
+++ b/busybox/docs/busybox.net/images/text.png
Binary files differ
diff --git a/busybox/docs/busybox.net/images/vh40.gif b/busybox/docs/busybox.net/images/vh40.gif
new file mode 100644
index 000000000..c5e9402e7
--- /dev/null
+++ b/busybox/docs/busybox.net/images/vh40.gif
Binary files differ
diff --git a/busybox/docs/busybox.net/images/written.in.vi.png b/busybox/docs/busybox.net/images/written.in.vi.png
new file mode 100644
index 000000000..84f59bc15
--- /dev/null
+++ b/busybox/docs/busybox.net/images/written.in.vi.png
Binary files differ
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 @@
<!--#include file="news.html" -->
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 @@
1<!--#include file="header.html" -->
2
3
4<h3>The GPL BusyBox license</h3>
5
6There has been some confusion in the past as to exactly what is
7required to safely distribute GPL'd software such as BusyBox as
8part of a product. To ensure that there is no confusion
9whatsoever, this page attempts to summarize what you should do to
10ensure you do not accidentally violate the law.
11
12<p>
13<h3>Complying with the BusyBox license is easy and completely free.</h3>
14
15U.S. and International Law protects copyright owners from the unauthorized
16reproduction, adaptation, display, distribution, etc of copyright protected
17works. Copyright violations (such as shipping BusyBox in a manner contrary to
18its license) are subject to severe penalties. The courts can award up to
19$150,000 per product shipped without even showing any actual loss by the
20copyright holder. Criminal penalties are available for intentional acts
21undertaken for purposes of "commercial advantage" or "private financial gain."
22In addition, if it comes to my attention that you are violating the BusyBox
23license, I will list you on the <a href="/shame.html">BusyBox Hall of Shame</a>
24webpage.
25
26<p>
27
28Nobody wants that to happen. Do everyone a favor and don't break the law -- if
29you use BusyBox, you <b>must comply with the BusyBox license</b>.
30
31<p>
32<h3>BusyBox is licensed under the GNU General Public License</h3>
33
34BusyBox is licensed under the GNU General Public License , which
35is generally just abbreviated as the GPL license, or
36just the GPL.
37<p>
38<a href="/products.html">Anyone thinking of shipping
39BusyBox as part of a product</a> should be familiar with the
40licensing terms under which they are allowed to use and
41distribute BusyBox. You are advised to take a look over the
42
43<ul>
44<li><a href="http://www.gnu.org/licenses/gpl.html">full text of
45the GNU General Public License</a>, and
46<li><a href="http://www.gnu.org/licenses/gpl-faq.html">
47Frequently Asked Questions about the GNU GPL</a>
48</ul>
49to be sure you (and your lawyers) fully understand them.
50
51<p>
52
53The following is a quick summary for the impatient. If you
54carefully follow these steps, it will ensure that you are 100%
55authorized to ship BusyBox with your product, and have no reason
56to worry about lawsuits or being listed on the <a
57href="/shame.html">BusyBox Hall of Shame</a> page. You will be
58able to sleep peacefully at night knowing you have fulfilled all
59your licensing obligations.
60
61<p>
62
63If you distribute a product, it should either be accompanied by
64<b>full source for all GPL'd products</b> (including BusyBox)
65and/or a <b>written offer</b> to supply the source for all
66GPL'd products for the cost of shipping and handling. The source
67has to be in its preferred machine readable form, so you cannot
68encrypt or obfuscate it. You are not required to provide full
69source for all the closed source applications that happen to be
70part of the system with BusyBox, though you can certainly do so
71if you feel like it. But providing source for the GPL licensed
72applications such as BusyBox is mandatory.
73
74<p>
75
76<b>Accompanied by source</b> generally means you distribute the full
77source code for all GPL'd products including BusyBox along with your
78product, such as by placing it somewhere on a driver CD. Full source
79code includes the BusyBox ".config" file used when your shipping BusyBox
80binary was compiled, and any and all modifications you made to the
81BusyBox source code.
82
83<p>
84
85<b>A written offer</b> generally means that somewhere in the
86documentation for your product, you write something like
87
88<blockquote>
89The GPL source code contained in this product is available as a
90free download from http://blah.blah.blah/
91</blockquote>
92Alternatively, you can offer the source code by writing
93somewhere in the documentation for your product something like
94<blockquote>
95If you would like a copy of the GPL source code contained in this
96product shipped to you on CD, please send $9.99 to &lt;address&gt;
97which covers the cost of preparing and mailing a CD to you.
98</blockquote>
99<p>
100
101Keep in mind though that if you distribute GPL'd binaries online (as is often
102done when supplying firmware updates), it is <b>highly</b> recommended that you
103make the corresponding source available online at the same place. Regardless,
104if you distribute a binary copy of BusyBox online (such as part of a firmware
105update) you <b>must</b> either make source available online (i.e.
106<b>accompanied by source</b>) and/or inform those downloading firmware updates
107of their right to obtain source (i.e. <b>a written offer</b>). Failure to do
108so is a violation of your licensing obligations.
109
110
111<p>
112
113Some people have the mistaken understanding that if they use unmodified
114GPL'd source code, they do not need to distribute anything. This belief
115is not correct, and is not supported by the
116<a href="http://www.gnu.org/licenses/gpl.html">text of GPL</a>.
117Please do re-read it -- you will find there is no such provision.
118If you distribute any GPL'd binaries, you must also make source available
119as discussed on this webpage.
120
121<p>
122<h3>A Good Example</h3>
123
124These days, <a href="http://www.linksys.com/">Linksys</a> is
125doing a good job at complying with the GPL, they get to be an
126example of how to do things right. Please take a moment and
127check out what they do with
128<a href="http://www.linksys.com/download/firmware.asp?fwid=178">
129distributing the firmware for their WRT54G Router.</a>
130Following their example would be a fine way to ensure that you
131have also fulfilled your licensing obligations.
132
133
134<!--#include file="footer.html" -->
135
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 @@
1<!--#include file="header.html" -->
2
3
4<!-- Begin Introduction section -->
5
6<h3>Mailing List Information</h3>
7BusyBox has a <a href="/lists/busybox/">mailing list</a> for discussion and
8development. You can subscribe by visiting
9<a href="http://codepoet.org/mailman/listinfo/busybox">this page</a>.
10Only subscribers to the BusyBox mailing list are allowed to post
11to this list.
12
13<p>
14There is also a mailing list for <a href="/lists/busybox-cvs/">active developers</a>
15wishing to read the complete diff of each and every change to busybox -- not for the
16faint of heart. Active developers can subscribe by visiting
17<a href="http://codepoet.org/mailman/listinfo/busybox-cvs">this page</a>.
18The CVS server is the only one permtted to post to this list.
19
20<p>
21
22
23<h3>Search the List Archives</h3>
24Please search the mailing list archives before asking questions on the mailing
25list, since there is a good chance someone else has asked the same question
26before. Checking the archives is a great way to avoid annoying everyone on the
27list with frequently asked questions...
28<p>
29
30<center>
31<form method="GET" action="http://www.google.com/custom">
32<input type="hidden" name="domains" value="busybox.net">
33<input type="hidden" name="sitesearch" value="busybox.net">
34<input type="text" name="q" size="31" maxlength="255" value="">
35<br>
36<input type="submit" name="sa" value="search the mailing list archives">
37<br>
38<a href="http://www.google.com"><img src="http://www.google.com/logos/Logo_25wht.gif" border="0" alt="Google" height="32" width="75" align="middle"></a>
39<br>
40</form>
41</center>
42
43
44
45<!--#include file="footer.html" -->
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 @@
1<!--#include file="header.html" -->
2
3
4<ul>
5
6 <li><b>13 October 2004 -- BusyBox 1.00 released</b><p>
7
8 When you take a careful look at nearly every embedded Linux device or
9 software distribution shipping today, you will find a copy of BusyBox.
10 With countless routers, set top boxes, wireless access points, PDAs, and
11 who knows what else, the future for Linux and BusyBox on embedded devices
12 is looking very bright.
13
14 <p>
15
16 It is therefore with great satisfaction that I declare each and every
17 device already shipping with BusyBox is now officially out of date.
18 The highly anticipated release of BusyBox 1.00 has arrived!
19
20 <p>
21
22 Over three years in development, BusyBox 1.00 represents a tremendous
23 improvement over the old 0.60.x stable series. Now featuring a Linux
24 KernelConf based configuration system (as used by the Linux kernel),
25 Linux 2.6 kernel support, many many new applets, and the development
26 work and testing of thousands of people from around the world.
27
28 <p>
29
30 If you are already using BusyBox, you are strongly encouraged to upgrade to
31 BusyBox 1.00. If you are considering developing an embedded Linux device
32 or software distribution, you may wish to investigate if using BusyBox is
33 right for your application. If you need help getting started using
34 BusyBox, if you wish to donate to help cover expenses, or if you find a bug
35 and need help reporting it, you are invited to visit the <a
36 href="FAQ.html">BusyBox FAQ</a>.
37
38 <p>
39
40 As usual you can <a href="downloads">download busybox here</a>.
41
42 <p>Have Fun!
43
44 <p>
45 <li><b>Old News</b><p>
46 <a href="/oldnews.html">Click here to read older news</a>
47
48
49</ul>
50
51<!--#include file="footer.html" -->
52
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 @@
1<!--#include file="header.html" -->
2
3
4<ul>
5
6 <li><b>16 August 2004 -- BusyBox 1.0.0-rc3 released</b><p>
7
8 Here goes release candidate 3...
9 <p>
10 The <a href="downloads/Changelog">changelog</a> has all the details.
11 And as usual you can <a href="downloads">download busybox here</a>.
12
13 <p>Have Fun!
14
15 <p>
16 <li><b>26 July 2004 -- BusyBox 1.0.0-rc2 released</b><p>
17
18 Here goes release candidate 2...
19 <p>
20 The <a href="downloads/Changelog">changelog</a> has all the details.
21 And as usual you can <a href="downloads">download busybox here</a>.
22
23 <p>Have Fun!
24
25 <p>
26 <li><b>20 July 2004 -- BusyBox 1.0.0-rc1 released</b><p>
27
28 Here goes release candidate 1... This fixes all (most?) of the problems
29 that have turned up since -pre10. In particular, loading and unloading of
30 kernel modules with 2.6.x kernels should be working much better.
31 <p>
32
33 I <b>really</b> want to get BusyBox 1.0.0 released soon and I see no real
34 reason why the 1.0.0 release shouldn't happen with things pretty much as
35 is. BusyBox is in good shape at the moment, and it works nicely for
36 everything that I'm doing with it. And from the reports I've been getting,
37 it works nicely for what most everyone else is doing with it as well.
38 There will eventually be a 1.0.1 anyway, so we might as well get on with
39 it. No, BusyBox is not perfect. No piece of software ever is. And while
40 there is still plenty that can be done to improve things, most of that work
41 is waiting till we can get a solid 1.0.0 release out the door....
42 <p>
43
44 Please do not bother to send in patches adding cool new features at this
45 time. Only bug-fix patches will be accepted. If you have submitted a
46 bug-fixing patch to the busybox mailing list and no one has emailed you
47 explaining why your patch was rejected, it is safe to say that your patch
48 has been lost or forgotten. That happens sometimes. Please re-submit your
49 bug-fixing patch to the BusyBox mailing list, and be sure to put "[PATCH]"
50 at the beginning of the email subject line!
51
52 <p>
53 The <a href="downloads/Changelog">changelog</a> has all the details.
54 And as usual you can <a href="downloads">download busybox here</a>.
55
56 <p>Have Fun!
57
58 <p>
59 On a less happy note, My 92 year old grandmother (my dad's mom) passed away
60 yesterday (June 19th). The funeral will be Thursday in a little town about
61 2 hours south of my home. I've checked and there is absolutely no way I
62 could be back in time for the funeral if I attend <a
63 href="http://www.linuxsymposium.org/2004/">OLS</a> and give my presentation
64 as scheduled.
65 <p>
66 As such, it is with great reluctance and sadness that I have come
67 to the conclusion I will have to make my appologies and skip OLS
68 this year.
69 <p>
70
71
72 <p>
73 <li><b>13 April 2004 -- BusyBox 1.0.0-pre10 released</b><p>
74
75 Ok, I lied. It turns out that -pre9 will not be the final BusyBox
76 pre-release. With any luck however -pre10 will be, since I <b>really</b>
77 want to get BusyBox 1.0.0 released very soon. As usual, please do not
78 bother to send in patches adding cool new features at this time. Only
79 bug-fix patches will be accepted. It would also be <b>very</b> helpful if
80 people could continue to review the BusyBox documentation and submit
81 improvements.
82
83 <p>
84 The <a href="downloads/Changelog">changelog</a> has all the details.
85 And as usual you can <a href="downloads">download busybox here</a>.
86
87 <p>Have Fun!
88 <p>
89
90
91 <p>
92 <li><b>6 April 2004 -- BusyBox 1.0.0-pre9 released</b><p>
93
94 Here goes the final BusyBox pre-release... This is your last chance for
95 bug fixes. With luck this will be released as BusyBox 1.0.0 later this
96 week. Please do not bother to send in patches adding cool new features at
97 this time. Only bug-fix patches will be accepted. It would also be
98 <b>very</b> helpful if people could help review the BusyBox documentation
99 and submit improvements. I've spent a lot of time updating the
100 documentation to make it better match reality, but I could really use some
101 assistance in checking that the features supported by the various applets
102 match the features listed in the documentation.
103
104 <p>
105 I had hoped to get this released a month ago, but
106 <a href="http://codepoet.org/gallery/baby_peter/img_1796">
107 another release on 1 March 2004</a> has kept me busy...
108
109 <p>
110 The <a href="downloads/Changelog">changelog</a> has all the details.
111 And as usual you can <a href="downloads">download busybox here</a>.
112
113 <p>Have Fun!
114 <p>
115
116
117 <p>
118 <li><b>23 February 2004 -- BusyBox 1.0.0-pre8 released</b><p>
119
120 Here goes yet another BusyBox pre-release... Please do not bother to send
121 in patches supplying new features at this time. Only bug-fix patches will
122 be accepted. If you have a cool new feature you would like to see
123 supported, or if you have an amazing new applet you would like to submit,
124 please wait and submit such things later. We really want to get a release
125 out we can all be proud of. We are still aiming to finish off the -pre
126 series in February and move on to the final 1.0.0 release... So if you
127 spot any bugs, now would be an excellent time to send in a fix to the
128 busybox mailing list. It would also be <b>very</b> helpful if people could
129 help review the BusyBox documentation and submit improvements. It would be
130 especially helpful if people could check that the features supported by the
131 various applets match the features listed in the documentation.
132
133 <p>
134
135 The <a href="downloads/Changelog">changelog</a> has all the details.
136 And as usual you can <a href="downloads">download busybox here</a>.
137
138 <p>Have Fun!
139 <p>
140
141
142 <li><b>4 February 2004 -- BusyBox 1.0.0-pre7 released</b><p>
143
144 There was a bug in -pre6 that broke argument parsing for a
145 number of applets, since a variable was not being zeroed out
146 properly. This release is primarily intended to fix that one
147 problem. In addition, this release fixes several other
148 problems, including a rewrite by mjn3 of the code for parsing
149 the busybox.conf file used for suid handling, some shell updates
150 from vodz, and a scattering of other small fixes. We are still
151 aiming to finish off the -pre series in February and move on to
152 the final 1.0.0 release... If you see any problems, of have
153 suggestions to make, as always, please feel free to email the
154 busybox mailing list.
155
156 <p>
157
158 The <a href="downloads/Changelog">changelog</a> has all
159 the details. And as usual you can
160 <a href="downloads">download busybox here</a>.
161
162 <p>Have Fun!
163 <p>
164
165
166 <p>
167 <li><b>30 January 2004 -- BusyBox 1.0.0-pre6 released</b><p>
168
169 Here goes the next pre-release for the new BusyBox stable
170 series. This release adds a number of size optimizations,
171 updates udhcp, fixes up 2.6 modutils support, updates ash
172 and the shell command line editing, and the usual pile of
173 bug fixes both large and small. Things appear to be
174 settling down now, so with a bit of luck and some testing
175 perhaps we can finish off the -pre series in February and
176 move on to the final 1.0.0 release... If you see any
177 problems, of have suggestions to make, as always, please
178 feel free to email the busybox mailing list.
179
180 <p>
181
182 People who rely on the <a href= "downloads/snapshots/">daily BusyBox snapshots</a>
183 should be aware that snapshots of the old busybox 0.60.x
184 series are no longer available. Daily snapshots are now
185 only available for the BusyBox 1.0.0 series and now use
186 the naming scheme "busybox-&lt;date&gt;.tar.bz2". Please
187 adjust any build scripts using the old naming scheme accordingly.
188
189 <p>
190
191 The <a href="downloads/Changelog">changelog</a> has all
192 the details. And as usual you can
193 <a href="downloads">download busybox here</a>.
194
195 <p>Have Fun!
196 <p>
197
198
199 <p>
200 <li><b>23 December 2003 -- BusyBox 1.0.0-pre5 released</b><p>
201
202 Here goes the next pre-release for the new BusyBox stable
203 series. The most obvious thing in this release is a fix for
204 a terribly stupid bug in mount that prevented it from working
205 properly unless you specified the filesystem type. This
206 release also fixes a few compile problems, updates udhcp,
207 fixes a silly bug in fdisk, fixes ifup/ifdown to behave like
208 the Debian version, updates devfsd, updates the 2.6.x
209 modutils support, add a new 'rx' applet, removes the obsolete
210 'loadacm' applet, fixes a few tar bugs, fixes a sed bug, and
211 a few other odd fixes.
212
213 <p>
214
215 If you see any problems, of have suggestions to make, as
216 always, please feel free to send an email to the busybox
217 mailing list.
218
219 <p>
220
221 The <a href="downloads/Changelog">changelog</a> has all
222 the details. And as usual you can
223 <a href="downloads">download busybox here</a>.
224
225 <p>Have Fun!
226 <p>
227
228
229
230 <li><b>10 December 2003 -- BusyBox 1.0.0-pre4 released</b><p>
231
232 Here goes the fourth pre-release for the new BusyBox stable
233 series. This release includes major rework to sed, lots of
234 rework on tar, a new tiny implementation of bunzip2, a new
235 devfsd applet, support for 2.6.x kernel modules, updates to
236 the ash shell, sha1sum and md5sum have been merged into a
237 common applet, the dpkg applets has been cleaned up, and tons
238 of random bugs have been fixed. Thanks everyone for all the
239 testing, bug reports, and patches! Once again, a big
240 thank-you goes to Glenn McGrath (bug1) for stepping in and
241 helping get patches merged!
242
243 <p>
244
245 And of course, if you are reading this, you might have noticed
246 the busybox website has been completely reworked. Hopefully
247 things are now somewhat easier to navigate... If you see any
248 problems, of have suggestions to make, as always, please feel
249 free to send an email to the busybox mailing list.
250
251 <p>
252
253 The <a href="downloads/Changelog">changelog</a> has all
254 the details. And as usual you can
255 <a href="downloads">download busybox here</a>.
256
257 <p>Have Fun!
258
259
260
261 <p>
262 <li><b>12 Sept 2003 -- BusyBox 1.0.0-pre3 released</b><p>
263
264 Here goes the third pre-release for the new BusyBox stable
265 series. The last prerelease has held up quite well under
266 testing, but a number of problems have turned up as the number
267 of people using it has increased. Thanks everyone for all
268 the testing, bug reports, and patches!
269
270 <p>
271
272 If you have submitted a patch or a bug report to the busybox
273 mailing list and no one has emailed you explaining why your
274 patch was rejected, it is safe to say that your patch has
275 somehow gotten lost or forgotten. That happens sometimes.
276 Please re-submit your patch or bug report to the BusyBox
277 mailing list!
278
279 <p>
280
281 The point of the "-preX" versions is to get a larger group of
282 people and vendors testing, so any problems that turn up can be
283 fixed prior to the final 1.0.0 release. The main feature
284 (besides additional testing) that is still still on the TODO
285 list before the final BusyBox 1.0.0 release is sorting out the
286 modutils issues. For the new 2.6.x kernels, we already have
287 patches adding insmod and rmmod support and those need to be
288 integrated. For 2.4.x kernels, for which busybox only supports
289 a limited number of architectures, we may want to invest a bit
290 more work before we cut 1.0.0. Or we may just leave 2.4.x
291 module loading alone.
292
293 <p>
294
295 I had hoped this release would be out a month ago. And of
296 course, it wasn't since Erik became busy getting a release of
297 <a href="http://www.uclibc.org/">uClibc</a>
298 out the door. Many thanks to Glenn McGrath (bug1) for
299 stepping in and helping get a bunch of patches merged! I am
300 not even going to state a date for releasing BusyBox 1.0.0
301 -pre4 (or the final 1.0.0). We're aiming for late September...
302 But if this release proves as to be exceptionally stable (or
303 exceptionally unstable!), the next release may be very soon
304 indeed.
305
306 <p>
307
308 The <a href="downloads/Changelog">changelog</a> has all
309 the details. And as usual you can
310 <a href="downloads">download busybox here</a>.
311
312 <p>Have Fun!
313
314
315 <p>
316 <li><b>30 July 2003 -- BusyBox 1.0.0-pre2 released</b><p>
317
318 Here goes another pre release for the new BusyBox stable
319 series. The last prerelease (pre1) was given quite a lot of
320 testing (thanks everyone!) which has helped turn up a number of
321 bugs, and these problems have now been fixed.
322
323 <p>
324
325 Highlights of -pre2 include updating the 'ash' shell to sync up
326 with the Debian 'dash' shell, a new 'hdparm' applet was added,
327 init again supports pivot_root, The 'reboot' 'halt' and
328 'poweroff' applets can now be used without using busybox init.
329 an ifconfig buffer overflow was fixed, losetup now allows
330 read-write loop devices, uClinux daemon support was added, the
331 'watchdog', 'fdisk', and 'kill' applets were rewritten, there were
332 tons of doc updates, and there were many other bugs fixed.
333 <p>
334
335 If you have submitted a patch and it is not included in this
336 release and Erik has not emailed you explaining why your patch
337 was rejected, it is safe to say that he has lost your patch.
338 That happens sometimes. Please re-submit your patch to the
339 BusyBox mailing list.
340 <p>
341
342 The point of the "-preX" versions is to get a larger group of
343 people and vendors testing, so any problems that turn up can be
344 fixed prior to the final 1.0.0 release. The main feature that
345 is still still on the TODO list before the final BusyBox 1.0.0
346 release is adding module support for the new 2.6.x kernels. If
347 necessary, a -pre3 BusyBox release will happen on August 6th.
348 Hopefully (i.e. unless some horrible catastrophic problem
349 turns up) the final BusyBox 1.0.0 release will be ready by
350 then...
351 <p>
352
353 The <a href="downloads/Changelog">changelog</a> has all
354 the details. As usual you can <a href="downloads">download busybox here</a>.
355
356 <p>Have Fun!
357 <p>
358
359 <p>
360 <li><b>15 July 2003 -- BusyBox 1.0.0-pre1 released</b><p>
361
362 The busybox development series has been under construction for
363 nearly two years now. Which is just entirely too long... So
364 it is with great pleasure that I announce the imminent release
365 of a new stable series. Due to the huge number of changes
366 since the last stable release (and the usual mindless version
367 number inflation) I am branding this new stable series verison
368 1.0.x...
369 <p>
370
371 The point of "-preX" versions is to get a larger group of
372 people and vendors testing, so any problems that turn up can be
373 fixed prior to the magic 1.0.0 release (which should happen
374 later this month)... I plan to release BusyBox 1.0.0-pre2 next
375 Monday (July 21st), and, if necessary, -pre3 on July 28th.
376 Hopefully (i.e. unless some horrible catastrophic problem turns
377 up) the final BusyBox 1.0.0 release should be ready by the end
378 of July.
379 <p>
380
381 If you have submitted patches, and they are not in this release
382 and I have not emailed you explaining why your patch was
383 rejected, it is safe to say that I have lost your patch. That
384 happens sometimes. Please do <B>NOT</b> send all your patches,
385 support questions, etc, directly to Erik. I get hundreds of
386 emails every day (which is why I end up losing patches
387 sometimes in the flood)... The busybox mailing list is the
388 right place to send your patches, support questions, etc.
389 <p>
390
391 I would like to especially thank Vladimir Oleynik (vodz), Glenn
392 McGrath (bug1), Robert Griebl (sandman), and Manuel Novoa III
393 (mjn3) for their significant efforts and contributions that
394 have made this release possible.
395 <p>
396
397 As usual you can <a href="downloads">download busybox here</a>.
398 You don't really need to bother with the
399 <a href="downloads/Changelog">changelog</a>, as the changes
400 vs the stable version are way too extensive to easily enumerate.
401 But you can take a look if you really want too.
402
403 <p>Have Fun!
404 <p>
405
406
407
408 <p>
409 <li><b>26 October 2002 -- BusyBox 0.60.5 released</b><p>
410
411 I am very pleased to announce that the BusyBox 0.60.5 (stable)
412 is now available for download. This is a bugfix release for
413 the stable series to address all the problems that have turned
414 up since the last release. Unfortunately, the previous release
415 had a few nasty bugs (i.e. init could deadlock, gunzip -c tried
416 to delete source files, cp -a wouldn't copy symlinks, and init
417 was not always providing controlling ttys when it should have).
418 I know I said that the previous release would be the end of the
419 0.60.x series. Well, it turns out I'm a liar. But this time I
420 mean it (just like last time ;-). This will be the last
421 release for the 0.60.x series -- all further development work
422 will be done for the development busybox tree. Expect the development
423 version to have its first real release very very soon now...
424
425 <p>
426 The <a href="downloads/Changelog.full">changelog</a> has all
427 the details. As usual you can <a href="downloads">download busybox here</a>.
428 <p>Have Fun!
429 <p>
430
431 <p>
432 <li><b>18 September 2002 -- BusyBox 0.60.4 released</b><p>
433
434 I am very pleased to announce that the BusyBox 0.60.4
435 (stable) is now available for download. This is primarily
436 a bugfix release for the stable series to address all
437 the problems that have turned up since the last
438 release. This will be the last release for the 0.60.x series.
439 I mean it this time -- all further development work will be done
440 on the development busybox tree, which is quite solid now and
441 should soon be getting its first real release.
442
443 <p>
444 The <a href="downloads/Changelog.full">changelog</a> has all
445 the details. As usual you can <a href="downloads">download busybox here</a>.
446 <p>Have Fun!
447 <p>
448
449
450 <p>
451 <li><b>27 April 2002 -- BusyBox 0.60.3 released</b><p>
452
453 I am very pleased to announce that the BusyBox 0.60.3 (stable) is
454 now available for download. This is primarily a bugfix release
455 for the stable series. A number of problems have turned up since
456 the last release, and this should address most of those problems.
457 This should be the last release for the 0.60.x series. The
458 development busybox tree has been progressing nicely, and will
459 hopefully be ready to become the next stable release.
460
461 <p>
462 The <a href="downloads/Changelog">changelog</a> has all
463 the details. As usual you can <a href="downloads">download busybox here</a>.
464 <p>Have Fun!
465 <p>
466
467
468 <p>
469 <li><b>6 March 2002 -- busybox.net now has mirrors!</b><p>
470
471 Busybox.net is now much more available, thanks to
472 the fine folks at <a href= "http://i-netinnovations.com/">http://i-netinnovations.com/</a>
473 who are providing hosting for busybox.net and
474 uclibc.org. In addition, we now have two mirrors:
475 <a href= "http://busybox.linuxmagic.com/">http://busybox.linuxmagic.com/</a>
476 in Canada and
477 <a href= "http://busybox.csservers.de/">http://busybox.csservers.de/</a>
478 in Germany. I hope this makes things much more
479 accessible for everyone!
480
481
482<li>
483<b>3 January 2002 -- Welcome to busybox.net!</b>
484
485<p>Thanks to the generosity of a number of busybox
486users, we have been able to purchase busybox.net
487(which is where you are probably reading this).
488Right now, busybox.net and uclibc.org are both
489living on my home system (at the end of my DSL
490line). I apologize for the abrupt move off of
491busybox.lineo.com. Unfortunately, I no longer have
492the access needed to keep that system updated (for
493example, you might notice the daily snapshots there
494stopped some time ago).</p>
495
496<p>Busybox.net is currently hosted on my home
497server, at the end of a DSL line. Unfortunately,
498the load on them is quite heavy. To address this,
499I'm trying to make arrangements to get busybox.net
500co-located directly at an ISP. To assist in the
501co-location effort, <a href=
502"http://www.codepoet.org/~markw">Mark Whitley</a>
503(author of busybox sed, cut, and grep) has donated
504his <a href=
505"http://www.netwinder.org/">NetWinder</a> computer
506for hosting busybox.net and uclibc.org. Once this
507system is co-located, the current speed problems
508should be completely eliminated. Hopefully, too,
509some of you will volunteer to set up some mirror
510sites, to help to distribute the load a bit.</p>
511
512<p><!--
513 <center>
514 Click here to help support busybox.net!
515 <form action="https://www.paypal.com/cgi-bin/webscr" method="post">
516 <input type="hidden" name="cmd" value="_xclick">
517 <input type="hidden" name="business" value="andersen@codepoet.org">
518 <input type="hidden" name="item_name" value="Support Busybox">
519 <input type="hidden" name="image_url" value="https://codepoet-consulting.com/images/busybox2.jpg">
520 <input type="hidden" name="no_shipping" value="1">
521 <input type="image" src="images/donate.png" border="0" name="submit" alt="Make donation using PayPal">
522 </form>
523 </center>
524 -->
525 Since some people expressed concern over BusyBox
526donations, let me assure you that no one is getting
527rich here. All BusyBox and uClibc donations will be
528spent paying for bandwidth and needed hardware
529upgrades. For example, Mark's NetWinder currently
530has just 64Meg of memory. As demonstrated when
531google spidered the site the other day, 64 Megs in
532not enough, so I'm going to be ordering 256Megs of
533ram and a larger hard drive for the box today. So
534far, donations received have been sufficient to
535cover almost all expenses. In the future, we may
536have co-location fees to worry about, but for now
537we are ok. A <b>HUGE thank-you</b> goes out to
538everyone that has contributed!<br>
539 -Erik</p>
540</li>
541
542<li>
543<b>20 November 2001 -- BusyBox 0.60.2 released</b>
544
545<p>We am very pleased to announce that the BusyBox
5460.60.2 (stable) is now released to the world. This
547one is primarily a bugfix release for the stable
548series, and it should take care of most everyone's
549needs till we can get the nice new stuff we have
550been working on in CVS ready to release (with the
551wonderful new buildsystem). The biggest change in
552this release (beyond bugfixes) is the fact that msh
553(the minix shell) has been re-worked by Vladimir N.
554Oleynik (vodz) and so it no longer crashes when
555told to do complex things with backticks.</p>
556
557<p>This release has been tested on x86, ARM, and
558powerpc using glibc 2.2.4, libc5, and uClibc, so it
559should work with just about any Linux system you
560throw it at. See the <a href=
561"downloads/Changelog">changelog</a> for <small>most
562of</small> the details. The last release was
563<em>very</em> solid for people, and this one should
564be even better.</p>
565
566<p>As usual BusyBox 0.60.2 can be downloaded from
567<a href=
568"downloads">http://www.busybox.net/downloads</a>.</p>
569
570<p>Have Fun.<br>
571 -Erik</p>
572</li>
573
574<li> <b>18 November 2001 -- Help us buy busybox.net!</b>
575
576<!-- Begin PayPal Logo -->
577<center>
578Click here to help buy busybox.net!
579<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
580<input type="hidden" name="cmd" value="_xclick">
581<input type="hidden" name="business" value="andersen@codepoet.org">
582<input type="hidden" name="item_name" value="Support Busybox">
583<input type="hidden" name="image_url" value="https://busybox.net/images/busybox2.jpg">
584<input type="hidden" name="no_shipping" value="1">
585<input type="image" src="images/donate.png" border="0" name="submit" alt="Make donation using PayPal">
586</form>
587</center>
588<!-- End PayPal Logo -->
589
590I've contacted the current owner of busybox.net and he is willing
591to sell the domain name -- for $250. He also owns busybox.org but
592will not part with it... I will then need to pay the registry fee
593for a couple of years and start paying for bandwidth, so this will
594initially cost about $300. I would like to host busybox.net on my
595home machine (codepoet.org) so I have full control over the system,
596but to do that would require that I increase the level of bandwidth
597I am paying for. Did you know that so far this month, there
598have been over 1.4 Gigabytes of busybox ftp downloads? I don't
599even <em>know</em> how much CVS bandwidth it requires. For the
600time being, Lineo has continued to graciously provide this
601bandwidth, despite the fact that I no longer work for them. If I
602start running this all on my home machine, paying for the needed bandwidth
603will start costing some money.
604<p>
605
606I was going to pay it all myself, but my wife didn't like that
607idea at all (big surprise). It turns out &lt;insert argument
608where she wins and I don't&gt; she has better ideas
609about what we should spend our money on that don't involve
610busybox. She suggested I should ask for contributions on the
611mailing list and web page. So...
612<p>
613
614I am hoping that if everyone could contribute a bit, we could pick
615up the busybox.net domain name and cover the bandwidth costs. I
616know that busybox is being used by a lot of companies as well as
617individuals -- hopefully people and companies that are willing to
618contribute back a bit. So if everyone could please help out, that
619would be wonderful!
620<p>
621
622
623<li> <b>23 August 2001 -- BusyBox 0.60.1 released</b>
624<br>
625
626 This is a relatively minor bug fixing release that fixes
627 up the bugs that have shown up in the stable release in
628 the last few weeks. Fortunately, nothing <em>too</em>
629 serious has shown up. This release only fixes bugs -- no
630 new features, no new applets. So without further ado,
631 here it is. Come and get it.
632 <p>
633 The
634 <a href="downloads/Changelog">changelog</a> has all
635 the details. As usual BusyBox 0.60.1 can be downloaded from
636 <a href="downloads">http://busybox.net/downloads</a>.
637 <p>Have Fun!
638 <p>
639
640
641<li> <b>2 August 2001 -- BusyBox 0.60.0 released</b>
642<br>
643 I am very pleased to announce the immediate availability of
644 BusyBox 0.60.0. I have personally tested this release with libc5, glibc,
645 and <a href="http://uclibc.org/">uClibc</a> on
646 x86, ARM, and powerpc using linux 2.2 and 2.4, and I know a number
647 of people using it on everything from ia64 to m68k with great success.
648 Everything seems to be working very nicely now, so getting a nice
649 stable bug-free(tm) release out seems to be in order. This releases fixes
650 a memory leak in syslogd, a number of bugs in the ash and msh shells, and
651 cleans up a number of things.
652
653 <p>
654
655 Those wanting an easy way to test the 0.60.0 release with uClibc can
656 use <a href="http://user-mode-linux.sourceforge.net/">User-Mode Linux</a>
657 to give it a try by downloading and compiling
658 <a href="ftp://busybox.net/buildroot.tar.gz">buildroot.tar.gz</a>.
659 You don't have to be root or reboot your machine to run test this way.
660 Preconfigured User-Mode Linux kernel source is also on busybox.net.
661 <p>
662 Another cool thing is the nifty <a href="downloads/tutorial/index.html">
663 BusyBox Tutorial</a> contributed by K Computing. This requires
664 a ShockWave plugin (or standalone viewer), so you may want to grab the
665 the GPLed shockwave viewer from <a href="http://www.swift-tools.com/Flash/flash-0.4.10.tgz">here</a>
666 to view the tutorial.
667 <p>
668
669 Finally, In case you didn't notice anything odd about the
670 version number of this release, let me point out that this release
671 is <em>not</em> 0.53, because I bumped the version number up a
672 bit. This reflects the fact that this release is intended to form
673 a new stable BusyBox release series. If you need to rely on a
674 stable version of BusyBox, you should plan on using the stable
675 0.60.x series. If bugs show up then I will release 0.60.1, then
676 0.60.2, etc... This is also intended to deal with the fact that
677 the BusyBox build system will be getting a major overhaul for the
678 next release and I don't want that to break products that people
679 are shipping. To avoid that, the new build system will be
680 released as part of a new BusyBox development series that will
681 have some not-yet-decided-on odd version number. Once things
682 stabilize and the new build system is working for everyone, then
683 I will release that as a new stable release series.
684
685 <p>
686 The
687 <a href="downloads/Changelog">changelog</a> has all
688 the details. As usual BusyBox 0.60.0 can be downloaded from
689 <a href="downloads">http://busybox.net/downloads</a>.
690 <p>Have Fun!
691 <p>
692
693
694<li> <b>7 July 2001 -- BusyBox 0.52 released</b>
695<br>
696
697 I am very pleased to announce the immediate availability of
698 BusyBox 0.52 (the "new-and-improved rock-solid release"). This
699 release is the result of <em>many</em> hours of work and has tons
700 of bugfixes, optimizations, and cleanups. This release adds
701 several new applets, including several new shells (such as hush, msh,
702 and ash).
703
704 <p>
705 The
706 <a href="downloads/Changelog">changelog</a> covers
707 some of the more obvious details, but there are many many things that
708 are not mentioned, but have been improved in subtle ways. As usual,
709 BusyBox 0.52 can be downloaded from
710 <a href="downloads">http://busybox.net/downloads</a>.
711 <p>Have Fun!
712 <p>
713
714
715<li> <b>10 April 2001 - Graph of Busybox Growth </b>
716<br>
717The illustrious Larry Doolittle has made a PostScript chart of the growth
718of the Busybox tarball size over time. It is available for downloading /
719viewing <a href= "busybox-growth.ps"> right here</a>.
720
721<p> (Note that while the number of applets in Busybox has increased, you
722can still configure Busybox to be as small as you want by selectively
723turning off whichever applets you don't need.)
724<p>
725
726
727<li> <b>10 April 2001 -- BusyBox 0.51 released</b>
728<br>
729
730 BusyBox 0.51 (the "rock-solid release") is now out there. This
731 release adds only 2 new applets: env and vi. The vi applet,
732 contributed by Sterling Huxley, is very functional, and is only
733 22k. This release fixes 3 critical bugs in the 0.50 release.
734 There were 2 potential segfaults in lash (the busybox shell) in
735 the 0.50 release which are now fixed. Another critical bug in
736 0.50 which is now fixed: syslogd from 0.50 could potentially
737 deadlock the init process and thereby break your entire system.
738 <p>
739
740 There are a number of improvements in this release as well. For
741 one thing, the wget applet is greatly improved. Dmitry Zakharov
742 added FTP support, and Laurence Anderson make wget fully RFC
743 compliant for HTTP 1.1. The mechanism for including utility
744 functions in previous releases was clumsy and error prone. Now
745 all utility functions are part of a new libbb library, which makes
746 maintaining utility functions much simpler. And BusyBox now
747 compiles on itanium systems (thanks to the Debian itanium porters
748 for letting me use their system!).
749 <p>
750 You can read the
751 <a href="downloads/Changelog">changelog</a> for
752 complete details. BusyBox 0.51 can be downloaded from
753 <a href="downloads">http://busybox.net/downloads</a>.
754 <p>Have Fun!
755 <p>
756
757<li> <b>Busybox Boot-Floppy Image</b>
758
759<p>Because you asked for it, we have made available a <a href=
760"downloads/busybox.floppy.img"> Busybox boot floppy
761image</a>. Here's how you use it:
762
763<ol>
764
765 <li> <a href= "downloads/busybox.floppy.img">
766 Download the image</a>
767
768 <li> dd it onto a floppy like so: <tt> dd if=busybox.floppy.img
769 of=/dev/fd0 ; sync </tt>
770
771 <li> Pop it in a machine and boot up.
772
773</ol>
774
775<p> If you want to look at the contents of the initrd image, do this:
776
777<pre>
778 mount ./busybox.floppy.img /mnt -o loop -t msdos
779 cp /mnt/initrd.gz /tmp
780 umount /mnt
781 gunzip /tmp/initrd.gz
782 mount /tmp/initrd /mnt -o loop -t minix
783</pre>
784
785
786<li> <b>15 March 2001 -- BusyBox 0.50 released</b>
787<br>
788
789 This release adds several new applets including ifconfig, route, pivot_root, stty,
790 and tftp, and also fixes tons of bugs. Tab completion in the
791 shell is now working very well, and the shell's environment variable
792 expansion was fixed. Tons of other things were fixed or made
793 smaller. For a fairly complete overview, see the
794 <a href="downloads/Changelog">changelog</a>.
795 <p>
796 lash (the busybox shell) is still with us, fixed up a bit so it
797 now behaves itself quite nicely. It really is quite usable as
798 long as you don't expect it to provide Bourne shell grammer.
799 Standard things like pipes, redirects, command line editing, and
800 environment variable expansion work great. But we have found that
801 this shell, while very usable, does not provide an extensible
802 framework for adding in full Bourne shell behavior. So the first order of
803 business as we begin working on the next BusyBox release will be to merge in the new shell
804 currently in progress at
805 <a href="http://doolittle.faludi.com/~larry/parser.html">Larry Doolittle's website</a>.
806 <p>
807
808
809<li> <b>27 January 2001 -- BusyBox 0.49 released</b>
810<br>
811
812 Several new applets, lots of bug fixes, cleanups, and many smaller
813 things made nicer. Several cleanups and improvements to the shell.
814 For a list of the most interesting changes
815 you might want to look at the <a href="downloads/Changelog">changelog</a>.
816 <p>
817 Special thanks go out to Matt Kraai and Larry Doolittle for all their
818 work on this release, and for keeping on top of things while I've been
819 out of town.
820 <p>
821 <em>Special Note</em><br>
822
823 BusyBox 0.49 was supposed to have replaced lash, the BusyBox
824 shell, with a new shell that understands full Bourne shell/Posix shell grammer.
825 Well, that simply didn't happen in time for this release. A new
826 shell that will eventually replace lash is already under
827 construction. This new shell is being developed by Larry
828 Doolittle, and could use all of our help. Please see the work in
829 progress on <a href="http://doolittle.faludi.com/~larry/parser.html">Larry's website</a>
830 and help out if you can. This shell will be included in the next
831 release of BusyBox.
832 <p>
833
834<li> <b>13 December 2000 -- BusyBox 0.48 released</b>
835<br>
836
837 This release fixes lots and lots of bugs. This has had some very
838 rigorous testing, and looks very, very clean. The usual tar
839 update of course: tar no longer breaks hardlinks, tar -xzf is
840 optionally supported, and the LRP folks will be pleased to know
841 that 'tar -X' and 'tar --exclude' are both now in. Applets are
842 now looked up using a binary search making lash (the busybox
843 shell) much faster. For the new debian-installer (for Debian
844 woody) a .udeb can now be generated.
845 <p>
846 The curious can get a list of some of the more interesting changes by reading
847 the <a href="downloads/Changelog">changelog</a>.
848 <p>
849 Many thanks go out to the many many people that have contributed to
850 this release, especially Matt Kraai, Larry Doolittle, and Kent Robotti.
851 <p>
852<p> <li> <b>26 September 2000 -- BusyBox 0.47 released</b>
853<br>
854
855 This release fixes lots of bugs (including an ugly bug in 0.46
856 syslogd that could fork-bomb your system). Added several new
857 apps: rdate, wget, getopt, dos2unix, unix2dos, reset, unrpm,
858 renice, xargs, and expr. syslogd now supports network logging.
859 There are the usual tar updates. Most apps now use getopt for
860 more correct option parsing.
861 See the <a href="downloads/Changelog">changelog</a>
862 for complete details.
863
864
865<p> <li> <b>11 July 2000 -- BusyBox 0.46 released</b>
866<br>
867
868 This release fixes several bugs (including a ugly bug in tar,
869 and fixes for NFSv3 mount support). Added a dumpkmap to allow
870 people to dump a binary keymaps for use with 'loadkmap', and a
871 completely reworked 'grep' and 'sed' which should behave better.
872 BusyBox shell can now also be used as a login shell.
873 See the <a href="downloads/Changelog">changelog</a>
874 for complete details.
875
876
877<p> <li> <b>21 June 2000 -- BusyBox 0.45 released</b>
878<br>
879
880 This release has been slow in coming, but is very solid at this
881 point. BusyBox now supports libc5 as well as GNU libc. This
882 release provides the following new apps: cut, tr, insmod, ar,
883 mktemp, setkeycodes, md5sum, uuencode, uudecode, which, and
884 telnet. There are bug fixes for just about every app as well (see
885 the <a href="downloads/Changelog">changelog</a> for
886 details).
887 <p>
888 Also, some exciting infrastructure news! Busybox now has its own
889 <a href="lists/busybox/">mailing list</a>,
890 publically browsable
891 <a href="/cgi-bin/cvsweb/busybox/">CVS tree</a>,
892 anonymous
893 <a href="cvs_anon.html">CVS access</a>, and
894 for those that are actively contributing there is even
895 <a href="cvs_write.html">CVS write access</a>.
896 I think this will be a huge help to the ongoing development of BusyBox.
897 <p>
898 Also, for the curious, there is no 0.44 release. Somehow 0.44 got announced
899 a few weeks ago prior to its actually being released. To avoid any confusion
900 we are just skipping 0.44.
901 <p>
902 Many thanks go out to the many people that have contributed to this release
903 of BusyBox (esp. Pavel Roskin)!
904
905
906<p> <li> <b>19 April 2000 -- syslogd bugfix</b>
907<br>
908Turns out that there was still a bug in busybox syslogd.
909For example, with the following test app:
910<pre>
911#include &lt;syslog.h&gt;
912
913int do_log(char* msg, int delay)
914{
915 openlog("testlog", LOG_PID, LOG_DAEMON);
916 while(1) {
917 syslog(LOG_ERR, "%s: testing one, two, three\n", msg);
918 sleep(delay);
919 }
920 closelog();
921 return(0);
922};
923
924int main(void)
925{
926 if (fork()==0)
927 do_log("A", 2);
928 do_log("B", 3);
929}
930</pre>
931it should be logging stuff from both "A" and "B". As released in 0.43 only stuff
932from "A" would have been logged. This means that if init tries to log something
933while say ppp has the syslog open, init would block (which is bad, bad, bad).
934<p>
935Karl M. Hegbloom has created a fix for the problem.
936Thanks Karl!
937
938
939<p> <li> <b>18 April 2000 -- BusyBox 0.43 released (finally!)</b>
940<br>
941I have finally gotten everything into a state where I feel pretty
942good about things. This is definitely the most stable, solid release
943so far. A lot of bugs have been fixed, and the following new apps
944have been added: sh, basename, dirname, killall, uptime,
945freeramdisk, tr, echo, test, and usleep. Tar has been completely
946rewritten from scratch. Bss size has also been greatly reduced.
947More details are available in the
948<a href="downloads/Changelog">changelog</a>.
949Oh, and as a special bonus, I wrote some fairly comprehensive
950<em>documentation</em>, complete with examples and full usage information.
951
952<p>
953Many thanks go out to the fine people that have helped by submitting patches
954and bug reports; particularly instrumental in helping for this release were
955Karl Hegbloom, Pavel Roskin, Friedrich Vedder, Emanuele Caratti,
956Bob Tinsley, Nicolas Pitre, Avery Pennarun, Arne Bernin, John Beppu, and Jim Gleason.
957There were others so if I somehow forgot to mention you, I'm very sorry.
958<p>
959
960You can grab BusyBox 0.43 tarballs <a href="downloads">here</a>.
961
962<p> <li> <b>9 April 2000 -- BusyBox 0.43 pre release</b>
963<br>
964Unfortunately, I have not yet finished all the things I want to
965do for BusyBox 0.43, so I am posting this pre-release for people
966to poke at. This contains my complete rewrite of tar, which now weighs in at
9675k (7k with all options turned on) and works for reading and writing
968tarballs (which it does correctly for everything I have been able to throw
969at it). Tar also (optionally) supports the "--exclude" option (mainly because
970the Linux Router Project folks asked for it). This also has a pre-release
971of the micro shell I have been writing. This pre-release should be stable
972enough for production use -- it just isn't a release since I have some structural
973changes I still want to make.
974<p>
975The pre-release can be found <a href="downloads">here</a>.
976Please let me know ASAP if you find <em>any</em> bugs.
977
978<p> <li> <b>28 March 2000 -- Andersen Baby Boy release</b>
979<br>
980I am pleased to announce that on Tuesday March 28th at 5:48pm, weighing in at 7
981lbs. 12 oz, Micah Erik Andersen was born at LDS Hospital here in Salt Lake City.
982He was born in the emergency room less then 5 minutes after we arrived -- and
983it was such a relief that we even made it to the hospital at all. Despite the
984fact that I was driving at an amazingly unlawful speed and honking at everybody
985and thinking decidedly unkind thoughts about the people in our way, my wife
986(inconsiderate of my feelings and complete lack of medical training) was lying
987down in the back seat saying things like "I think I need to start pushing now"
988(which she then proceeded to do despite my best encouraging statements to the
989contrary).
990<p>
991Anyway, I'm glad to note that despite the much-faster-than-we-were-expecting
992labor, both Shaunalei and our new baby boy are doing wonderfully.
993<p>
994So now that I am done with my excuse for the slow release cycle...
995Progress on the next release of BusyBox has been slow but steady. I expect
996to have a release sometime during the first week of April. This release will
997include a number of important changes, including the addition of a shell, a
998re-write of tar (to accommodate the Linux Router Project), and syslogd can now
999accept multiple concurrent connections, fixing lots of unexpected blocking
1000problems.
1001
1002
1003<p> <li> <b>11 February 2000 -- BusyBox 0.42 released</b>
1004<br>
1005
1006 This is the most solid BusyBox release so far. Many, many
1007 bugs have been fixed. See the
1008 <a href="downloads/Changelog">changelog</a> for details.
1009
1010 Of particular interest, init will now cleanly unmount
1011 filesystems on reboot, cp and mv have been rewritten and
1012 behave much better, and mount and umount no longer leak
1013 loop devices. Many thanks go out to Randolph Chung,
1014 Karl M. Hegbloom, Taketoshi Sano, and Pavel Roskin for
1015 their hard work on this release of BusyBox. Please pound
1016 on it and let me know if you find any bugs.
1017
1018<p> <li> <b>19 January 2000 -- BusyBox 0.41 released</b>
1019<br>
1020
1021 This release includes bugfixes to cp, mv, logger, true, false,
1022 mkdir, syslogd, and init. New apps include wc, hostid,
1023 logname, tty, whoami, and yes. New features include loop device
1024 support in mount and umount, and better TERM handling by init.
1025 The changelog can be found <a href="downloads/Changelog">here</a>.
1026
1027<p> <li> <b>7 January 2000 -- BusyBox 0.40 released</b>
1028<br>
1029
1030 This release includes bugfixes to init (now includes inittab support),
1031 syslogd, head, logger, du, grep, cp, mv, sed, dmesg, ls, kill, gunzip, and mknod.
1032 New apps include sort, uniq, lsmod, rmmod, fbset, and loadacm.
1033 In particular, this release fixes an important bug in tar which
1034 in some cases produced serious security problems.
1035 As always, the changelog can be found <a href="downloads/Changelog">here</a>.
1036
1037<p> <li> <b>11 December 1999 -- BusyBox Website</b>
1038<br>
1039 I have received permission from Bruce Perens (the original author of BusyBox)
1040 to set up this site as the new primary website for BusyBox. This website
1041 will always contain pointers to the latest and greatest, and will also
1042 contain the latest documentation on how to use BusyBox, what it can do,
1043 what arguments its apps support, etc.
1044
1045<p> <li> <b>10 December 1999 -- BusyBox 0.39 released</b>
1046<br>
1047 This release includes fixes to init, reboot, halt, kill, and ls, and contains
1048 the new apps ping, hostname, mkfifo, free, tail, du, tee, and head. A full
1049 changelog can be found <a href="downloads/Changelog">here</a>.
1050<p> <li> <b>5 December 1999 -- BusyBox 0.38 released</b>
1051<br>
1052 This release includes fixes to tar, cat, ls, dd, rm, umount, find, df,
1053 and make install, and includes new apps syslogd/klogd and logger.
1054
1055
1056</ul>
1057
1058
1059<!--#include file="footer.html" -->
1060
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 @@
1<!--#include file="header.html" -->
2
3
4<h3>Products/Projects Using BusyBox</h3>
5
6Do you use BusyBox? I'd love to know about it and
7I'd be happy to link to you.
8
9<p>
10I know of the following products and/or projects that use BusyBox --
11listed in the order I happen to add them to the web page:
12
13<ul>
14
15
16<li><a href="/cgi-bin/cvsweb/buildroot/">buildroot</a><br>A configurable
17means for building your own busybox/uClibc based system systems.
18
19<li><a href="http://www.pengutronix.de/software/ptxdist_en.html">PTXdist</a><br>another
20configurable means for building your own busybox based system systems.
21
22</li><li><a href=
23"http://cvs.debian.org/boot-floppies/">
24Debian installer (boot floppies) project</a>
25
26</li><li><a href="http://redhat.com/">Red Hat installer</a>
27
28</li><li><a href=
29"http://distro.ibiblio.org/pub/Linux/distributions/slackware/slackware-current/source/rootdisks/">
30Slackware Installer</a>
31
32</li><li><a href="http://www.gentoo.org/">Gentoo Linux install/boot CDs</a>
33</li><li><a href="http://www.mandrake.com/">The Mandrake installer</a>
34
35</li><li><a href="http://Leaf.SourceForge.net">Linux Embedded Appliance Firewall</a><br>The sucessor of the Linux Router Project, supporting all sorts of embedded Linux gateways, routers, wireless routers, and firewalls.
36
37</li><li><a href=
38"http://www.toms.net/rb/">tomsrtbt</a>
39
40</li><li><a href="http://www.stormix.com/">Stormix
41Installer</a>
42
43</li><li><a href=
44"http://www.emacinc.com/linux2_sbc.htm">EMAC Linux
452.0 SBC</a>
46
47</li><li><a href="http://www.trinux.org/">Trinux</a>
48
49</li><li><a href="http://oddas.sourceforge.net/">ODDAS
50project</a>
51
52</li><li><a href="http://byld.sourceforge.net/">Build Your
53Linux Disk</a>
54
55</li><li><a href=
56"http://ibiblio.org/pub/Linux/system/recovery">Zdisk</a>
57
58</li><li><a href="http://www.adtran.com">AdTran -
59VPN/firewall VPN Linux Distribution</a>
60
61</li><li><a href="http://mkcdrec.ota.be/">mkCDrec - make
62CD-ROM recovery</a>
63
64</li><li><a href=
65"http://recycle.lbl.gov/~ldoolitt/bse/">Linux on
66nanoEngine</a>
67
68</li><li><a href=
69"http://www.zelow.no/floppyfw/">Floppyfw</a>
70
71</li><li><a href="http://www.ltsp.org/">Linux Terminal
72Server Project</a>
73
74</li><li><a href="http://www.devil-linux.org/">Devil-Linux</a>
75
76</li><li><a href="http://dutnux.sourceforge.net/">DutNux</a>
77
78</li><li><a href="http://www.microwerks.net/~hugo/mindi/">Mindi</a>
79
80</li><li><a href="http://www.minimalinux.org/ttylinux/">ttylinux</a>
81
82</li><li><a href="http://www.coyotelinux.com/">Coyote Linux</a>
83
84</li><li><a href="http://www.partimage.org/">Partition
85Image</a>
86
87</li><li><a href="http://www.fli4l.de/">fli4l the on(e)-disk-router</a>
88
89</li><li><a href="http://tinfoilhat.cultists.net/">Tinfoil
90Hat Linux</a>
91
92</li><li><a href="http://sourceforge.net/projects/gp32linux/">gp32linux</a>
93</li><li><a href="http://familiar.handhelds.org/">Familiar Linux</a><br>A linux distribution for handheld computers
94</li><li><a href="http://rescuecd.sourceforge.net/">Timo's Rescue CD Set</a>
95</li><li><a href="http://sf.net/projects/netstation/">Netstation</a>
96</li><li><a href="http://www.fiwix.org/">GNU/Fiwix Operating System</a>
97</li><li><a href="http://www.softcraft.com/">Generations Linux</a>
98</li><li><a href="http://systemimager.org/relatedprojects/">SystemImager / System Installation Suite</a>
99</li><li><a href="http://www.bablokb.de/gendist/">GENDIST distribution generator</a>
100</li><li><a href="http://diet-pc.sourceforge.net/">DIET-PC embedded Linux thin client distribution</a>
101</li><li><a href="http://byzgl.sourceforge.net/">BYZantine Gnu/Linux</a>
102</li><li><a href="http://dban.sourceforge.net/">Darik's Boot and Nuke</a>
103</li><li><a href="http://www.timesys.com/">TimeSys real-time Linux</a>
104</li><li><a href="http://movix.sf.net/">MoviX</a><br>Boots from CD and automatically plays every video file on the CD
105</li><li><a href="http://katamaran.sourceforge.net">katamaran</a><br>Linux, X11, xfce windowmanager, based on BusyBox
106</li><li><a href="http://www.sourceforge.net/projects/simplygnustep">Prometheus SimplyGNUstep</a>
107</li><li><a href="http://www.renyi.hu/~ekho/lowlife/">lowlife</a><br>A documentation project on how to make your own uClibc-based systems and floppy.
108</li><li><a href="http://metadistros.hispalinux.es/">Metadistros</a><br>a project to allow you easily make Live-CD distributions.
109</li><li><a href="http://salvare.sourceforge.net/">Salvare</a><br>More Linux than tomsrtbt but less than Knoppix, aims to provide a useful workstation as well as a rescue disk.
110</li><li><a href="http://www.stresslinux.org/">stresslinux</a><br>minimal linux distribution running from a bootable cdrom or via PXE.
111</li><li><a href="http://thinstation.sourceforge.net/">thinstation</a><br>convert standard PCs into full-featured diskless thinclients.
112</li><li><a href="http://www.uhulinux.hu/">UHU-Linux Hungary</a>
113</li><li><a href="http://deep-water.berlios.de/">Deep-Water Linux</a>
114</li><li><a href="http://www.freesco.org/">Freesco router</a>
115</li><li><a href="http://Sentry.SourceForge.net/">Sentry Firewall CD</a>
116
117
118
119</li><li><a href="http://tuxscreen.net">Tuxscreen Linux Phone</a>
120</li><li><a href="http://www.kerbango.com/">The Kerbango Internet Radio</a>
121</li><li><a href="http://www.linuxmagic.com/vpn/">LinuxMagic VPN Firewall</a>
122</li><li><a href="http://www.isilver-inc.com/">I-Silver Linux appliance servers</a>
123</li><li><a href="http://zaurus.sourceforge.net/">Sharp Zaurus PDA</a>
124</li><li><a href="http://www.cyclades.com/">Cyclades-TS and other Cyclades products</a>
125</li><li><a href="http://www.linksys.com/products/product.asp?prid=508">Linksys WRT54G - Wireless-G Broadband Router</a>
126</li><li><a href="http://www.dell.com/us/en/biz/topics/sbtopic_005_truemobile.htm">Dell TrueMobile 1184</a>
127</li><li><a href="http://actiontec.com/products/modems/dual_pcmodem/dpm_overview.html">Actiontec Dual PC Modem</a>
128</li><li><a href="http://www.kiss-technology.com/">Kiss DP Series DVD players</a>
129</li><li><a href="http://www.netgear.com/products/prod_details.asp?prodID=170">NetGear WG602 wireless router</a>
130 <br>with sources <a href="http://www.netgear.com/support/support_details.asp?dnldID=453">here</a>
131</li><li><a href="http://www.trendware.com/products/TEW-411BRP.htm">TRENDnet TEW-411BRP 802.11g Wireless AP/Router/Switch</a>
132 <br>Source for busybox and udhcp <a href="http://www.trendware.com/asp/download/fileinfo.asp?file_id=277&B1=Search">here</a> though no kernel source is provided.
133</li><li><a href="http://www.buffalo-technology.com/webcontent/products/wireless/wbr-g54.htm">Buffalo WBR-G54 wireless router</a>
134 </li><li><a href="http://www.asus.com/products/communication/wireless/wl-300g/overview.htm">ASUS WL-300g Wireless LAN Access Point</a>
135 <br>with source<a href="http://www.asus.com.tw/support/download/item.aspx?ModelName=WL-300G">here</a>
136 </li><li><a href="http://catalog.belkin.com/IWCatProductPage.process?Merchant_Id=&Section_Id=201522&pcount=&Product_Id=136493">Belkin 54g Wireless DSL/Cable Gateway Router</a>
137 <br>with source<a href="http://web.belkin.com/support/gpl.asp">here</a>
138 <li><a href="http://www.acronis.com/products/partitionexpert/">Acronis PartitionExpert 2003</a>
139 <br>includes a heavily modified BusyBox v0.60.5 with built in
140 cardmgr, device detection, gpm, lspci, etc. Also includes udhcp,
141 uClibc 0.9.26, a heavily patched up linux kernel, etc. Source
142 can only be obtained <a href="http://www.acronis.com/files/gpl/linux.tar.bz2">here</a>
143
144</li><li><a href="http://www.usr.com/">U.S. Robotics Sureconnect 4-port ADSL router</a><br>
145 with source <a href="http://www.usr.com/support/s-gpl-code.asp">here</a>
146</li><li><a href="http://www.actiontec.com/products/broadband/54mbps_wireless_gateway_1p/index.html">
147 ActionTec GT701-WG Wireless Gateway/DSL Modem</a>
148 with source <a href="http://128.121.226.214/gtproducts/index.html">here</a>
149</li><li><a href="http://smartlinux.sourceforge.net/">S.M.A.R.T. Linux</a>
150</li><li><a href="http://www.dlink.com/">DLink - Model GSL-G604T, DSL-300T, and possibly other models</a>
151 with source <a href="ftp://ftp.dlink.co.uk/dsl_routers_modems/">here,</a>
152 with source <a href="ftp://ftp.dlink.de/dsl-products/">and here,</a>
153 and quite possibly other places as well. You may need to dig down a bit
154 to find the source, but it does seem to be there.
155</li><li><a href="http://www.siemens-mobile.de/cds/frontdoor/0,2241,de_de_0_42931_rArNrNrNrN,00.html">Siemens SE515 DSL router</a>
156 with source <a href="http://now-portal.c-lab.de/projects/gigaset/">here, I think...</a>
157 with some details <a href="http://heinz.hippenstiel.org/familie/hp/hobby/gigaset_se515dsl.html">here.</a>
158</li><li><a href="http://frwt.stim.ru/">Free Remote Windows Terminal</a>
159
160
161</li>
162</ul>
163
164
165<!--#include file="footer.html" -->
166
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 @@
1<!--#include file="header.html" -->
2
3
4<!-- Begin Screenshot -->
5
6<h3> Busybox Screenshot! </h3>
7
8
9Everybody loves to look at screenshots, so here is a live action screenshot of BusyBox.
10
11<pre style="background-color: black; color: lightgreen; padding: 5px;
12font-family: monospace; font-size: smaller;" width="100">
13
14
15$ ./busybox
16BusyBox v1.00 (2004.10.13-04:49+0000) multi-call binary
17
18Usage: busybox [function] [arguments]...
19 or: [function] [arguments]...
20
21 BusyBox is a multi-call binary that combines many common Unix
22 utilities into a single executable. Most people will create a
23 link to busybox for each function they wish to use, and BusyBox
24 will act like whatever it was invoked as.
25
26Currently defined functions:
27
28 [, addgroup, adduser, adjtimex, ar, arping, ash, awk, basename, bunzip2,
29 busybox, bzcat, cal, cat, chgrp, chmod, chown, chroot, chvt, clear, cmp,
30 cp, cpio, crond, crontab, cut, date, dc, dd, deallocvt, delgroup, deluser,
31 devfsd, df, dirname, dmesg, dos2unix, dpkg, dpkg-deb, du, dumpkmap,
32 dumpleases, echo, egrep, env, expr, false, fbset, fdflush, fdformat, fdisk,
33 fgrep, find, fold, free, freeramdisk, fsck.minix, ftpget, ftpput, getopt,
34 getty, grep, gunzip, gzip, halt, hdparm, head, hexdump, hostid, hostname,
35 httpd, hush, hwclock, id, ifconfig, ifdown, ifup, inetd, init, insmod,
36 install, ip, ipaddr, ipcalc, iplink, iproute, iptunnel, kill, killall,
37 klogd, lash, last, length, linuxrc, ln, loadfont, loadkmap, logger, login,
38 logname, logread, losetup, ls, lsmod, makedevs, md5sum, mesg, mkdir,
39 mkfifo, mkfs.minix, mknod, mkswap, mktemp, modprobe, more, mount, msh, mt,
40 mv, nameif, nc, netstat, nslookup, od, openvt, passwd, patch, pidof, ping,
41 ping6, pipe_progress, pivot_root, poweroff, printf, ps, pwd, rdate,
42 readlink, realpath, reboot, renice, reset, rm, rmdir, rmmod, route, rpm,
43 rpm2cpio, run-parts, rx, sed, seq, setkeycodes, sha1sum, sleep, sort,
44 start-stop-daemon, strings, stty, su, sulogin, swapoff, swapon, sync,
45 sysctl, syslogd, tail, tar, tee, telnet, telnetd, test, tftp, time, top,
46 touch, tr, traceroute, true, tty, udhcpc, udhcpd, umount, uname,
47 uncompress, uniq, unix2dos, unzip, uptime, usleep, uudecode, uuencode,
48 vconfig, vi, vlock, watch, watchdog, wc, wget, which, who, whoami, xargs,
49 yes, zcat
50
51
52$ <blink>_</blink>
53
54</pre>
55
56<!--#include file="footer.html" -->
57
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 @@
1<!--#include file="header.html" -->
2
3
4<h3>Hall of Shame!!!</h3>
5
6The following products and/or projects appear to use BusyBox, but do not
7appear to release source code as required by the <a
8href="/license.html">BusyBox license</a>. This is a violation of the law!
9The distributors of these products are invited to contact <a href=
10"mailto:andersen@codepoet.org">Erik Andersen</a> if they have any confusion
11as to what is needed to bring their products into compliance, or if they have
12already brought their product into compliance and wish to be removed from the
13Hall of Shame.
14
15<p>
16
17Here are the details of <a href="/license.html">exactly how to comply
18with the BusyBox license</a>, so there should be no question as to
19exactly what is expected.
20Complying with the Busybox license is easy and completely free, so the
21companies listed below should be ashamed of themselves. Furthermore, each
22product listed here is subject to being legally ordered to cease and desist
23distribution for violation of copyright law, and the distributor of each
24product is subject to being sued for statutory copyright infringement damages
25of up to $150,000 per work plus legal fees. Nobody wants to be sued, and <a
26href="mailto:andersen@codepoet.org">Erik</a> certainly would prefer to spend
27his time doing better things than sue people. But he will sue if forced to
28do so to maintain compliance.
29
30<p>
31
32Do everyone a favor and don't break the law -- if you use busybox, comply with
33the busybox license by releasing the source code with your product.
34
35<p>
36
37<ul>
38
39 <li><a href="http://www.trittontechnologies.com/products.html">Tritton Technologies NAS120</a>
40 <br>see <a href="http://www.ussg.iu.edu/hypermail/linux/kernel/0404.0/1611.html">here for details</a>
41 <li><a href="http://www.macsense.com/product/homepod/">Macsense HomePod</a>
42 <br>with details
43 <a href="http://developer.gloolabs.com/modules.php?op=modload&name=Forums&file=viewtopic&topic=123&forum=7">here</a>
44 <li><a href="http://www.cpx.com/products.asp?c=Wireless+Products">Compex Wireless Products</a>
45 <br>appears to be running v0.60.5 with Linux version 2.4.20-uc0 on ColdFire,
46 but no source code is mentioned or offered.
47 <li><a href="http://www.inventel.com/en/product/datasheet/10/">Inventel DW 200 wireless/ADSL router</a>
48 <li><a href="http://www.sweex.com/product.asp">Sweex DSL router</a>
49 <br>appears to be running BusyBox v1.00-pre2 and udhcpd, but no source
50 code is mentioned or offered.
51 <li><a href="http://www.trendware.com/products/TEW-410APB.htm">TRENDnet TEW-410APB</a>
52 </li><li><a href="http://www.hauppauge.com/Pages/products/data_mediamvp.html">Hauppauge Media MVP</a>
53 <br>Hauppauge contacted me on 16 Dec 2003, and claims to be working on resolving this problem.
54 </li><li><a href="http://www.hitex.com/download/adescom/data/">TriCore</a>
55 </li><li><a href="http://www.allnet.de/">ALLNET 0186 wireless router</a>
56 </li><li><a href="http://www.dmmtv.com/">Dreambox DM7000S DVB Satellite Receiver</a>
57 <br> Dream Multimedia contacted me on 22 Dec 2003 and is working on resolving this problem.
58 <br> Source _may_ be here: http://cvs.tuxbox.org/cgi-bin/viewcvs.cgi/tuxbox/cdk/
59 </li><li><a href="http://testing.lkml.org/slashdot.php?mid=331690">Sigma Designs EM8500 based DVD players</a>
60 <br>Source for the Sigma Designs reference platform is found here<br>
61 <a href="http://www.uclinux.org/pub/uClinux/ports/arm/EM8500/uClinux-2.4-sigma.tar.gz">uClinux-2.4-sigma.tar.gz</a>, so while Sigma Designs itself appears to be in compliance, as far as I can tell,
62 no vendors of Sigma Designs EM8500 based devices actually comply with the GPL....
63 </li><li><a href="http://testing.lkml.org/slashdot.php?mid=433790">Liteon LVD2001 DVD player using the Sigma Designs EM8500</a>
64 </li><li><a href="http://www.rimax.net/">Rimax DVD players using the Sigma Designs EM8500</a>
65 </li><li><a href="http://www.vinc.us/">Bravo DVD players using the Sigma Designs EM8500</a>
66 </li><li><a href="http://www.hb-direct.com/">H&B DX3110 Divx player based on Sigma Designs EM8500</a>
67 </li><li><a href="http://www.recospa.it/mdpro1/index.php">United *DVX4066 mpeg4 capable DVD players</a>
68 </li><li><a href="http://www.a-link.com/RR64AP.html">Avaks alink Roadrunner 64</a>
69 <br> Partial source available, based on source distributed under NDA from <a href="http://www.lsilogic.com/products/dsl_platform_solutions/hb_linuxr2_2.html"> LSILogic</a>. Why the NDA LSILogic, what are you hiding ?
70 <br>To verify the Avaks infrigment see my slashdot <a href="http://slashdot.org/~bug1/journal/">journal</a>.
71 </li><li>Undoubtedly there are others... Please report them so we can shame them (or if necessary sue them) into compliance.
72
73</ul>
74
75
76<!--#include file="footer.html" -->
77
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 @@
1=back
2
3=head1 LIBC NSS
4
5GNU Libc (glibc) uses the Name Service Switch (NSS) to configure the behavior
6of the C library for the local environment, and to configure how it reads
7system data, such as passwords and group information. This is implemented
8using an /etc/nsswitch.conf configuration file, and using one or more of the
9/lib/libnss_* libraries. BusyBox tries to avoid using any libc calls that make
10use of NSS. Some applets however, such as login and su, will use libc functions
11that require NSS.
12
13If you enable CONFIG_USE_BB_PWD_GRP, BusyBox will use internal functions to
14directly access the /etc/passwd, /etc/group, and /etc/shadow files without
15using NSS. This may allow you to run your system without the need for
16installing any of the NSS configuration files and libraries.
17
18When used with glibc, the BusyBox 'networking' applets will similarly require
19that you install at least some of the glibc NSS stuff (in particular,
20/etc/nsswitch.conf, /lib/libnss_dns*, /lib/libnss_files*, and /lib/libresolv*).
21
22Shameless Plug: As an alternative, one could use a C library such as uClibc. In
23addition to making your system significantly smaller, uClibc does not require the
24use of any NSS support files or libraries.
25
26=head1 MAINTAINER
27
28Erik Andersen <andersen@codepoet.org>
29
30=head1 AUTHORS
31
32The following people have contributed code to BusyBox whether they know it or
33not. If you have written code included in BusyBox, you should probably be
34listed here so you can obtain your bit of eternal glory. If you should be
35listed here, or the description of what you have done needs more detail, or is
36incorect, please send in an update.
37
38
39=for html <br>
40
41Emanuele Aina <emanuele.aina@tiscali.it>
42 run-parts
43
44=for html <br>
45
46Erik Andersen <andersen@codepoet.org>
47
48 Tons of new stuff, major rewrite of most of the
49 core apps, tons of new apps as noted in header files.
50 Lots of tedious effort writing these boring docs that
51 nobody is going to actually read.
52
53=for html <br>
54
55Laurence Anderson <l.d.anderson@warwick.ac.uk>
56
57 rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm
58
59=for html <br>
60
61Jeff Angielski <jeff@theptrgroup.com>
62
63 ftpput, ftpget
64
65=for html <br>
66
67Edward Betts <edward@debian.org>
68
69 expr, hostid, logname, whoami
70
71=for html <br>
72
73John Beppu <beppu@codepoet.org>
74
75 du, nslookup, sort
76
77=for html <br>
78
79Brian Candler <B.Candler@pobox.com>
80
81 tiny-ls(ls)
82
83=for html <br>
84
85Randolph Chung <tausq@debian.org>
86
87 fbset, ping, hostname
88
89=for html <br>
90
91Dave Cinege <dcinege@psychosis.com>
92
93 more(v2), makedevs, dutmp, modularization, auto links file,
94 various fixes, Linux Router Project maintenance
95
96=for html <br>
97
98Jordan Crouse <jordan@cosmicpenguin.net>
99
100 ipcalc
101
102=for html <br>
103
104Magnus Damm <damm@opensource.se>
105
106 tftp client insmod powerpc support
107
108=for html <br>
109
110Larry Doolittle <ldoolitt@recycle.lbl.gov>
111
112 pristine source directory compilation, lots of patches and fixes.
113
114=for html <br>
115
116Glenn Engel <glenne@engel.org>
117
118 httpd
119
120=for html <br>
121
122Gennady Feldman <gfeldman@gena01.com>
123
124 Sysklogd (single threaded syslogd, IPC Circular buffer support,
125 logread), various fixes.
126
127=for html <br>
128
129Karl M. Hegbloom <karlheg@debian.org>
130
131 cp_mv.c, the test suite, various fixes to utility.c, &c.
132
133=for html <br>
134
135Daniel Jacobowitz <dan@debian.org>
136
137 mktemp.c
138
139=for html <br>
140
141Matt Kraai <kraai@alumni.cmu.edu>
142
143 documentation, bugfixes, test suite
144
145=for html <br>
146
147Stephan Linz <linz@li-pro.net>
148
149 ipcalc, Red Hat equivalence
150
151=for html <br>
152
153John Lombardo <john@deltanet.com>
154
155 tr
156
157=for html <br>
158
159Glenn McGrath <bug1@iinet.net.au>
160
161 Common unarchving code and unarchiving applets, ifupdown, ftpgetput,
162 nameif, sed, patch, fold, install, uudecode.
163 Various bugfixes, review and apply numerous patches.
164
165=for html <br>
166
167Manuel Novoa III <mjn3@codepoet.org>
168
169 cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes,
170 mesg, vconfig, make_directory, parse_mode, dirname, mode_string,
171 get_last_path_component, simplify_path, and a number trivial libbb routines
172
173 also bug fixes, partial rewrites, and size optimizations in
174 ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir,
175 mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable,
176 interface, dutmp, ifconfig, route
177
178=for html <br>
179
180Vladimir Oleynik <dzo@simtreas.ru>
181
182 cmdedit; xargs(current), httpd(current);
183 ports: ash, crond, fdisk, inetd, stty, traceroute, top;
184 locale, various fixes
185 and irreconcilable critic of everything not perfect.
186
187=for html <br>
188
189Bruce Perens <bruce@pixar.com>
190
191 Original author of BusyBox in 1995, 1996. Some of his code can
192 still be found hiding here and there...
193
194=for html <br>
195
196Tim Riker <Tim@Rikers.org>
197
198 bug fixes, member of fan club
199
200=for html <br>
201
202Kent Robotti <robotti@metconnect.com>
203
204 reset, tons and tons of bug reports and patches.
205
206=for html <br>
207
208Chip Rosenthal <chip@unicom.com>, <crosenth@covad.com>
209
210 wget - Contributed by permission of Covad Communications
211
212=for html <br>
213
214Pavel Roskin <proski@gnu.org>
215
216 Lots of bugs fixes and patches.
217
218=for html <br>
219
220Gyepi Sam <gyepi@praxis-sw.com>
221
222 Remote logging feature for syslogd
223
224=for html <br>
225
226Linus Torvalds <torvalds@transmeta.com>
227
228 mkswap, fsck.minix, mkfs.minix
229
230=for html <br>
231
232Mark Whitley <markw@codepoet.org>
233
234 grep, sed, cut, xargs(previous),
235 style-guide, new-applet-HOWTO, bug fixes, etc.
236
237=for html <br>
238
239Charles P. Wright <cpwright@villagenet.com>
240
241 gzip, mini-netcat(nc)
242
243=for html <br>
244
245Enrique Zanardi <ezanardi@ull.es>
246
247 tarcat (since removed), loadkmap, various fixes, Debian maintenance
248
249=for html <br>
250
251Tito Ragusa <farmatito@tiscali.it>
252
253 devfsd and size optimizations in strings, openvt and deallocvt.
254
255=cut
256
257# $Id: busybox_footer.pod,v 1.18 2004/04/25 06:05:14 bug1 Exp $
258
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 @@
1# vi: set sw=4 ts=4:
2
3=head1 NAME
4
5BusyBox - The Swiss Army Knife of Embedded Linux
6
7=head1 SYNTAX
8
9 BusyBox <function> [arguments...] # or
10
11 <function> [arguments...] # if symlinked
12
13=head1 DESCRIPTION
14
15BusyBox combines tiny versions of many common UNIX utilities into a single
16small executable. It provides minimalist replacements for most of the utilities
17you usually find in GNU coreutils, util-linux, etc. The utilities in BusyBox
18generally have fewer options than their full-featured GNU cousins; however, the
19options that are included provide the expected functionality and behave very
20much like their GNU counterparts.
21
22BusyBox has been written with size-optimization and limited resources in mind.
23It is also extremely modular so you can easily include or exclude commands (or
24features) at compile time. This makes it easy to customize your embedded
25systems. To create a working system, just add /dev, /etc, and a Linux kernel.
26BusyBox provides a fairly complete POSIX environment for any small or embedded
27system.
28
29BusyBox is extremely configurable. This allows you to include only the
30components you need, thereby reducing binary size. Run 'make config' or 'make
31menuconfig' to select the functionality that you wish to enable. The run
32'make' to compile BusyBox using your configuration.
33
34After the compile has finished, you should use 'make install' to install
35BusyBox. This will install the '/bin/busybox' binary, and will also create
36symlinks pointing to the '/bin/busybox' binary for each utility that you
37compile into BusyBox. By default, 'make install' will place these symlinks
38into the './_install' directory, unless you have defined 'PREFIX', thereby
39specifying some alternative location (i.e., 'make PREFIX=/tmp/foo install').
40If you wish to install using hardlinks, rather than the default of using
41symlinks, you can use 'make PREFIX=/tmp/foo install-hardlinks' instead.
42
43=head1 USAGE
44
45BusyBox is a multi-call binary. A multi-call binary is an executable program
46that performs the same job as more than one utility program. That means there
47is just a single BusyBox binary, but that single binary acts like a large
48number of utilities. This allows BusyBox to be smaller since all the built-in
49utility programs (we call them applets) can share code for many common operations.
50
51You can also invoke BusyBox by issuing a command as an argument on the
52command line. For example, entering
53
54 /bin/busybox ls
55
56will also cause BusyBox to behave as 'ls'.
57
58Of course, adding '/bin/busybox' into every command would be painful. So most
59people will invoke BusyBox using links to the BusyBox binary.
60
61For example, entering
62
63 ln -s /bin/busybox ls
64 ./ls
65
66will cause BusyBox to behave as 'ls' (if the 'ls' command has been compiled
67into BusyBox). Generally speaking, you should never need to make all these
68links yourself, as the BusyBox build system will do this for you when you run
69the 'make install' command.
70
71If you invoke BusyBox with no arguments, it will provide you with a list of the
72applets that have been compiled into your BusyBox binary.
73
74=head1 COMMON OPTIONS
75
76Most BusyBox commands support the B<--help> argument to provide a terse runtime
77description of their behavior. If the CONFIG_FEATURE_VERBOSE_USAGE option has
78been enabled, more detailed usage information will also be available.
79
80=head1 COMMANDS
81
82Currently defined functions include:
83
84 addgroup, adduser, adjtimex, ar, arping, ash, awk, basename, bunzip2,
85 busybox, bzcat, cal, cat, chgrp, chmod, chown, chroot, chvt, clear, cmp,
86 cp, cpio, crond, crontab, cut, date, dc, dd, deallocvt, delgroup, deluser,
87 devfsd, df, dirname, dmesg, dos2unix, dpkg, dpkg-deb, du, dumpkmap,
88 dumpleases, echo, egrep, env, expr, false, fbset, fdflush, fdformat, fdisk,
89 fgrep, find, fold, free, freeramdisk, fsck.minix, ftpget, ftpput, getopt,
90 getty, grep, gunzip, gzip, halt, hdparm, head, hexdump, hostid, hostname,
91 httpd, hush, hwclock, id, ifconfig, ifdown, ifup, inetd, init, insmod,
92 install, ip, ipaddr, ipcalc, iplink, iproute, iptunnel, kill, killall,
93 klogd, lash, last, length, linuxrc, ln, loadfont, loadkmap, logger, login,
94 logname, logread, losetup, ls, lsmod, makedevs, md5sum, mesg, mkdir,
95 mkfifo, mkfs.minix, mknod, mkswap, mktemp, modprobe, more, mount, msh, mt,
96 mv, nameif, nc, netstat, nslookup, od, openvt, passwd, patch, pidof, ping,
97 ping6, pipe_progress, pivot_root, poweroff, printf, ps, pwd, rdate,
98 readlink, realpath, reboot, renice, reset, rm, rmdir, rmmod, route, rpm,
99 rpm2cpio, run-parts, rx, sed, seq, setkeycodes, sha1sum, sleep, sort,
100 start-stop-daemon, strings, stty, su, sulogin, swapoff, swapon, sync,
101 sysctl, syslogd, tail, tar, tee, telnet, telnetd, test, tftp, time, top,
102 touch, tr, traceroute, true, tty, udhcpc, udhcpd, umount, uname,
103 uncompress, uniq, unix2dos, unzip, uptime, usleep, uudecode, uuencode,
104 vconfig, vi, vlock, watch, watchdog, wc, wget, which, who, whoami, xargs,
105 yes, zcat
106
107=head1 COMMAND DESCRIPTIONS
108
109=over 4
110
111
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 @@
1Contributing To Busybox
2=======================
3
4This document describes what you need to do to contribute to Busybox, where
5you can help, guidelines on testing, and how to submit a well-formed patch
6that is more likely to be accepted.
7
8The Busybox home page is at: http://busybox.net/
9
10
11
12Pre-Contribution Checklist
13--------------------------
14
15So you want to contribute to Busybox, eh? Great, wonderful, glad you want to
16help. However, before you dive in, headlong and hotfoot, there are some things
17you need to do:
18
19
20Checkout the Latest Code from CVS
21~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22
23This is a necessary first step. Please do not try to work with the last
24released version, as there is a good chance that somebody has already fixed
25the bug you found. Somebody might have even added the feature you had in mind.
26Don't make your work obsolete before you start!
27
28For information on how to check out Busybox from CVS, please look at the
29following links:
30
31 http://busybox.net/cvs_anon.html
32 http://busybox.net/cvs_howto.html
33
34
35Read the Mailing List
36~~~~~~~~~~~~~~~~~~~~~
37
38No one is required to read the entire archives of the mailing list, but you
39should at least read up on what people have been talking about lately. If
40you've recently discovered a problem, chances are somebody else has too. If
41you're the first to discover a problem, post a message and let the rest of us
42know.
43
44Archives can be found here:
45
46 http://busybox.net/lists/busybox/
47
48If you have a serious interest in Busybox, i.e., you are using it day-to-day or
49as part of an embedded project, it would be a good idea to join the mailing
50list.
51
52A web-based sign-up form can be found here:
53
54 http://busybox.net/mailman/listinfo/busybox
55
56
57Coordinate with the Applet Maintainer
58~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
59
60Some (not all) of the applets in Busybox are "owned" by a maintainer who has
61put significant effort into it and is probably more familiar with it than
62others. To find the maintainer of an applet, look at the top of the .c file
63for a name following the word 'Copyright' or 'Written by' or 'Maintainer'.
64
65Before plunging ahead, it's a good idea to send a message to the mailing list
66that says: "Hey, I was thinking about adding the 'transmogrify' feature to the
67'foo' applet. Would this be useful? Is anyone else working on it?" You might
68want to CC the maintainer (if any) with your question.
69
70
71
72Areas Where You Can Help
73------------------------
74
75Busybox can always use improvement! If you're looking for ways to help, there
76are a variety of areas where you could help.
77
78
79What Busybox Doesn't Need
80~~~~~~~~~~~~~~~~~~~~~~~~~
81
82Before listing the areas where you _can_ help, it's worthwhile to mention the
83areas where you shouldn't bother. While Busybox strives to be the "Swiss Army
84Knife" of embedded Linux, there are some applets that will not be accepted:
85
86 - Any filesystem manipulation tools: Busybox is filesystem independent and
87 we do not want to start adding mkfs/fsck tools for every (or any)
88 filesystem under the sun. (fsck_minix.c and mkfs_minix.c are living on
89 borrowed time.) There are far too many of these tools out there. Use
90 the upstream version. Not everything has to be part of Busybox.
91
92 - Any partitioning tools: Partitioning a device is typically done once and
93 only once, and tools which do this generally do not need to reside on the
94 target device (esp a flash device). If you need a partitioning tool, grab
95 one (such as fdisk, sfdisk, or cfdisk from util-linux) and use that, but
96 don't try to merge it into busybox. These are nasty and complex and we
97 don't want to maintain them.
98
99 - Any disk, device, or media-specific tools: Use the -utils or -tools package
100 that was designed for your device; don't try to shoehorn them into Busybox.
101
102 - Any architecture specific tools: Busybox is (or should be) architecture
103 independent. Do not send us tools that cannot be used across multiple
104 platforms / arches.
105
106 - Any daemons that are not essential to basic system operation. To date, only
107 syslogd and klogd meet this requirement. We do not need a web server, an
108 ftp daemon, a dhcp server, a mail transport agent or a dns resolver. If you
109 need one of those, you are welcome to ask the folks on the mailing list for
110 recommendations, but please don't bloat up Busybox with any of these.
111
112
113Bug Reporting
114~~~~~~~~~~~~~
115
116If you find bugs, please submit a detailed bug report to the busybox mailing
117list at busybox@busybox.net. A well-written bug report should include a
118transcript of a shell session that demonstrates the bad behavior and enables
119anyone else to duplicate the bug on their own machine. The following is such
120an example:
121
122 To: busybox@busybox.net
123 From: diligent@testing.linux.org
124 Subject: /bin/date doesn't work
125
126 Package: busybox
127 Version: 1.00
128
129 When I execute Busybox 'date' it produces unexpected results.
130 With GNU date I get the following output:
131
132 $ date
133 Wed Mar 21 14:19:41 MST 2001
134
135 But when I use BusyBox date I get this instead:
136
137 $ date
138 llegal instruction
139
140 I am using Debian unstable, kernel version 2.4.19-rmk1 on an Netwinder,
141 and the latest uClibc from CVS. Thanks for the wonderful program!
142
143 -Diligent
144
145Note the careful description and use of examples showing not only what BusyBox
146does, but also a counter example showing what an equivalent GNU app does. Bug
147reports lacking such detail may never be fixed... Thanks for understanding.
148
149
150
151Write Documentation
152~~~~~~~~~~~~~~~~~~~
153
154Chances are, documentation in Busybox is either missing or needs improvement.
155Either way, help is welcome.
156
157Work is being done to automatically generate documentation from sources,
158especially from the usage.h file. If you want to correct the documentation,
159please make changes to the pre-generation parts, rather than the generated
160documentation. [More to come on this later...]
161
162It is preferred that modifications to documentation be submitted in patch
163format (more on this below), but we're a little more lenient when it comes to
164docs. You could, for example, just say "after the listing of the mount
165options, the following example would be helpful..."
166
167
168Consult Existing Sources
169~~~~~~~~~~~~~~~~~~~~~~~~
170
171For a quick listing of "needs work" spots in the sources, cd into the Busybox
172directory and run the following:
173
174 for i in TODO FIXME XXX; do find -name '*.[ch]'|xargs grep $i; done
175
176This will show all of the trouble spots or 'questionable' code. Pick a spot,
177any spot, these are all invitations for you to contribute.
178
179
180Add a New Applet
181~~~~~~~~~~~~~~~~
182
183If you want to add a new applet to Busybox, we'd love to see it. However,
184before you write any code, please ask beforehand on the mailing list something
185like "Do you think applet 'foo' would be useful in Busybox?" or "Would you
186guys accept applet 'foo' into Busybox if I were to write it?" If the answer is
187"no" by the folks on the mailing list, then you've saved yourself some time.
188Conversely, you could get some positive responses from folks who might be
189interested in helping you implement it, or can recommend the best approach.
190Perhaps most importantly, this is your way of calling "dibs" on something and
191avoiding duplication of effort.
192
193Also, before you write a line of code, please read the 'new-applet-HOWTO.txt'
194file in the docs/ directory.
195
196
197Janitorial Work
198~~~~~~~~~~~~~~~
199
200These are dirty jobs, but somebody's gotta do 'em.
201
202 - Converting applets to use getopt() for option processing. Type 'find -name
203 '*.c'|grep -L getopt' to get a listing of the applets that currently don't
204 use getopt. If a .c file processes no options, it should have a line that
205 reads: /* no options, no getopt */ somewhere in the file.
206
207 - Replace any "naked" calls to malloc, calloc, realloc, str[n]dup, fopen with
208 the x* equivalents found in libbb/xfuncs.c.
209
210 - Security audits:
211 http://www.securityfocus.com/popups/forums/secprog/intro.shtml
212
213 - Synthetic code removal: http://www.perl.com/pub/2000/06/commify.html - This
214 is very Perl-specific, but the advice given in here applies equally well to
215 C.
216
217 - C library function use audits: Verifying that functions are being used
218 properly (called with the right args), replacing unsafe library functions
219 with safer versions, making sure return codes are being checked, etc.
220
221 - Where appropriate, replace preprocessor defined macros and values with
222 compile-time equivalents.
223
224 - Style guide compliance. See: docs/style-guide.txt
225
226 - Add testcases to tests/testcases.
227
228 - Makefile improvements:
229 http://www.canb.auug.org.au/~millerp/rmch/recu-make-cons-harm.html
230 (I think the recursive problems are pretty much taken care of at this point, non?)
231
232 - "Ten Commandments" compliance: (this is a "maybe", certainly not as
233 important as any of the previous items.)
234 http://www.lysator.liu.se/c/ten-commandments.html
235
236Other useful links:
237
238 - the comp.lang.c FAQ: http://web.onetelnet.ch/~twolf/tw/c/index.html#Sources
239
240
241
242Submitting Patches To Busybox
243-----------------------------
244
245Here are some guidelines on how to submit a patch to Busybox.
246
247
248Making A Patch
249~~~~~~~~~~~~~~
250
251If you've got anonymous CVS access set up, making a patch is simple. Just make
252sure you're in the busybox/ directory and type 'cvs diff -bwu > mychanges.patch'.
253You can send the resulting .patch file to the mailing list with a description
254of what it does. (But not before you test it! See the next section for some
255guidelines.) It is preferred that patches be sent as attachments, but it is
256not required.
257
258Also, feel free to help test other people's patches and reply to them with
259comments. You can apply a patch by saving it into your busybox/ directory and
260typing 'patch < mychanges.patch'. Then you can recompile, see if it runs, test
261if it works as advertised, and post your findings to the mailing list.
262
263NOTE: Please do not include extraneous or irrelevant changes in your patches.
264Please do not try to "bundle" two patches together into one. Make single,
265discreet changes on a per-patch basis. Sometimes you need to make a patch that
266touches code in many places, but these kind of patches are rare and should be
267coordinated with a maintainer.
268
269
270Testing Guidelines
271~~~~~~~~~~~~~~~~~~
272
273It's considered good form to test your new feature before you submit a patch
274to the mailing list, and especially before you commit a change to CVS. Here
275are some guidelines on how to test your changes.
276
277 - Always test Busybox applets against GNU counterparts and make sure the
278 behavior / output is identical between the two.
279
280 - Try several different permutations and combinations of the features you're
281 adding (i.e., different combinations of command-line switches) and make sure
282 they all work; make sure one feature does not interfere with another.
283
284 - Make sure you test compiling against the source both with the feature
285 turned on and turned off in Config.h and make sure Busybox compiles cleanly
286 both ways.
287
288 - Run the multibuild.pl script in the tests directory and make sure
289 everything checks out OK. (Do this from within the busybox/ directory by
290 typing: 'tests/multibuild.pl'.)
291
292
293Making Sure Your Patch Doesn't Get Lost
294~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
295
296If you don't want your patch to be lost or forgotten, send it to the busybox
297mailing list with a subject line something like this:
298
299 [PATCH] - Adds "transmogrify" feature to "foo"
300
301In the body, you should have a pseudo-header that looks like the following:
302
303 Package: busybox
304 Version: v1.01pre (or whatever the current version is)
305 Severity: wishlist
306
307The remainder of the body should read along these lines:
308
309 This patch adds the "transmogrify" feature to the "foo" applet. I have
310 tested this on [arch] system(s) and it works. I have tested it against the
311 GNU counterparts and the outputs are identical. I have run the scripts in
312 the 'tests' directory and nothing breaks.
313
314
315
316Improving Your Chances of Patch Acceptance
317------------------------------------------
318
319Even after you send a brilliant patch to the mailing list, sometimes it can go
320unnoticed, un-replied-to, and sometimes (sigh) even lost. This is an
321unfortunate fact of life, but there are steps you can take to help your patch
322get noticed and convince a maintainer that it should be added:
323
324
325Be Succinct
326~~~~~~~~~~~
327
328A patch that includes small, isolated, obvious changes is more likely to be
329accepted than a patch that touches code in lots of different places or makes
330sweeping, dubious changes.
331
332
333Back It Up
334~~~~~~~~~~
335
336Hard facts on why your patch is better than the existing code will go a long
337way toward convincing maintainers that your patch should be included.
338Specifically, patches are more likely to be accepted if they are provably more
339correct, smaller, faster, simpler, or more maintainable than the existing
340code.
341
342Conversely, any patch that is supported with nothing more than "I think this
343would be cool" or "this patch is good because I say it is and I've got a Phd
344in Computer Science" will likely be ignored.
345
346
347Follow The Style Guide
348~~~~~~~~~~~~~~~~~~~~~~
349
350It's considered good form to abide by the established coding style used in a
351project; Busybox is no exception. We have gone so far as to delineate the
352"elements of Busybox style" in the file docs/style-guide.txt. Please follow
353them.
354
355
356Work With Someone Else
357~~~~~~~~~~~~~~~~~~~~~~
358
359Working on a patch in isolation is less effective than working with someone
360else for a variety of reasons. If another Busybox user is interested in what
361you're doing, then it's two (or more) voices instead of one that can petition
362for inclusion of the patch. You'll also have more people that can test your
363changes, or even offer suggestions on better approaches you could take.
364
365Getting other folks interested follows as a natural course if you've received
366responses from queries to applet maintainer or positive responses from folks
367on the mailing list.
368
369We've made strident efforts to put a useful "collaboration" infrastructure in
370place in the form of mailing lists, the bug tracking system, and CVS. Please
371use these resources.
372
373
374Send Patches to the Bug Tracking System
375~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
376
377This was mentioned above in the "Making Sure Your Patch Doesn't Get Lost"
378section, but it is worth mentioning again. A patch sent to the mailing list
379might be unnoticed and forgotten. A patch sent to the bug tracking system will
380be stored and closely connected to the bug it fixes.
381
382
383Be Polite
384~~~~~~~~~
385
386The old saying "You'll catch more flies with honey than you will with vinegar"
387applies when submitting patches to the mailing list for approval. The way you
388present your patch is sometimes just as important as the actual patch itself
389(if not more so). Being rude to the maintainers is not an effective way to
390convince them that your patch should be included; it will likely have the
391opposite effect.
392
393
394
395Committing Changes to CVS
396-------------------------
397
398If you submit several patches that demonstrate that you are a skilled and wise
399coder, you may be invited to become a committer, thus enabling you to commit
400changes directly to CVS. This is nice because you don't have to wait for
401someone else to commit your change for you, you can just do it yourself.
402
403But note that this is a priviledge that comes with some responsibilities. You
404should test your changes before you commit them. You should also talk to an
405applet maintainer before you make any kind of sweeping changes to somebody
406else's code. Big changes should still go to the mailing list first. Remember,
407being wise, polite, and discreet is more important than being clever.
408
409
410When To Commit
411~~~~~~~~~~~~~~
412
413Generally, you should feel free to commit a change if:
414
415 - Your changes are small and don't touch many files
416 - You are fixing a bug
417 - Somebody has told you that it's okay
418 - It's obviously the Right Thing
419
420The more of the above are true, the better it is to just commit a change
421directly to CVS.
422
423
424When Not To Commit
425~~~~~~~~~~~~~~~~~~
426
427Even if you have commit rights, you should probably still post a patch to the
428mailing list if:
429
430 - Your changes are broad and touch many different files
431 - You are adding a feature
432 - Your changes are speculative or experimental (i.e., trying a new algorithm)
433 - You are not the maintainer and your changes make the maintainer cringe
434
435The more of the above are true, the better it is to post a patch to the
436mailing list instead of committing.
437
438
439
440Final Words
441-----------
442
443If all of this seems complicated, don't panic, it's really not that tough. If
444you're having difficulty following some of the steps outlined in this
445document don't worry, the folks on the Busybox mailing list are a fairly
446good-natured bunch and will work with you to help get your patches into shape
447or help you make contributions.
448
449
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 @@
1How to Add a New Applet to BusyBox
2==================================
3
4This document details the steps you must take to add a new applet to BusyBox.
5
6Credits:
7Matt Kraai - initial writeup
8Mark Whitley - the remix
9Thomas Lundquist - Added stuff for the new directory layout.
10
11Initial Write
12-------------
13
14First, write your applet. Be sure to include copyright information at the
15top, such as who you stole the code from and so forth. Also include the
16mini-GPL boilerplate. Be sure to name the main function <applet>_main instead
17of main. And be sure to put it in <applet>.c. Usage do not have to be taken care of by your applet.
18
19For a new applet mu, here is the code that would go in mu.c:
20
21----begin example code------
22
23/* vi: set sw=4 ts=4: */
24/*
25 * Mini mu implementation for busybox
26 *
27 *
28 * Copyright (C) [YEAR] by [YOUR NAME] <YOUR EMAIL>
29 *
30 * This program is free software; you can redistribute it and/or modify
31 * it under the terms of the GNU General Public License as published by
32 * the Free Software Foundation; either version 2 of the License, or
33 * (at your option) any later version.
34 *
35 * This program is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
38 * General Public License for more details.
39 *
40 * You should have received a copy of the GNU General Public License
41 * along with this program; if not, write to the Free Software
42 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
43 * 02111-1307 USA
44 *
45 */
46
47#include "busybox.h"
48
49int mu_main(int argc, char **argv)
50{
51 int fd;
52 char mu;
53
54 if ((fd = open("/dev/random", O_RDONLY)) < 0)
55 perror_msg_and_die("/dev/random");
56
57 if ((n = safe_read(fd, &mu, 1)) < 1)
58 perror_msg_and_die("/dev/random");
59
60 return mu;
61}
62
63----end example code------
64
65
66Coding Style
67------------
68
69Before you submit your applet for inclusion in BusyBox, (or better yet, before
70you _write_ your applet) please read through the style guide in the docs
71directory and make your program compliant.
72
73
74Some Words on libbb
75-------------------
76
77As you are writing your applet, please be aware of the body of pre-existing
78useful functions in libbb. Use these instead of reinventing the wheel.
79
80Additionally, if you have any useful, general-purpose functions in your
81program that could be useful in another program, consider putting them in
82libbb.
83
84
85Placement / Directory
86---------------------
87
88Find the appropriate directory for your new applet.
89
90Make sure you find the appropriate places in the files, the applets are
91sorted alphabetically.
92
93Add the applet to Makefile.in in the chosen applet directory:
94
95obj-$(CONFIG_MU) += mu.o
96
97Add the applet to Config.in in the chosen applet directory:
98
99config CONFIG_MU
100 bool "MU"
101 default n
102 help
103 Returns an indeterminate value.
104
105
106Usage String(s)
107---------------
108
109Next, add usage information for you applet to include/usage.h.
110This should look like the following:
111
112 #define mu_trivial_usage \
113 "-[abcde] FILES"
114 #define mu_full_usage \
115 "Returns an indeterminate value.\n\n" \
116 "Options:\n" \
117 "\t-a\t\tfirst function\n" \
118 "\t-b\t\tsecond function\n" \
119
120If your program supports flags, the flags should be mentioned on the first
121line (-[abcde]) and a detailed description of each flag should go in the
122mu_full_usage section, one flag per line. (Numerous examples of this
123currently exist in usage.h.)
124
125
126Header Files
127------------
128
129Next, add an entry to include/applets.h. Be *sure* to keep the list
130in alphabetical order, or else it will break the binary-search lookup
131algorithm in busybox.c and the Gods of BusyBox smite you. Yea, verily:
132
133 /* all programs above here are alphabetically "less than" 'mu' */
134 #ifdef CONFIG_MU
135 APPLET("mu", mu_main, _BB_DIR_USR_BIN, mu_usage)
136 #endif
137 /* all programs below here are alphabetically "greater than" 'mu' */
138
139
140Finally, add a define for your applet to include/config.h
141
142 #undef CONFIG_MU
143
144
145Documentation
146-------------
147
148If you're feeling especially nice, you should also document your applet in the
149docs directory (but nobody ever does that).
150
151Adding some text to docs/Configure.help is a nice start.
152
153
154The Grand Announcement
155----------------------
156
157Then create a diff -urN of the files you added (<appletdir/><applet>.c,
158include/usage.c, include/applets.h, include/config.h, <appletdir>/Makefile.in, <appletdir>/config.in)
159and send it to the mailing list:
160busybox@busybox.net.
161
162Sending patches as attachments is preferred, but not required.
163
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 @@
1Busybox Style Guide
2===================
3
4This document describes the coding style conventions used in Busybox. If you
5add a new file to Busybox or are editing an existing file, please format your
6code according to this style. If you are the maintainer of a file that does
7not follow these guidelines, please -- at your own convenience -- modify the
8file(s) you maintain to bring them into conformance with this style guide.
9Please note that this is a low priority task.
10
11To help you format the whitespace of your programs, an ".indent.pro" file is
12included in the main Busybox source directory that contains option flags to
13format code as per this style guide. This way you can run GNU indent on your
14files by typing 'indent myfile.c myfile.h' and it will magically apply all the
15right formatting rules to your file. Please _do_not_ run this on all the files
16in the directory, just your own.
17
18
19
20Declaration Order
21-----------------
22
23Here is the order in which code should be laid out in a file:
24
25 - commented program name and one-line description
26 - commented author name and email address(es)
27 - commented GPL boilerplate
28 - commented longer description / notes for the program (if needed)
29 - #includes of .h files with angle brackets (<>) around them
30 - #includes of .h files with quotes ("") around them
31 - #defines (if any, note the section below titled "Avoid the Preprocessor")
32 - const and global variables
33 - function declarations (if necessary)
34 - function implementations
35
36
37
38Whitespace and Formatting
39-------------------------
40
41This is everybody's favorite flame topic so let's get it out of the way right
42up front.
43
44
45Tabs vs. Spaces in Line Indentation
46~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
47
48The preference in Busybox is to indent lines with tabs. Do not indent lines
49with spaces and do not indents lines using a mixture of tabs and spaces. (The
50indentation style in the Apache and Postfix source does this sort of thing:
51\s\s\s\sif (expr) {\n\tstmt; --ick.) The only exception to this rule is
52multi-line comments that use an asterisk at the beginning of each line, i.e.:
53
54 /t/*
55 /t * This is a block comment.
56 /t * Note that it has multiple lines
57 /t * and that the beginning of each line has a tab plus a space
58 /t * except for the opening '/*' line where the slash
59 /t * is used instead of a space.
60 /t */
61
62Furthermore, The preference is that tabs be set to display at four spaces
63wide, but the beauty of using only tabs (and not spaces) at the beginning of
64lines is that you can set your editor to display tabs at *whatever* number of
65spaces is desired and the code will still look fine.
66
67
68Operator Spacing
69~~~~~~~~~~~~~~~~
70
71Put spaces between terms and operators. Example:
72
73 Don't do this:
74
75 for(i=0;i<num_items;i++){
76
77 Do this instead:
78
79 for (i = 0; i < num_items; i++) {
80
81 While it extends the line a bit longer, the spaced version is more
82 readable. An allowable exception to this rule is the situation where
83 excluding the spacing makes it more obvious that we are dealing with a
84 single term (even if it is a compound term) such as:
85
86 if (str[idx] == '/' && str[idx-1] != '\\')
87
88 or
89
90 if ((argc-1) - (optind+1) > 0)
91
92
93Bracket Spacing
94~~~~~~~~~~~~~~~
95
96If an opening bracket starts a function, it should be on the
97next line with no spacing before it. However, if a bracket follows an opening
98control block, it should be on the same line with a single space (not a tab)
99between it and the opening control block statement. Examples:
100
101 Don't do this:
102
103 while (!done)
104 {
105
106 do
107 {
108
109 Don't do this either:
110
111 while (!done){
112
113 do{
114
115 And for heaven's sake, don't do this:
116
117 while (!done)
118 {
119
120 do
121 {
122
123 Do this instead:
124
125 while (!done) {
126
127 do {
128
129
130Spacing around Parentheses
131~~~~~~~~~~~~~~~~~~~~~~~~~~
132
133Put a space between C keywords and left parens, but not between function names
134and the left paren that starts it's parameter list (whether it is being
135declared or called). Examples:
136
137 Don't do this:
138
139 while(foo) {
140 for(i = 0; i < n; i++) {
141
142 Do this instead:
143
144 while (foo) {
145 for (i = 0; i < n; i++) {
146
147 But do functions like this:
148
149 static int my_func(int foo, char bar)
150 ...
151 baz = my_func(1, 2);
152
153Also, don't put a space between the left paren and the first term, nor between
154the last arg and the right paren.
155
156 Don't do this:
157
158 if ( x < 1 )
159 strcmp( thisstr, thatstr )
160
161 Do this instead:
162
163 if (x < 1)
164 strcmp(thisstr, thatstr)
165
166
167Cuddled Elses
168~~~~~~~~~~~~~
169
170Also, please "cuddle" your else statements by putting the else keyword on the
171same line after the right bracket that closes an 'if' statement.
172
173 Don't do this:
174
175 if (foo) {
176 stmt;
177 }
178 else {
179 stmt;
180 }
181
182 Do this instead:
183
184 if (foo) {
185 stmt;
186 } else {
187 stmt;
188 }
189
190The exception to this rule is if you want to include a comment before the else
191block. Example:
192
193 if (foo) {
194 stmts...
195 }
196 /* otherwise, we're just kidding ourselves, so re-frob the input */
197 else {
198 other_stmts...
199 }
200
201
202
203Variable and Function Names
204---------------------------
205
206Use the K&R style with names in all lower-case and underscores occasionally
207used to separate words (e.g., "variable_name" and "numchars" are both
208acceptable). Using underscores makes variable and function names more readable
209because it looks like whitespace; using lower-case is easy on the eyes.
210
211 Frowned upon:
212
213 hitList
214 TotalChars
215 szFileName
216 pf_Nfol_TriState
217
218 Preferred:
219
220 hit_list
221 total_chars
222 file_name
223 sensible_name
224
225Exceptions:
226
227 - Enums, macros, and constant variables are occasionally written in all
228 upper-case with words optionally seperatedy by underscores (i.e. FIFOTYPE,
229 ISBLKDEV()).
230
231 - Nobody is going to get mad at you for using 'pvar' as the name of a
232 variable that is a pointer to 'var'.
233
234
235Converting to K&R
236~~~~~~~~~~~~~~~~~
237
238The Busybox codebase is very much a mixture of code gathered from a variety of
239sources. This explains why the current codebase contains such a hodge-podge of
240different naming styles (Java, Pascal, K&R, just-plain-weird, etc.). The K&R
241guideline explained above should therefore be used on new files that are added
242to the repository. Furthermore, the maintainer of an existing file that uses
243alternate naming conventions should, at his own convenience, convert those
244names over to K&R style. Converting variable names is a very low priority
245task.
246
247If you want to do a search-and-replace of a single variable name in different
248files, you can do the following in the busybox directory:
249
250 $ perl -pi -e 's/\bOldVar\b/new_var/g' *.[ch]
251
252If you want to convert all the non-K&R vars in your file all at once, follow
253these steps:
254
255 - In the busybox directory type 'examples/mk2knr.pl files-to-convert'. This
256 does not do the actual conversion, rather, it generates a script called
257 'convertme.pl' that shows what will be converted, giving you a chance to
258 review the changes beforehand.
259
260 - Review the 'convertme.pl' script that gets generated in the busybox
261 directory and remove / edit any of the substitutions in there. Please
262 especially check for false positives (strings that should not be
263 converted).
264
265 - Type './convertme.pl same-files-as-before' to perform the actual
266 conversion.
267
268 - Compile and see if everything still works.
269
270Please be aware of changes that have cascading effects into other files. For
271example, if you're changing the name of something in, say utility.c, you
272should probably run 'examples/mk2knr.pl utility.c' at first, but when you run
273the 'convertme.pl' script you should run it on _all_ files like so:
274'./convertme.pl *.[ch]'.
275
276
277
278Avoid The Preprocessor
279----------------------
280
281At best, the preprocessor is a necessary evil, helping us account for platform
282and architecture differences. Using the preprocessor unnecessarily is just
283plain evil.
284
285
286The Folly of #define
287~~~~~~~~~~~~~~~~~~~~
288
289Use 'const <type> var' for declaring constants.
290
291 Don't do this:
292
293 #define var 80
294
295 Do this instead, when the variable is in a header file and will be used in
296 several source files:
297
298 const int var = 80;
299
300 Or do this when the variable is used only in a single source file:
301
302 static const int var = 80;
303
304Declaring variables as '[static] const' gives variables an actual type and
305makes the compiler do type checking for you; the preprocessor does _no_ type
306checking whatsoever, making it much more error prone. Declaring variables with
307'[static] const' also makes debugging programs much easier since the value of
308the variable can be easily queried and displayed.
309
310
311The Folly of Macros
312~~~~~~~~~~~~~~~~~~~
313
314Use 'static inline' instead of a macro.
315
316 Don't do this:
317
318 #define mini_func(param1, param2) (param1 << param2)
319
320 Do this instead:
321
322 static inline int mini_func(int param1, param2)
323 {
324 return (param1 << param2);
325 }
326
327Static inline functions are greatly preferred over macros. They provide type
328safety, have no length limitations, no formatting limitations, have an actual
329return value, and under gcc they are as cheap as macros. Besides, really long
330macros with backslashes at the end of each line are ugly as sin.
331
332
333The Folly of #ifdef
334~~~~~~~~~~~~~~~~~~~
335
336Code cluttered with ifdefs is difficult to read and maintain. Don't do it.
337Instead, put your ifdefs at the top of your .c file (or in a header), and
338conditionally define 'static inline' functions, (or *maybe* macros), which are
339used in the code.
340
341 Don't do this:
342
343 ret = my_func(bar, baz);
344 if (!ret)
345 return -1;
346 #ifdef CONFIG_FEATURE_FUNKY
347 maybe_do_funky_stuff(bar, baz);
348 #endif
349
350 Do this instead:
351
352 (in .h header file)
353
354 #ifdef CONFIG_FEATURE_FUNKY
355 static inline void maybe_do_funky_stuff (int bar, int baz)
356 {
357 /* lotsa code in here */
358 }
359 #else
360 static inline void maybe_do_funky_stuff (int bar, int baz) {}
361 #endif
362
363 (in the .c source file)
364
365 ret = my_func(bar, baz);
366 if (!ret)
367 return -1;
368 maybe_do_funky_stuff(bar, baz);
369
370The great thing about this approach is that the compiler will optimize away
371the "no-op" case (the empty function) when the feature is turned off.
372
373Note also the use of the word 'maybe' in the function name to indicate
374conditional execution.
375
376
377
378Notes on Strings
379----------------
380
381Strings in C can get a little thorny. Here's some guidelines for dealing with
382strings in Busybox. (There is surely more that could be added to this
383section.)
384
385
386String Files
387~~~~~~~~~~~~
388
389Put all help/usage messages in usage.c. Put other strings in messages.c.
390Putting these strings into their own file is a calculated decision designed to
391confine spelling errors to a single place and aid internationalization
392efforts, if needed. (Side Note: we might want to use a single file - maybe
393called 'strings.c' - instead of two, food for thought).
394
395
396Testing String Equivalence
397~~~~~~~~~~~~~~~~~~~~~~~~~~
398
399There's a right way and a wrong way to test for sting equivalence with
400strcmp():
401
402 The wrong way:
403
404 if (!strcmp(string, "foo")) {
405 ...
406
407 The right way:
408
409 if (strcmp(string, "foo") == 0){
410 ...
411
412The use of the "equals" (==) operator in the latter example makes it much more
413obvious that you are testing for equivalence. The former example with the
414"not" (!) operator makes it look like you are testing for an error. In a more
415perfect world, we would have a streq() function in the string library, but
416that ain't the world we're living in.
417
418
419Avoid Dangerous String Functions
420~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
421
422Unfortunately, the way C handles strings makes them prone to overruns when
423certain library functions are (mis)used. The following table offers a summary
424of some of the more notorious troublemakers:
425
426function overflows preferred
427----------------------------------------
428strcpy dest string strncpy
429strcat dest string strncat
430gets string it gets fgets
431getwd buf string getcwd
432[v]sprintf str buffer [v]snprintf
433realpath path buffer use with pathconf
434[vf]scanf its arguments just avoid it
435
436
437The above is by no means a complete list. Be careful out there.
438
439
440
441Avoid Big Static Buffers
442------------------------
443
444First, some background to put this discussion in context: Static buffers look
445like this in code:
446
447 /* in a .c file outside any functions */
448 static char *buffer[BUFSIZ]; /* happily used by any function in this file,
449 but ick! big! */
450
451The problem with these is that any time any busybox app is run, you pay a
452memory penalty for this buffer, even if the applet that uses said buffer is
453not run. This can be fixed, thusly:
454
455 static char *buffer;
456 ...
457 other_func()
458 {
459 strcpy(buffer, lotsa_chars); /* happily uses global *buffer */
460 ...
461 foo_main()
462 {
463 buffer = xmalloc(sizeof(char)*BUFSIZ);
464 ...
465
466However, this approach trades bss segment for text segment. Rather than
467mallocing the buffers (and thus growing the text size), buffers can be
468declared on the stack in the *_main() function and made available globally by
469assigning them to a global pointer thusly:
470
471 static char *pbuffer;
472 ...
473 other_func()
474 {
475 strcpy(pbuffer, lotsa_chars); /* happily uses global *pbuffer */
476 ...
477 foo_main()
478 {
479 char *buffer[BUFSIZ]; /* declared locally, on stack */
480 pbuffer = buffer; /* but available globally */
481 ...
482
483This last approach has some advantages (low code size, space not used until
484it's needed), but can be a problem in some low resource machines that have
485very limited stack space (e.g., uCLinux).
486
487A macro is declared in busybox.h that implements compile-time selection
488between xmalloc() and stack creation, so you can code the line in question as
489
490 RESERVE_CONFIG_BUFFER(buffer, BUFSIZ);
491
492and the right thing will happen, based on your configuration.
493
494
495
496Miscellaneous Coding Guidelines
497-------------------------------
498
499The following are important items that don't fit into any of the above
500sections.
501
502
503Model Busybox Applets After GNU Counterparts
504~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
505
506When in doubt about the proper behavior of a Busybox program (output,
507formatting, options, etc.), model it after the equivalent GNU program.
508Doesn't matter how that program behaves on some other flavor of *NIX; doesn't
509matter what the POSIX standard says or doesn't say, just model Busybox
510programs after their GNU counterparts and it will make life easier on (nearly)
511everyone.
512
513The only time we deviate from emulating the GNU behavior is when:
514
515 - We are deliberately not supporting a feature (such as a command line
516 switch)
517 - Emulating the GNU behavior is prohibitively expensive (lots more code
518 would be required, lots more memory would be used, etc.)
519 - The difference is minor or cosmetic
520
521A note on the 'cosmetic' case: Output differences might be considered
522cosmetic, but if the output is significant enough to break other scripts that
523use the output, it should really be fixed.
524
525
526Scope
527~~~~~
528
529If a const variable is used only in a single source file, put it in the source
530file and not in a header file. Likewise, if a const variable is used in only
531one function, do not make it global to the file. Instead, declare it inside
532the function body. Bottom line: Make a conscious effort to limit declarations
533to the smallest scope possible.
534
535Inside applet files, all functions should be declared static so as to keep the
536global name space clean. The only exception to this rule is the "applet_main"
537function which must be declared extern.
538
539If you write a function that performs a task that could be useful outside the
540immediate file, turn it into a general-purpose function with no ties to any
541applet and put it in the utility.c file instead.
542
543
544Brackets Are Your Friends
545~~~~~~~~~~~~~~~~~~~~~~~~~
546
547Please use brackets on all if and else statements, even if it is only one
548line. Example:
549
550 Don't do this:
551
552 if (foo)
553 stmt1;
554 stmt2
555 stmt3;
556
557 Do this instead:
558
559 if (foo) {
560 stmt1;
561 }
562 stmt2
563 stmt3;
564
565The "bracketless" approach is error prone because someday you might add a line
566like this:
567
568 if (foo)
569 stmt1;
570 new_line();
571 stmt2
572 stmt3;
573
574And the resulting behavior of your program would totally bewilder you. (Don't
575laugh, it happens to us all.) Remember folks, this is C, not Python.
576
577
578Function Declarations
579~~~~~~~~~~~~~~~~~~~~~
580
581Do not use old-style function declarations that declare variable types between
582the parameter list and opening bracket. Example:
583
584 Don't do this:
585
586 int foo(parm1, parm2)
587 char parm1;
588 float parm2;
589 {
590 ....
591
592 Do this instead:
593
594 int foo(char parm1, float parm2)
595 {
596 ....
597
598The only time you would ever need to use the old declaration syntax is to
599support ancient, antediluvian compilers. To our good fortune, we have access
600to more modern compilers and the old declaration syntax is neither necessary
601nor desired.
602
603
604Emphasizing Logical Blocks
605~~~~~~~~~~~~~~~~~~~~~~~~~~
606
607Organization and readability are improved by putting extra newlines around
608blocks of code that perform a single task. These are typically blocks that
609begin with a C keyword, but not always.
610
611Furthermore, you should put a single comment (not necessarily one line, just
612one comment) before the block, rather than commenting each and every line.
613There is an optimal ammount of commenting that a program can have; you can
614comment too much as well as too little.
615
616A picture is really worth a thousand words here, the following example
617illustrates how to emphasize logical blocks:
618
619 while (line = get_line_from_file(fp)) {
620
621 /* eat the newline, if any */
622 chomp(line);
623
624 /* ignore blank lines */
625 if (strlen(file_to_act_on) == 0) {
626 continue;
627 }
628
629 /* if the search string is in this line, print it,
630 * unless we were told to be quiet */
631 if (strstr(line, search) && !be_quiet) {
632 puts(line);
633 }
634
635 /* clean up */
636 free(line);
637 }
638
639
640Processing Options with getopt
641~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
642
643If your applet needs to process command-line switches, please use getopt() to
644do so. Numerous examples can be seen in many of the existing applets, but
645basically it boils down to two things: at the top of the .c file, have this
646line in the midst of your #includes:
647
648 #include <getopt.h>
649
650And a code block similar to the following near the top of your applet_main()
651routine:
652
653 while ((opt = getopt(argc, argv, "abc")) > 0) {
654 switch (opt) {
655 case 'a':
656 do_a_opt = 1;
657 break;
658 case 'b':
659 do_b_opt = 1;
660 break;
661 case 'c':
662 do_c_opt = 1;
663 break;
664 default:
665 show_usage(); /* in utility.c */
666 }
667 }
668
669If your applet takes no options (such as 'init'), there should be a line
670somewhere in the file reads:
671
672 /* no options, no getopt */
673
674That way, when people go grepping to see which applets need to be converted to
675use getopt, they won't get false positives.
676
677Additional Note: Do not use the getopt_long library function and do not try to
678hand-roll your own long option parsing. Busybox applets should only support
679short options. Explanations and examples of the short options should be
680documented 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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Editors"
7
8config CONFIG_AWK
9 bool "awk"
10 default n
11 help
12 Awk is used as a pattern scanning and processing language. This is
13 the BusyBox implementation of that programming language.
14
15config CONFIG_FEATURE_AWK_MATH
16 bool " Enable math functions (requires libm)"
17 default y
18 depends on CONFIG_AWK
19 help
20 Enable math functions of the Awk programming language.
21 NOTE: This will require libm to be present for linking.
22
23config CONFIG_PATCH
24 bool "patch"
25 default n
26 help
27 Apply a unified diff formatted patch.
28
29config CONFIG_SED
30 bool "sed"
31 default n
32 help
33 sed is used to perform text transformations on a file
34 or input from a pipeline.
35
36config CONFIG_VI
37 bool "vi"
38 default n
39 help
40 'vi' is a text editor. More specifically, it is the One True
41 text editor <grin>. It does, however, have a rather steep
42 learning curve. If you are not already comfortable with 'vi'
43 you may wish to use something else.
44
45config CONFIG_FEATURE_VI_COLON
46 bool " Enable \":\" colon commands (no \"ex\" mode)"
47 default y
48 depends on CONFIG_VI
49 help
50 Enable a limited set of colon commands for vi. This does not
51 provide an "ex" mode.
52
53config CONFIG_FEATURE_VI_YANKMARK
54 bool " Enable yank/put commands and mark cmds"
55 default y
56 depends on CONFIG_VI
57 help
58 This will enable you to use yank and put, as well as mark in
59 busybox vi.
60
61config CONFIG_FEATURE_VI_SEARCH
62 bool " Enable search and replace cmds"
63 default y
64 depends on CONFIG_VI
65 help
66 Select this if you wish to be able to do search and replace in
67 busybox vi.
68
69config CONFIG_FEATURE_VI_USE_SIGNALS
70 bool " Catch signals"
71 default y
72 depends on CONFIG_VI
73 help
74 Selecting this option will make busybox vi signal aware. This will
75 make busybox vi support SIGWINCH to deal with Window Changes, catch
76 Ctrl-Z and Ctrl-C and alarms.
77
78config CONFIG_FEATURE_VI_DOT_CMD
79 bool " Remember previous cmd and \".\" cmd"
80 default y
81 depends on CONFIG_VI
82 help
83 Make busybox vi remember the last command and be able to repeat it.
84
85config CONFIG_FEATURE_VI_READONLY
86 bool " Enable -R option and \"view\" mode"
87 default y
88 depends on CONFIG_VI
89 help
90 Enable the read-only command line option, which allows the user to
91 open a file in read-only mode.
92
93config CONFIG_FEATURE_VI_SETOPTS
94 bool " Enable set-able options, ai ic showmatch"
95 default y
96 depends on CONFIG_VI
97 help
98 Enable the editor to set some (ai, ic, showmatch) options.
99
100config CONFIG_FEATURE_VI_SET
101 bool " Support for :set"
102 default y
103 depends on CONFIG_VI
104 help
105 Support for ":set".
106
107config CONFIG_FEATURE_VI_WIN_RESIZE
108 bool " Handle window resize"
109 default y
110 depends on CONFIG_VI
111 help
112 Make busybox vi behave nicely with terminals that get resized.
113
114config CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
115 bool " Optimize cursor movement"
116 default y
117 depends on CONFIG_VI
118 help
119 This will make the cursor movement faster, but requires more memory
120 and it makes the applet a tiny bit larger.
121
122endmenu
123
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/editors
23EDITOR_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include $(srcdir)/Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20EDITOR_AR:=editors.a
21ifndef $(EDITOR_DIR)
22EDITOR_DIR:=$(top_builddir)/editors/
23endif
24srcdir=$(top_srcdir)/editors
25
26EDITOR-y:=
27EDITOR-$(CONFIG_AWK) += awk.o
28EDITOR-$(CONFIG_PATCH) += patch.o
29EDITOR-$(CONFIG_SED) += sed.o
30EDITOR-$(CONFIG_VI) += vi.o
31EDITOR_SRC:= $(EDITOR-y)
32EDITOR_OBJ:= $(patsubst %.c,$(EDITOR_DIR)%.o, $(EDITOR_SRC))
33
34libraries-y+=$(EDITOR_DIR)$(EDITOR_AR)
35
36needlibm-y:=
37needlibm-$(CONFIG_FEATURE_AWK_MATH) := y
38
39ifeq ($(needlibm-y),y)
40 LIBRARIES += -lm
41endif
42
43$(EDITOR_DIR)$(EDITOR_AR): $(patsubst %,$(EDITOR_DIR)%, $(EDITOR-y))
44 $(AR) -ro $@ $(patsubst %,$(EDITOR_DIR)%, $(EDITOR-y))
45
46$(EDITOR_DIR)%.o: $(srcdir)/%.c
47 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
48
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * awk implementation for busybox
4 *
5 * Copyright (C) 2002 by Dmitry Zakharov <dmit@crp.bank.gov.ua>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <unistd.h>
26#include <errno.h>
27#include <string.h>
28#include <time.h>
29#include <math.h>
30#include <ctype.h>
31#include <getopt.h>
32#include <regex.h>
33
34#include "busybox.h"
35
36
37#define MAXVARFMT 240
38#define MINNVBLOCK 64
39
40/* variable flags */
41#define VF_NUMBER 0x0001 /* 1 = primary type is number */
42#define VF_ARRAY 0x0002 /* 1 = it's an array */
43
44#define VF_CACHED 0x0100 /* 1 = num/str value has cached str/num eq */
45#define VF_USER 0x0200 /* 1 = user input (may be numeric string) */
46#define VF_SPECIAL 0x0400 /* 1 = requires extra handling when changed */
47#define VF_WALK 0x0800 /* 1 = variable has alloc'd x.walker list */
48#define VF_FSTR 0x1000 /* 1 = string points to fstring buffer */
49#define VF_CHILD 0x2000 /* 1 = function arg; x.parent points to source */
50#define VF_DIRTY 0x4000 /* 1 = variable was set explicitly */
51
52/* these flags are static, don't change them when value is changed */
53#define VF_DONTTOUCH (VF_ARRAY | VF_SPECIAL | VF_WALK | VF_CHILD | VF_DIRTY)
54
55/* Variable */
56typedef struct var_s {
57 unsigned short type; /* flags */
58 double number;
59 char *string;
60 union {
61 int aidx; /* func arg index (on compilation stage) */
62 struct xhash_s *array; /* array ptr */
63 struct var_s *parent; /* for func args, ptr to actual parameter */
64 char **walker; /* list of array elements (for..in) */
65 } x;
66} var;
67
68/* Node chain (pattern-action chain, BEGIN, END, function bodies) */
69typedef struct chain_s {
70 struct node_s *first;
71 struct node_s *last;
72 char *programname;
73} chain;
74
75/* Function */
76typedef struct func_s {
77 unsigned short nargs;
78 struct chain_s body;
79} func;
80
81/* I/O stream */
82typedef struct rstream_s {
83 FILE *F;
84 char *buffer;
85 int adv;
86 int size;
87 int pos;
88 unsigned short is_pipe;
89} rstream;
90
91typedef struct hash_item_s {
92 union {
93 struct var_s v; /* variable/array hash */
94 struct rstream_s rs; /* redirect streams hash */
95 struct func_s f; /* functions hash */
96 } data;
97 struct hash_item_s *next; /* next in chain */
98 char name[1]; /* really it's longer */
99} hash_item;
100
101typedef struct xhash_s {
102 unsigned int nel; /* num of elements */
103 unsigned int csize; /* current hash size */
104 unsigned int nprime; /* next hash size in PRIMES[] */
105 unsigned int glen; /* summary length of item names */
106 struct hash_item_s **items;
107} xhash;
108
109/* Tree node */
110typedef struct node_s {
111 unsigned long info;
112 unsigned short lineno;
113 union {
114 struct node_s *n;
115 var *v;
116 int i;
117 char *s;
118 regex_t *re;
119 } l;
120 union {
121 struct node_s *n;
122 regex_t *ire;
123 func *f;
124 int argno;
125 } r;
126 union {
127 struct node_s *n;
128 } a;
129} node;
130
131/* Block of temporary variables */
132typedef struct nvblock_s {
133 int size;
134 var *pos;
135 struct nvblock_s *prev;
136 struct nvblock_s *next;
137 var nv[0];
138} nvblock;
139
140typedef struct tsplitter_s {
141 node n;
142 regex_t re[2];
143} tsplitter;
144
145/* simple token classes */
146/* Order and hex values are very important!!! See next_token() */
147#define TC_SEQSTART 1 /* ( */
148#define TC_SEQTERM (1 << 1) /* ) */
149#define TC_REGEXP (1 << 2) /* /.../ */
150#define TC_OUTRDR (1 << 3) /* | > >> */
151#define TC_UOPPOST (1 << 4) /* unary postfix operator */
152#define TC_UOPPRE1 (1 << 5) /* unary prefix operator */
153#define TC_BINOPX (1 << 6) /* two-opnd operator */
154#define TC_IN (1 << 7)
155#define TC_COMMA (1 << 8)
156#define TC_PIPE (1 << 9) /* input redirection pipe */
157#define TC_UOPPRE2 (1 << 10) /* unary prefix operator */
158#define TC_ARRTERM (1 << 11) /* ] */
159#define TC_GRPSTART (1 << 12) /* { */
160#define TC_GRPTERM (1 << 13) /* } */
161#define TC_SEMICOL (1 << 14)
162#define TC_NEWLINE (1 << 15)
163#define TC_STATX (1 << 16) /* ctl statement (for, next...) */
164#define TC_WHILE (1 << 17)
165#define TC_ELSE (1 << 18)
166#define TC_BUILTIN (1 << 19)
167#define TC_GETLINE (1 << 20)
168#define TC_FUNCDECL (1 << 21) /* `function' `func' */
169#define TC_BEGIN (1 << 22)
170#define TC_END (1 << 23)
171#define TC_EOF (1 << 24)
172#define TC_VARIABLE (1 << 25)
173#define TC_ARRAY (1 << 26)
174#define TC_FUNCTION (1 << 27)
175#define TC_STRING (1 << 28)
176#define TC_NUMBER (1 << 29)
177
178#define TC_UOPPRE (TC_UOPPRE1 | TC_UOPPRE2)
179
180/* combined token classes */
181#define TC_BINOP (TC_BINOPX | TC_COMMA | TC_PIPE | TC_IN)
182#define TC_UNARYOP (TC_UOPPRE | TC_UOPPOST)
183#define TC_OPERAND (TC_VARIABLE | TC_ARRAY | TC_FUNCTION | \
184 TC_BUILTIN | TC_GETLINE | TC_SEQSTART | TC_STRING | TC_NUMBER)
185
186#define TC_STATEMNT (TC_STATX | TC_WHILE)
187#define TC_OPTERM (TC_SEMICOL | TC_NEWLINE)
188
189/* word tokens, cannot mean something else if not expected */
190#define TC_WORD (TC_IN | TC_STATEMNT | TC_ELSE | TC_BUILTIN | \
191 TC_GETLINE | TC_FUNCDECL | TC_BEGIN | TC_END)
192
193/* discard newlines after these */
194#define TC_NOTERM (TC_COMMA | TC_GRPSTART | TC_GRPTERM | \
195 TC_BINOP | TC_OPTERM)
196
197/* what can expression begin with */
198#define TC_OPSEQ (TC_OPERAND | TC_UOPPRE | TC_REGEXP)
199/* what can group begin with */
200#define TC_GRPSEQ (TC_OPSEQ | TC_OPTERM | TC_STATEMNT | TC_GRPSTART)
201
202/* if previous token class is CONCAT1 and next is CONCAT2, concatenation */
203/* operator is inserted between them */
204#define TC_CONCAT1 (TC_VARIABLE | TC_ARRTERM | TC_SEQTERM | \
205 TC_STRING | TC_NUMBER | TC_UOPPOST)
206#define TC_CONCAT2 (TC_OPERAND | TC_UOPPRE)
207
208#define OF_RES1 0x010000
209#define OF_RES2 0x020000
210#define OF_STR1 0x040000
211#define OF_STR2 0x080000
212#define OF_NUM1 0x100000
213#define OF_CHECKED 0x200000
214
215/* combined operator flags */
216#define xx 0
217#define xV OF_RES2
218#define xS (OF_RES2 | OF_STR2)
219#define Vx OF_RES1
220#define VV (OF_RES1 | OF_RES2)
221#define Nx (OF_RES1 | OF_NUM1)
222#define NV (OF_RES1 | OF_NUM1 | OF_RES2)
223#define Sx (OF_RES1 | OF_STR1)
224#define SV (OF_RES1 | OF_STR1 | OF_RES2)
225#define SS (OF_RES1 | OF_STR1 | OF_RES2 | OF_STR2)
226
227#define OPCLSMASK 0xFF00
228#define OPNMASK 0x007F
229
230/* operator priority is a highest byte (even: r->l, odd: l->r grouping)
231 * For builtins it has different meaning: n n s3 s2 s1 v3 v2 v1,
232 * n - min. number of args, vN - resolve Nth arg to var, sN - resolve to string
233 */
234#define P(x) (x << 24)
235#define PRIMASK 0x7F000000
236#define PRIMASK2 0x7E000000
237
238/* Operation classes */
239
240#define SHIFT_TIL_THIS 0x0600
241#define RECUR_FROM_THIS 0x1000
242
243enum {
244 OC_DELETE=0x0100, OC_EXEC=0x0200, OC_NEWSOURCE=0x0300,
245 OC_PRINT=0x0400, OC_PRINTF=0x0500, OC_WALKINIT=0x0600,
246
247 OC_BR=0x0700, OC_BREAK=0x0800, OC_CONTINUE=0x0900,
248 OC_EXIT=0x0a00, OC_NEXT=0x0b00, OC_NEXTFILE=0x0c00,
249 OC_TEST=0x0d00, OC_WALKNEXT=0x0e00,
250
251 OC_BINARY=0x1000, OC_BUILTIN=0x1100, OC_COLON=0x1200,
252 OC_COMMA=0x1300, OC_COMPARE=0x1400, OC_CONCAT=0x1500,
253 OC_FBLTIN=0x1600, OC_FIELD=0x1700, OC_FNARG=0x1800,
254 OC_FUNC=0x1900, OC_GETLINE=0x1a00, OC_IN=0x1b00,
255 OC_LAND=0x1c00, OC_LOR=0x1d00, OC_MATCH=0x1e00,
256 OC_MOVE=0x1f00, OC_PGETLINE=0x2000, OC_REGEXP=0x2100,
257 OC_REPLACE=0x2200, OC_RETURN=0x2300, OC_SPRINTF=0x2400,
258 OC_TERNARY=0x2500, OC_UNARY=0x2600, OC_VAR=0x2700,
259 OC_DONE=0x2800,
260
261 ST_IF=0x3000, ST_DO=0x3100, ST_FOR=0x3200,
262 ST_WHILE=0x3300
263};
264
265/* simple builtins */
266enum {
267 F_in=0, F_rn, F_co, F_ex, F_lg, F_si, F_sq, F_sr,
268 F_ti, F_le, F_sy, F_ff, F_cl
269};
270
271/* builtins */
272enum {
273 B_a2=0, B_ix, B_ma, B_sp, B_ss, B_ti, B_lo, B_up,
274 B_ge, B_gs, B_su
275};
276
277/* tokens and their corresponding info values */
278
279#define NTC "\377" /* switch to next token class (tc<<1) */
280#define NTCC '\377'
281
282#define OC_B OC_BUILTIN
283
284static char * const tokenlist =
285 "\1(" NTC
286 "\1)" NTC
287 "\1/" NTC /* REGEXP */
288 "\2>>" "\1>" "\1|" NTC /* OUTRDR */
289 "\2++" "\2--" NTC /* UOPPOST */
290 "\2++" "\2--" "\1$" NTC /* UOPPRE1 */
291 "\2==" "\1=" "\2+=" "\2-=" /* BINOPX */
292 "\2*=" "\2/=" "\2%=" "\2^="
293 "\1+" "\1-" "\3**=" "\2**"
294 "\1/" "\1%" "\1^" "\1*"
295 "\2!=" "\2>=" "\2<=" "\1>"
296 "\1<" "\2!~" "\1~" "\2&&"
297 "\2||" "\1?" "\1:" NTC
298 "\2in" NTC
299 "\1," NTC
300 "\1|" NTC
301 "\1+" "\1-" "\1!" NTC /* UOPPRE2 */
302 "\1]" NTC
303 "\1{" NTC
304 "\1}" NTC
305 "\1;" NTC
306 "\1\n" NTC
307 "\2if" "\2do" "\3for" "\5break" /* STATX */
308 "\10continue" "\6delete" "\5print"
309 "\6printf" "\4next" "\10nextfile"
310 "\6return" "\4exit" NTC
311 "\5while" NTC
312 "\4else" NTC
313
314 "\5close" "\6system" "\6fflush" "\5atan2" /* BUILTIN */
315 "\3cos" "\3exp" "\3int" "\3log"
316 "\4rand" "\3sin" "\4sqrt" "\5srand"
317 "\6gensub" "\4gsub" "\5index" "\6length"
318 "\5match" "\5split" "\7sprintf" "\3sub"
319 "\6substr" "\7systime" "\10strftime"
320 "\7tolower" "\7toupper" NTC
321 "\7getline" NTC
322 "\4func" "\10function" NTC
323 "\5BEGIN" NTC
324 "\3END" "\0"
325 ;
326
327static unsigned long tokeninfo[] = {
328
329 0,
330 0,
331 OC_REGEXP,
332 xS|'a', xS|'w', xS|'|',
333 OC_UNARY|xV|P(9)|'p', OC_UNARY|xV|P(9)|'m',
334 OC_UNARY|xV|P(9)|'P', OC_UNARY|xV|P(9)|'M',
335 OC_FIELD|xV|P(5),
336 OC_COMPARE|VV|P(39)|5, OC_MOVE|VV|P(74),
337 OC_REPLACE|NV|P(74)|'+', OC_REPLACE|NV|P(74)|'-',
338 OC_REPLACE|NV|P(74)|'*', OC_REPLACE|NV|P(74)|'/',
339 OC_REPLACE|NV|P(74)|'%', OC_REPLACE|NV|P(74)|'&',
340 OC_BINARY|NV|P(29)|'+', OC_BINARY|NV|P(29)|'-',
341 OC_REPLACE|NV|P(74)|'&', OC_BINARY|NV|P(15)|'&',
342 OC_BINARY|NV|P(25)|'/', OC_BINARY|NV|P(25)|'%',
343 OC_BINARY|NV|P(15)|'&', OC_BINARY|NV|P(25)|'*',
344 OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3,
345 OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1,
346 OC_COMPARE|VV|P(39)|2, OC_MATCH|Sx|P(45)|'!',
347 OC_MATCH|Sx|P(45)|'~', OC_LAND|Vx|P(55),
348 OC_LOR|Vx|P(59), OC_TERNARY|Vx|P(64)|'?',
349 OC_COLON|xx|P(67)|':',
350 OC_IN|SV|P(49),
351 OC_COMMA|SS|P(80),
352 OC_PGETLINE|SV|P(37),
353 OC_UNARY|xV|P(19)|'+', OC_UNARY|xV|P(19)|'-',
354 OC_UNARY|xV|P(19)|'!',
355 0,
356 0,
357 0,
358 0,
359 0,
360 ST_IF, ST_DO, ST_FOR, OC_BREAK,
361 OC_CONTINUE, OC_DELETE|Vx, OC_PRINT,
362 OC_PRINTF, OC_NEXT, OC_NEXTFILE,
363 OC_RETURN|Vx, OC_EXIT|Nx,
364 ST_WHILE,
365 0,
366
367 OC_FBLTIN|Sx|F_cl, OC_FBLTIN|Sx|F_sy, OC_FBLTIN|Sx|F_ff, OC_B|B_a2|P(0x83),
368 OC_FBLTIN|Nx|F_co, OC_FBLTIN|Nx|F_ex, OC_FBLTIN|Nx|F_in, OC_FBLTIN|Nx|F_lg,
369 OC_FBLTIN|F_rn, OC_FBLTIN|Nx|F_si, OC_FBLTIN|Nx|F_sq, OC_FBLTIN|Nx|F_sr,
370 OC_B|B_ge|P(0xd6), OC_B|B_gs|P(0xb6), OC_B|B_ix|P(0x9b), OC_FBLTIN|Sx|F_le,
371 OC_B|B_ma|P(0x89), OC_B|B_sp|P(0x8b), OC_SPRINTF, OC_B|B_su|P(0xb6),
372 OC_B|B_ss|P(0x8f), OC_FBLTIN|F_ti, OC_B|B_ti|P(0x0b),
373 OC_B|B_lo|P(0x49), OC_B|B_up|P(0x49),
374 OC_GETLINE|SV|P(0),
375 0, 0,
376 0,
377 0
378};
379
380/* internal variable names and their initial values */
381/* asterisk marks SPECIAL vars; $ is just no-named Field0 */
382enum {
383 CONVFMT=0, OFMT, FS, OFS,
384 ORS, RS, RT, FILENAME,
385 SUBSEP, ARGIND, ARGC, ARGV,
386 ERRNO, FNR,
387 NR, NF, IGNORECASE,
388 ENVIRON, F0, _intvarcount_
389};
390
391static char * vNames =
392 "CONVFMT\0" "OFMT\0" "FS\0*" "OFS\0"
393 "ORS\0" "RS\0*" "RT\0" "FILENAME\0"
394 "SUBSEP\0" "ARGIND\0" "ARGC\0" "ARGV\0"
395 "ERRNO\0" "FNR\0"
396 "NR\0" "NF\0*" "IGNORECASE\0*"
397 "ENVIRON\0" "$\0*" "\0";
398
399static char * vValues =
400 "%.6g\0" "%.6g\0" " \0" " \0"
401 "\n\0" "\n\0" "\0" "\0"
402 "\034\0"
403 "\377";
404
405/* hash size may grow to these values */
406#define FIRST_PRIME 61;
407static const unsigned int PRIMES[] = { 251, 1021, 4093, 16381, 65521 };
408static const unsigned int NPRIMES = sizeof(PRIMES) / sizeof(unsigned int);
409
410/* globals */
411
412extern char **environ;
413
414static var * V[_intvarcount_];
415static chain beginseq, mainseq, endseq, *seq;
416static int nextrec, nextfile;
417static node *break_ptr, *continue_ptr;
418static rstream *iF;
419static xhash *vhash, *ahash, *fdhash, *fnhash;
420static char *programname;
421static short lineno;
422static int is_f0_split;
423static int nfields = 0;
424static var *Fields = NULL;
425static tsplitter fsplitter, rsplitter;
426static nvblock *cb = NULL;
427static char *pos;
428static char *buf;
429static int icase = FALSE;
430static int exiting = FALSE;
431
432static struct {
433 unsigned long tclass;
434 unsigned long info;
435 char *string;
436 double number;
437 short lineno;
438 int rollback;
439} t;
440
441/* function prototypes */
442extern void xregcomp(regex_t *preg, const char *regex, int cflags);
443static void handle_special(var *);
444static node *parse_expr(unsigned long);
445static void chain_group(void);
446static var *evaluate(node *, var *);
447static rstream *next_input_file(void);
448static int fmt_num(char *, int, char *, double, int);
449static int awk_exit(int);
450
451/* ---- error handling ---- */
452
453static const char EMSG_INTERNAL_ERROR[] = "Internal error";
454static const char EMSG_UNEXP_EOS[] = "Unexpected end of string";
455static const char EMSG_UNEXP_TOKEN[] = "Unexpected token";
456static const char EMSG_DIV_BY_ZERO[] = "Division by zero";
457static const char EMSG_INV_FMT[] = "Invalid format specifier";
458static const char EMSG_TOO_FEW_ARGS[] = "Too few arguments for builtin";
459static const char EMSG_NOT_ARRAY[] = "Not an array";
460static const char EMSG_POSSIBLE_ERROR[] = "Possible syntax error";
461static const char EMSG_UNDEF_FUNC[] = "Call to undefined function";
462#ifndef CONFIG_FEATURE_AWK_MATH
463static const char EMSG_NO_MATH[] = "Math support is not compiled in";
464#endif
465
466static void syntax_error(const char * const message)
467{
468 bb_error_msg("%s:%i: %s", programname, lineno, message);
469 exit(1);
470}
471
472#define runtime_error(x) syntax_error(x)
473
474
475/* ---- hash stuff ---- */
476
477static unsigned int hashidx(char *name) {
478
479 register unsigned int idx=0;
480
481 while (*name) idx = *name++ + (idx << 6) - idx;
482 return idx;
483}
484
485/* create new hash */
486static xhash *hash_init(void) {
487
488 xhash *newhash;
489
490 newhash = (xhash *)xcalloc(1, sizeof(xhash));
491 newhash->csize = FIRST_PRIME;
492 newhash->items = (hash_item **)xcalloc(newhash->csize, sizeof(hash_item *));
493
494 return newhash;
495}
496
497/* find item in hash, return ptr to data, NULL if not found */
498static void *hash_search(xhash *hash, char *name) {
499
500 hash_item *hi;
501
502 hi = hash->items [ hashidx(name) % hash->csize ];
503 while (hi) {
504 if (strcmp(hi->name, name) == 0)
505 return &(hi->data);
506 hi = hi->next;
507 }
508 return NULL;
509}
510
511/* grow hash if it becomes too big */
512static void hash_rebuild(xhash *hash) {
513
514 unsigned int newsize, i, idx;
515 hash_item **newitems, *hi, *thi;
516
517 if (hash->nprime == NPRIMES)
518 return;
519
520 newsize = PRIMES[hash->nprime++];
521 newitems = (hash_item **)xcalloc(newsize, sizeof(hash_item *));
522
523 for (i=0; i<hash->csize; i++) {
524 hi = hash->items[i];
525 while (hi) {
526 thi = hi;
527 hi = thi->next;
528 idx = hashidx(thi->name) % newsize;
529 thi->next = newitems[idx];
530 newitems[idx] = thi;
531 }
532 }
533
534 free(hash->items);
535 hash->csize = newsize;
536 hash->items = newitems;
537}
538
539/* find item in hash, add it if necessary. Return ptr to data */
540static void *hash_find(xhash *hash, char *name) {
541
542 hash_item *hi;
543 unsigned int idx;
544 int l;
545
546 hi = hash_search(hash, name);
547 if (! hi) {
548 if (++hash->nel / hash->csize > 10)
549 hash_rebuild(hash);
550
551 l = bb_strlen(name) + 1;
552 hi = xcalloc(sizeof(hash_item) + l, 1);
553 memcpy(hi->name, name, l);
554
555 idx = hashidx(name) % hash->csize;
556 hi->next = hash->items[idx];
557 hash->items[idx] = hi;
558 hash->glen += l;
559 }
560 return &(hi->data);
561}
562
563#define findvar(hash, name) (var *) hash_find ( (hash) , (name) )
564#define newvar(name) (var *) hash_find ( vhash , (name) )
565#define newfile(name) (rstream *) hash_find ( fdhash , (name) )
566#define newfunc(name) (func *) hash_find ( fnhash , (name) )
567
568static void hash_remove(xhash *hash, char *name) {
569
570 hash_item *hi, **phi;
571
572 phi = &(hash->items[ hashidx(name) % hash->csize ]);
573 while (*phi) {
574 hi = *phi;
575 if (strcmp(hi->name, name) == 0) {
576 hash->glen -= (bb_strlen(name) + 1);
577 hash->nel--;
578 *phi = hi->next;
579 free(hi);
580 break;
581 }
582 phi = &(hi->next);
583 }
584}
585
586/* ------ some useful functions ------ */
587
588static void skip_spaces(char **s) {
589
590 register char *p = *s;
591
592 while(*p == ' ' || *p == '\t' ||
593 (*p == '\\' && *(p+1) == '\n' && (++p, ++t.lineno))) {
594 p++;
595 }
596 *s = p;
597}
598
599static char *nextword(char **s) {
600
601 register char *p = *s;
602
603 while (*(*s)++) ;
604
605 return p;
606}
607
608static char nextchar(char **s) {
609
610 register char c, *pps;
611
612 c = *((*s)++);
613 pps = *s;
614 if (c == '\\') c = bb_process_escape_sequence((const char**)s);
615 if (c == '\\' && *s == pps) c = *((*s)++);
616 return c;
617}
618
619static inline int isalnum_(int c) {
620
621 return (isalnum(c) || c == '_');
622}
623
624static FILE *afopen(const char *path, const char *mode) {
625
626 return (*path == '-' && *(path+1) == '\0') ? stdin : bb_xfopen(path, mode);
627}
628
629/* -------- working with variables (set/get/copy/etc) -------- */
630
631static xhash *iamarray(var *v) {
632
633 var *a = v;
634
635 while (a->type & VF_CHILD)
636 a = a->x.parent;
637
638 if (! (a->type & VF_ARRAY)) {
639 a->type |= VF_ARRAY;
640 a->x.array = hash_init();
641 }
642 return a->x.array;
643}
644
645static void clear_array(xhash *array) {
646
647 unsigned int i;
648 hash_item *hi, *thi;
649
650 for (i=0; i<array->csize; i++) {
651 hi = array->items[i];
652 while (hi) {
653 thi = hi;
654 hi = hi->next;
655 free(thi->data.v.string);
656 free(thi);
657 }
658 array->items[i] = NULL;
659 }
660 array->glen = array->nel = 0;
661}
662
663/* clear a variable */
664static var *clrvar(var *v) {
665
666 if (!(v->type & VF_FSTR))
667 free(v->string);
668
669 v->type &= VF_DONTTOUCH;
670 v->type |= VF_DIRTY;
671 v->string = NULL;
672 return v;
673}
674
675/* assign string value to variable */
676static var *setvar_p(var *v, char *value) {
677
678 clrvar(v);
679 v->string = value;
680 handle_special(v);
681
682 return v;
683}
684
685/* same as setvar_p but make a copy of string */
686static var *setvar_s(var *v, char *value) {
687
688 return setvar_p(v, (value && *value) ? bb_xstrdup(value) : NULL);
689}
690
691/* same as setvar_s but set USER flag */
692static var *setvar_u(var *v, char *value) {
693
694 setvar_s(v, value);
695 v->type |= VF_USER;
696 return v;
697}
698
699/* set array element to user string */
700static void setari_u(var *a, int idx, char *s) {
701
702 register var *v;
703 static char sidx[12];
704
705 sprintf(sidx, "%d", idx);
706 v = findvar(iamarray(a), sidx);
707 setvar_u(v, s);
708}
709
710/* assign numeric value to variable */
711static var *setvar_i(var *v, double value) {
712
713 clrvar(v);
714 v->type |= VF_NUMBER;
715 v->number = value;
716 handle_special(v);
717 return v;
718}
719
720static char *getvar_s(var *v) {
721
722 /* if v is numeric and has no cached string, convert it to string */
723 if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) {
724 fmt_num(buf, MAXVARFMT, getvar_s(V[CONVFMT]), v->number, TRUE);
725 v->string = bb_xstrdup(buf);
726 v->type |= VF_CACHED;
727 }
728 return (v->string == NULL) ? "" : v->string;
729}
730
731static double getvar_i(var *v) {
732
733 char *s;
734
735 if ((v->type & (VF_NUMBER | VF_CACHED)) == 0) {
736 v->number = 0;
737 s = v->string;
738 if (s && *s) {
739 v->number = strtod(s, &s);
740 if (v->type & VF_USER) {
741 skip_spaces(&s);
742 if (*s != '\0')
743 v->type &= ~VF_USER;
744 }
745 } else {
746 v->type &= ~VF_USER;
747 }
748 v->type |= VF_CACHED;
749 }
750 return v->number;
751}
752
753static var *copyvar(var *dest, var *src) {
754
755 if (dest != src) {
756 clrvar(dest);
757 dest->type |= (src->type & ~VF_DONTTOUCH);
758 dest->number = src->number;
759 if (src->string)
760 dest->string = bb_xstrdup(src->string);
761 }
762 handle_special(dest);
763 return dest;
764}
765
766static var *incvar(var *v) {
767
768 return setvar_i(v, getvar_i(v)+1.);
769}
770
771/* return true if v is number or numeric string */
772static int is_numeric(var *v) {
773
774 getvar_i(v);
775 return ((v->type ^ VF_DIRTY) & (VF_NUMBER | VF_USER | VF_DIRTY));
776}
777
778/* return 1 when value of v corresponds to true, 0 otherwise */
779static int istrue(var *v) {
780
781 if (is_numeric(v))
782 return (v->number == 0) ? 0 : 1;
783 else
784 return (v->string && *(v->string)) ? 1 : 0;
785}
786
787/* temporary variables allocator. Last allocated should be first freed */
788static var *nvalloc(int n) {
789
790 nvblock *pb = NULL;
791 var *v, *r;
792 int size;
793
794 while (cb) {
795 pb = cb;
796 if ((cb->pos - cb->nv) + n <= cb->size) break;
797 cb = cb->next;
798 }
799
800 if (! cb) {
801 size = (n <= MINNVBLOCK) ? MINNVBLOCK : n;
802 cb = (nvblock *)xmalloc(sizeof(nvblock) + size * sizeof(var));
803 cb->size = size;
804 cb->pos = cb->nv;
805 cb->prev = pb;
806 cb->next = NULL;
807 if (pb) pb->next = cb;
808 }
809
810 v = r = cb->pos;
811 cb->pos += n;
812
813 while (v < cb->pos) {
814 v->type = 0;
815 v->string = NULL;
816 v++;
817 }
818
819 return r;
820}
821
822static void nvfree(var *v) {
823
824 var *p;
825
826 if (v < cb->nv || v >= cb->pos)
827 runtime_error(EMSG_INTERNAL_ERROR);
828
829 for (p=v; p<cb->pos; p++) {
830 if ((p->type & (VF_ARRAY|VF_CHILD)) == VF_ARRAY) {
831 clear_array(iamarray(p));
832 free(p->x.array->items);
833 free(p->x.array);
834 }
835 if (p->type & VF_WALK)
836 free(p->x.walker);
837
838 clrvar(p);
839 }
840
841 cb->pos = v;
842 while (cb->prev && cb->pos == cb->nv) {
843 cb = cb->prev;
844 }
845}
846
847/* ------- awk program text parsing ------- */
848
849/* Parse next token pointed by global pos, place results into global t.
850 * If token isn't expected, give away. Return token class
851 */
852static unsigned long next_token(unsigned long expected) {
853
854 char *p, *pp, *s;
855 char *tl;
856 unsigned long tc, *ti;
857 int l;
858 static int concat_inserted = FALSE;
859 static unsigned long save_tclass, save_info;
860 static unsigned long ltclass = TC_OPTERM;
861
862 if (t.rollback) {
863
864 t.rollback = FALSE;
865
866 } else if (concat_inserted) {
867
868 concat_inserted = FALSE;
869 t.tclass = save_tclass;
870 t.info = save_info;
871
872 } else {
873
874 p = pos;
875
876 readnext:
877 skip_spaces(&p);
878 lineno = t.lineno;
879 if (*p == '#')
880 while (*p != '\n' && *p != '\0') p++;
881
882 if (*p == '\n')
883 t.lineno++;
884
885 if (*p == '\0') {
886 tc = TC_EOF;
887
888 } else if (*p == '\"') {
889 /* it's a string */
890 t.string = s = ++p;
891 while (*p != '\"') {
892 if (*p == '\0' || *p == '\n')
893 syntax_error(EMSG_UNEXP_EOS);
894 *(s++) = nextchar(&p);
895 }
896 p++;
897 *s = '\0';
898 tc = TC_STRING;
899
900 } else if ((expected & TC_REGEXP) && *p == '/') {
901 /* it's regexp */
902 t.string = s = ++p;
903 while (*p != '/') {
904 if (*p == '\0' || *p == '\n')
905 syntax_error(EMSG_UNEXP_EOS);
906 if ((*s++ = *p++) == '\\') {
907 pp = p;
908 *(s-1) = bb_process_escape_sequence((const char **)&p);
909 if (*pp == '\\') *s++ = '\\';
910 if (p == pp) *s++ = *p++;
911 }
912 }
913 p++;
914 *s = '\0';
915 tc = TC_REGEXP;
916
917 } else if (*p == '.' || isdigit(*p)) {
918 /* it's a number */
919 t.number = strtod(p, &p);
920 if (*p == '.')
921 syntax_error(EMSG_UNEXP_TOKEN);
922 tc = TC_NUMBER;
923
924 } else {
925 /* search for something known */
926 tl = tokenlist;
927 tc = 0x00000001;
928 ti = tokeninfo;
929 while (*tl) {
930 l = *(tl++);
931 if (l == NTCC) {
932 tc <<= 1;
933 continue;
934 }
935 /* if token class is expected, token
936 * matches and it's not a longer word,
937 * then this is what we are looking for
938 */
939 if ((tc & (expected | TC_WORD | TC_NEWLINE)) &&
940 *tl == *p && strncmp(p, tl, l) == 0 &&
941 !((tc & TC_WORD) && isalnum_(*(p + l)))) {
942 t.info = *ti;
943 p += l;
944 break;
945 }
946 ti++;
947 tl += l;
948 }
949
950 if (! *tl) {
951 /* it's a name (var/array/function),
952 * otherwise it's something wrong
953 */
954 if (! isalnum_(*p))
955 syntax_error(EMSG_UNEXP_TOKEN);
956
957 t.string = --p;
958 while(isalnum_(*(++p))) {
959 *(p-1) = *p;
960 }
961 *(p-1) = '\0';
962 tc = TC_VARIABLE;
963 if (*p == '(') {
964 tc = TC_FUNCTION;
965 } else {
966 skip_spaces(&p);
967 if (*p == '[') {
968 p++;
969 tc = TC_ARRAY;
970 }
971 }
972 }
973 }
974 pos = p;
975
976 /* skipping newlines in some cases */
977 if ((ltclass & TC_NOTERM) && (tc & TC_NEWLINE))
978 goto readnext;
979
980 /* insert concatenation operator when needed */
981 if ((ltclass&TC_CONCAT1) && (tc&TC_CONCAT2) && (expected&TC_BINOP)) {
982 concat_inserted = TRUE;
983 save_tclass = tc;
984 save_info = t.info;
985 tc = TC_BINOP;
986 t.info = OC_CONCAT | SS | P(35);
987 }
988
989 t.tclass = tc;
990 }
991 ltclass = t.tclass;
992
993 /* Are we ready for this? */
994 if (! (ltclass & expected))
995 syntax_error((ltclass & (TC_NEWLINE | TC_EOF)) ?
996 EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN);
997
998 return ltclass;
999}
1000
1001static void rollback_token(void) { t.rollback = TRUE; }
1002
1003static node *new_node(unsigned long info) {
1004
1005 register node *n;
1006
1007 n = (node *)xcalloc(sizeof(node), 1);
1008 n->info = info;
1009 n->lineno = lineno;
1010 return n;
1011}
1012
1013static node *mk_re_node(char *s, node *n, regex_t *re) {
1014
1015 n->info = OC_REGEXP;
1016 n->l.re = re;
1017 n->r.ire = re + 1;
1018 xregcomp(re, s, REG_EXTENDED);
1019 xregcomp(re+1, s, REG_EXTENDED | REG_ICASE);
1020
1021 return n;
1022}
1023
1024static node *condition(void) {
1025
1026 next_token(TC_SEQSTART);
1027 return parse_expr(TC_SEQTERM);
1028}
1029
1030/* parse expression terminated by given argument, return ptr
1031 * to built subtree. Terminator is eaten by parse_expr */
1032static node *parse_expr(unsigned long iexp) {
1033
1034 node sn;
1035 node *cn = &sn;
1036 node *vn, *glptr;
1037 unsigned long tc, xtc;
1038 var *v;
1039
1040 sn.info = PRIMASK;
1041 sn.r.n = glptr = NULL;
1042 xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP | iexp;
1043
1044 while (! ((tc = next_token(xtc)) & iexp)) {
1045 if (glptr && (t.info == (OC_COMPARE|VV|P(39)|2))) {
1046 /* input redirection (<) attached to glptr node */
1047 cn = glptr->l.n = new_node(OC_CONCAT|SS|P(37));
1048 cn->a.n = glptr;
1049 xtc = TC_OPERAND | TC_UOPPRE;
1050 glptr = NULL;
1051
1052 } else if (tc & (TC_BINOP | TC_UOPPOST)) {
1053 /* for binary and postfix-unary operators, jump back over
1054 * previous operators with higher priority */
1055 vn = cn;
1056 while ( ((t.info & PRIMASK) > (vn->a.n->info & PRIMASK2)) ||
1057 ((t.info == vn->info) && ((t.info & OPCLSMASK) == OC_COLON)) )
1058 vn = vn->a.n;
1059 if ((t.info & OPCLSMASK) == OC_TERNARY)
1060 t.info += P(6);
1061 cn = vn->a.n->r.n = new_node(t.info);
1062 cn->a.n = vn->a.n;
1063 if (tc & TC_BINOP) {
1064 cn->l.n = vn;
1065 xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP;
1066 if ((t.info & OPCLSMASK) == OC_PGETLINE) {
1067 /* it's a pipe */
1068 next_token(TC_GETLINE);
1069 /* give maximum priority to this pipe */
1070 cn->info &= ~PRIMASK;
1071 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
1072 }
1073 } else {
1074 cn->r.n = vn;
1075 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
1076 }
1077 vn->a.n = cn;
1078
1079 } else {
1080 /* for operands and prefix-unary operators, attach them
1081 * to last node */
1082 vn = cn;
1083 cn = vn->r.n = new_node(t.info);
1084 cn->a.n = vn;
1085 xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP;
1086 if (tc & (TC_OPERAND | TC_REGEXP)) {
1087 xtc = TC_UOPPRE | TC_BINOP | TC_OPERAND | iexp;
1088 /* one should be very careful with switch on tclass -
1089 * only simple tclasses should be used! */
1090 switch (tc) {
1091 case TC_VARIABLE:
1092 case TC_ARRAY:
1093 cn->info = OC_VAR;
1094 if ((v = hash_search(ahash, t.string)) != NULL) {
1095 cn->info = OC_FNARG;
1096 cn->l.i = v->x.aidx;
1097 } else {
1098 cn->l.v = newvar(t.string);
1099 }
1100 if (tc & TC_ARRAY) {
1101 cn->info |= xS;
1102 cn->r.n = parse_expr(TC_ARRTERM);
1103 }
1104 xtc = TC_UOPPOST | TC_UOPPRE | TC_BINOP | TC_OPERAND | iexp;
1105 break;
1106
1107 case TC_NUMBER:
1108 case TC_STRING:
1109 cn->info = OC_VAR;
1110 v = cn->l.v = xcalloc(sizeof(var), 1);
1111 if (tc & TC_NUMBER)
1112 setvar_i(v, t.number);
1113 else
1114 setvar_s(v, t.string);
1115 break;
1116
1117 case TC_REGEXP:
1118 mk_re_node(t.string, cn,
1119 (regex_t *)xcalloc(sizeof(regex_t),2));
1120 break;
1121
1122 case TC_FUNCTION:
1123 cn->info = OC_FUNC;
1124 cn->r.f = newfunc(t.string);
1125 cn->l.n = condition();
1126 break;
1127
1128 case TC_SEQSTART:
1129 cn = vn->r.n = parse_expr(TC_SEQTERM);
1130 cn->a.n = vn;
1131 break;
1132
1133 case TC_GETLINE:
1134 glptr = cn;
1135 xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
1136 break;
1137
1138 case TC_BUILTIN:
1139 cn->l.n = condition();
1140 break;
1141 }
1142 }
1143 }
1144 }
1145 return sn.r.n;
1146}
1147
1148/* add node to chain. Return ptr to alloc'd node */
1149static node *chain_node(unsigned long info) {
1150
1151 register node *n;
1152
1153 if (! seq->first)
1154 seq->first = seq->last = new_node(0);
1155
1156 if (seq->programname != programname) {
1157 seq->programname = programname;
1158 n = chain_node(OC_NEWSOURCE);
1159 n->l.s = bb_xstrdup(programname);
1160 }
1161
1162 n = seq->last;
1163 n->info = info;
1164 seq->last = n->a.n = new_node(OC_DONE);
1165
1166 return n;
1167}
1168
1169static void chain_expr(unsigned long info) {
1170
1171 node *n;
1172
1173 n = chain_node(info);
1174 n->l.n = parse_expr(TC_OPTERM | TC_GRPTERM);
1175 if (t.tclass & TC_GRPTERM)
1176 rollback_token();
1177}
1178
1179static node *chain_loop(node *nn) {
1180
1181 node *n, *n2, *save_brk, *save_cont;
1182
1183 save_brk = break_ptr;
1184 save_cont = continue_ptr;
1185
1186 n = chain_node(OC_BR | Vx);
1187 continue_ptr = new_node(OC_EXEC);
1188 break_ptr = new_node(OC_EXEC);
1189 chain_group();
1190 n2 = chain_node(OC_EXEC | Vx);
1191 n2->l.n = nn;
1192 n2->a.n = n;
1193 continue_ptr->a.n = n2;
1194 break_ptr->a.n = n->r.n = seq->last;
1195
1196 continue_ptr = save_cont;
1197 break_ptr = save_brk;
1198
1199 return n;
1200}
1201
1202/* parse group and attach it to chain */
1203static void chain_group(void) {
1204
1205 unsigned long c;
1206 node *n, *n2, *n3;
1207
1208 do {
1209 c = next_token(TC_GRPSEQ);
1210 } while (c & TC_NEWLINE);
1211
1212 if (c & TC_GRPSTART) {
1213 while(next_token(TC_GRPSEQ | TC_GRPTERM) != TC_GRPTERM) {
1214 if (t.tclass & TC_NEWLINE) continue;
1215 rollback_token();
1216 chain_group();
1217 }
1218 } else if (c & (TC_OPSEQ | TC_OPTERM)) {
1219 rollback_token();
1220 chain_expr(OC_EXEC | Vx);
1221 } else { /* TC_STATEMNT */
1222 switch (t.info & OPCLSMASK) {
1223 case ST_IF:
1224 n = chain_node(OC_BR | Vx);
1225 n->l.n = condition();
1226 chain_group();
1227 n2 = chain_node(OC_EXEC);
1228 n->r.n = seq->last;
1229 if (next_token(TC_GRPSEQ | TC_GRPTERM | TC_ELSE)==TC_ELSE) {
1230 chain_group();
1231 n2->a.n = seq->last;
1232 } else {
1233 rollback_token();
1234 }
1235 break;
1236
1237 case ST_WHILE:
1238 n2 = condition();
1239 n = chain_loop(NULL);
1240 n->l.n = n2;
1241 break;
1242
1243 case ST_DO:
1244 n2 = chain_node(OC_EXEC);
1245 n = chain_loop(NULL);
1246 n2->a.n = n->a.n;
1247 next_token(TC_WHILE);
1248 n->l.n = condition();
1249 break;
1250
1251 case ST_FOR:
1252 next_token(TC_SEQSTART);
1253 n2 = parse_expr(TC_SEMICOL | TC_SEQTERM);
1254 if (t.tclass & TC_SEQTERM) { /* for-in */
1255 if ((n2->info & OPCLSMASK) != OC_IN)
1256 syntax_error(EMSG_UNEXP_TOKEN);
1257 n = chain_node(OC_WALKINIT | VV);
1258 n->l.n = n2->l.n;
1259 n->r.n = n2->r.n;
1260 n = chain_loop(NULL);
1261 n->info = OC_WALKNEXT | Vx;
1262 n->l.n = n2->l.n;
1263 } else { /* for(;;) */
1264 n = chain_node(OC_EXEC | Vx);
1265 n->l.n = n2;
1266 n2 = parse_expr(TC_SEMICOL);
1267 n3 = parse_expr(TC_SEQTERM);
1268 n = chain_loop(n3);
1269 n->l.n = n2;
1270 if (! n2)
1271 n->info = OC_EXEC;
1272 }
1273 break;
1274
1275 case OC_PRINT:
1276 case OC_PRINTF:
1277 n = chain_node(t.info);
1278 n->l.n = parse_expr(TC_OPTERM | TC_OUTRDR | TC_GRPTERM);
1279 if (t.tclass & TC_OUTRDR) {
1280 n->info |= t.info;
1281 n->r.n = parse_expr(TC_OPTERM | TC_GRPTERM);
1282 }
1283 if (t.tclass & TC_GRPTERM)
1284 rollback_token();
1285 break;
1286
1287 case OC_BREAK:
1288 n = chain_node(OC_EXEC);
1289 n->a.n = break_ptr;
1290 break;
1291
1292 case OC_CONTINUE:
1293 n = chain_node(OC_EXEC);
1294 n->a.n = continue_ptr;
1295 break;
1296
1297 /* delete, next, nextfile, return, exit */
1298 default:
1299 chain_expr(t.info);
1300
1301 }
1302 }
1303}
1304
1305static void parse_program(char *p) {
1306
1307 unsigned long tclass;
1308 node *cn;
1309 func *f;
1310 var *v;
1311
1312 pos = p;
1313 t.lineno = 1;
1314 while((tclass = next_token(TC_EOF | TC_OPSEQ | TC_GRPSTART |
1315 TC_OPTERM | TC_BEGIN | TC_END | TC_FUNCDECL)) != TC_EOF) {
1316
1317 if (tclass & TC_OPTERM)
1318 continue;
1319
1320 seq = &mainseq;
1321 if (tclass & TC_BEGIN) {
1322 seq = &beginseq;
1323 chain_group();
1324
1325 } else if (tclass & TC_END) {
1326 seq = &endseq;
1327 chain_group();
1328
1329 } else if (tclass & TC_FUNCDECL) {
1330 next_token(TC_FUNCTION);
1331 pos++;
1332 f = newfunc(t.string);
1333 f->body.first = NULL;
1334 f->nargs = 0;
1335 while(next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) {
1336 v = findvar(ahash, t.string);
1337 v->x.aidx = (f->nargs)++;
1338
1339 if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM)
1340 break;
1341 }
1342 seq = &(f->body);
1343 chain_group();
1344 clear_array(ahash);
1345
1346 } else if (tclass & TC_OPSEQ) {
1347 rollback_token();
1348 cn = chain_node(OC_TEST);
1349 cn->l.n = parse_expr(TC_OPTERM | TC_EOF | TC_GRPSTART);
1350 if (t.tclass & TC_GRPSTART) {
1351 rollback_token();
1352 chain_group();
1353 } else {
1354 chain_node(OC_PRINT);
1355 }
1356 cn->r.n = mainseq.last;
1357
1358 } else /* if (tclass & TC_GRPSTART) */ {
1359 rollback_token();
1360 chain_group();
1361 }
1362 }
1363}
1364
1365
1366/* -------- program execution part -------- */
1367
1368static node *mk_splitter(char *s, tsplitter *spl) {
1369
1370 register regex_t *re, *ire;
1371 node *n;
1372
1373 re = &spl->re[0];
1374 ire = &spl->re[1];
1375 n = &spl->n;
1376 if ((n->info && OPCLSMASK) == OC_REGEXP) {
1377 regfree(re);
1378 regfree(ire);
1379 }
1380 if (bb_strlen(s) > 1) {
1381 mk_re_node(s, n, re);
1382 } else {
1383 n->info = (unsigned long) *s;
1384 }
1385
1386 return n;
1387}
1388
1389/* use node as a regular expression. Supplied with node ptr and regex_t
1390 * storage space. Return ptr to regex (if result points to preg, it should
1391 * be later regfree'd manually
1392 */
1393static regex_t *as_regex(node *op, regex_t *preg) {
1394
1395 var *v;
1396 char *s;
1397
1398 if ((op->info & OPCLSMASK) == OC_REGEXP) {
1399 return icase ? op->r.ire : op->l.re;
1400 } else {
1401 v = nvalloc(1);
1402 s = getvar_s(evaluate(op, v));
1403 xregcomp(preg, s, icase ? REG_EXTENDED | REG_ICASE : REG_EXTENDED);
1404 nvfree(v);
1405 return preg;
1406 }
1407}
1408
1409/* gradually increasing buffer */
1410static void qrealloc(char **b, int n, int *size) {
1411
1412 if (! *b || n >= *size)
1413 *b = xrealloc(*b, *size = n + (n>>1) + 80);
1414}
1415
1416/* resize field storage space */
1417static void fsrealloc(int size) {
1418
1419 static int maxfields = 0;
1420 int i;
1421
1422 if (size >= maxfields) {
1423 i = maxfields;
1424 maxfields = size + 16;
1425 Fields = (var *)xrealloc(Fields, maxfields * sizeof(var));
1426 for (; i<maxfields; i++) {
1427 Fields[i].type = VF_SPECIAL;
1428 Fields[i].string = NULL;
1429 }
1430 }
1431
1432 if (size < nfields) {
1433 for (i=size; i<nfields; i++) {
1434 clrvar(Fields+i);
1435 }
1436 }
1437 nfields = size;
1438}
1439
1440static int awk_split(char *s, node *spl, char **slist) {
1441
1442 int l, n=0;
1443 char c[4];
1444 char *s1;
1445 regmatch_t pmatch[2];
1446
1447 /* in worst case, each char would be a separate field */
1448 *slist = s1 = bb_xstrndup(s, bb_strlen(s) * 2 + 3);
1449
1450 c[0] = c[1] = (char)spl->info;
1451 c[2] = c[3] = '\0';
1452 if (*getvar_s(V[RS]) == '\0') c[2] = '\n';
1453
1454 if ((spl->info & OPCLSMASK) == OC_REGEXP) { /* regex split */
1455 while (*s) {
1456 l = strcspn(s, c+2);
1457 if (regexec(icase ? spl->r.ire : spl->l.re, s, 1, pmatch, 0) == 0 &&
1458 pmatch[0].rm_so <= l) {
1459 l = pmatch[0].rm_so;
1460 if (pmatch[0].rm_eo == 0) { l++; pmatch[0].rm_eo++; }
1461 } else {
1462 pmatch[0].rm_eo = l;
1463 if (*(s+l)) pmatch[0].rm_eo++;
1464 }
1465
1466 memcpy(s1, s, l);
1467 *(s1+l) = '\0';
1468 nextword(&s1);
1469 s += pmatch[0].rm_eo;
1470 n++;
1471 }
1472 } else if (c[0] == '\0') { /* null split */
1473 while(*s) {
1474 *(s1++) = *(s++);
1475 *(s1++) = '\0';
1476 n++;
1477 }
1478 } else if (c[0] != ' ') { /* single-character split */
1479 if (icase) {
1480 c[0] = toupper(c[0]);
1481 c[1] = tolower(c[1]);
1482 }
1483 if (*s1) n++;
1484 while ((s1 = strpbrk(s1, c))) {
1485 *(s1++) = '\0';
1486 n++;
1487 }
1488 } else { /* space split */
1489 while (*s) {
1490 while (isspace(*s)) s++;
1491 if (! *s) break;
1492 n++;
1493 while (*s && !isspace(*s))
1494 *(s1++) = *(s++);
1495 *(s1++) = '\0';
1496 }
1497 }
1498 return n;
1499}
1500
1501static void split_f0(void) {
1502
1503 static char *fstrings = NULL;
1504 int i, n;
1505 char *s;
1506
1507 if (is_f0_split)
1508 return;
1509
1510 is_f0_split = TRUE;
1511 free(fstrings);
1512 fsrealloc(0);
1513 n = awk_split(getvar_s(V[F0]), &fsplitter.n, &fstrings);
1514 fsrealloc(n);
1515 s = fstrings;
1516 for (i=0; i<n; i++) {
1517 Fields[i].string = nextword(&s);
1518 Fields[i].type |= (VF_FSTR | VF_USER | VF_DIRTY);
1519 }
1520
1521 /* set NF manually to avoid side effects */
1522 clrvar(V[NF]);
1523 V[NF]->type = VF_NUMBER | VF_SPECIAL;
1524 V[NF]->number = nfields;
1525}
1526
1527/* perform additional actions when some internal variables changed */
1528static void handle_special(var *v) {
1529
1530 int n;
1531 char *b, *sep, *s;
1532 int sl, l, len, i, bsize;
1533
1534 if (! (v->type & VF_SPECIAL))
1535 return;
1536
1537 if (v == V[NF]) {
1538 n = (int)getvar_i(v);
1539 fsrealloc(n);
1540
1541 /* recalculate $0 */
1542 sep = getvar_s(V[OFS]);
1543 sl = bb_strlen(sep);
1544 b = NULL;
1545 len = 0;
1546 for (i=0; i<n; i++) {
1547 s = getvar_s(&Fields[i]);
1548 l = bb_strlen(s);
1549 if (b) {
1550 memcpy(b+len, sep, sl);
1551 len += sl;
1552 }
1553 qrealloc(&b, len+l+sl, &bsize);
1554 memcpy(b+len, s, l);
1555 len += l;
1556 }
1557 if (b) b[len] = '\0';
1558 setvar_p(V[F0], b);
1559 is_f0_split = TRUE;
1560
1561 } else if (v == V[F0]) {
1562 is_f0_split = FALSE;
1563
1564 } else if (v == V[FS]) {
1565 mk_splitter(getvar_s(v), &fsplitter);
1566
1567 } else if (v == V[RS]) {
1568 mk_splitter(getvar_s(v), &rsplitter);
1569
1570 } else if (v == V[IGNORECASE]) {
1571 icase = istrue(v);
1572
1573 } else { /* $n */
1574 n = getvar_i(V[NF]);
1575 setvar_i(V[NF], n > v-Fields ? n : v-Fields+1);
1576 /* right here v is invalid. Just to note... */
1577 }
1578}
1579
1580/* step through func/builtin/etc arguments */
1581static node *nextarg(node **pn) {
1582
1583 node *n;
1584
1585 n = *pn;
1586 if (n && (n->info & OPCLSMASK) == OC_COMMA) {
1587 *pn = n->r.n;
1588 n = n->l.n;
1589 } else {
1590 *pn = NULL;
1591 }
1592 return n;
1593}
1594
1595static void hashwalk_init(var *v, xhash *array) {
1596
1597 char **w;
1598 hash_item *hi;
1599 int i;
1600
1601 if (v->type & VF_WALK)
1602 free(v->x.walker);
1603
1604 v->type |= VF_WALK;
1605 w = v->x.walker = (char **)xcalloc(2 + 2*sizeof(char *) + array->glen, 1);
1606 *w = *(w+1) = (char *)(w + 2);
1607 for (i=0; i<array->csize; i++) {
1608 hi = array->items[i];
1609 while(hi) {
1610 strcpy(*w, hi->name);
1611 nextword(w);
1612 hi = hi->next;
1613 }
1614 }
1615}
1616
1617static int hashwalk_next(var *v) {
1618
1619 char **w;
1620
1621 w = v->x.walker;
1622 if (*(w+1) == *w)
1623 return FALSE;
1624
1625 setvar_s(v, nextword(w+1));
1626 return TRUE;
1627}
1628
1629/* evaluate node, return 1 when result is true, 0 otherwise */
1630static int ptest(node *pattern) {
1631 static var v;
1632
1633 return istrue(evaluate(pattern, &v));
1634}
1635
1636/* read next record from stream rsm into a variable v */
1637static int awk_getline(rstream *rsm, var *v) {
1638
1639 char *b;
1640 regmatch_t pmatch[2];
1641 int a, p, pp=0, size;
1642 int fd, so, eo, r, rp;
1643 char c, *m, *s;
1644
1645 /* we're using our own buffer since we need access to accumulating
1646 * characters
1647 */
1648 fd = fileno(rsm->F);
1649 m = rsm->buffer;
1650 a = rsm->adv;
1651 p = rsm->pos;
1652 size = rsm->size;
1653 c = (char) rsplitter.n.info;
1654 rp = 0;
1655
1656 if (! m) qrealloc(&m, 256, &size);
1657 do {
1658 b = m + a;
1659 so = eo = p;
1660 r = 1;
1661 if (p > 0) {
1662 if ((rsplitter.n.info & OPCLSMASK) == OC_REGEXP) {
1663 if (regexec(icase ? rsplitter.n.r.ire : rsplitter.n.l.re,
1664 b, 1, pmatch, 0) == 0) {
1665 so = pmatch[0].rm_so;
1666 eo = pmatch[0].rm_eo;
1667 if (b[eo] != '\0')
1668 break;
1669 }
1670 } else if (c != '\0') {
1671 s = strchr(b+pp, c);
1672 if (s) {
1673 so = eo = s-b;
1674 eo++;
1675 break;
1676 }
1677 } else {
1678 while (b[rp] == '\n')
1679 rp++;
1680 s = strstr(b+rp, "\n\n");
1681 if (s) {
1682 so = eo = s-b;
1683 while (b[eo] == '\n') eo++;
1684 if (b[eo] != '\0')
1685 break;
1686 }
1687 }
1688 }
1689
1690 if (a > 0) {
1691 memmove(m, (const void *)(m+a), p+1);
1692 b = m;
1693 a = 0;
1694 }
1695
1696 qrealloc(&m, a+p+128, &size);
1697 b = m + a;
1698 pp = p;
1699 p += safe_read(fd, b+p, size-p-1);
1700 if (p < pp) {
1701 p = 0;
1702 r = 0;
1703 setvar_i(V[ERRNO], errno);
1704 }
1705 b[p] = '\0';
1706
1707 } while (p > pp);
1708
1709 if (p == 0) {
1710 r--;
1711 } else {
1712 c = b[so]; b[so] = '\0';
1713 setvar_s(v, b+rp);
1714 v->type |= VF_USER;
1715 b[so] = c;
1716 c = b[eo]; b[eo] = '\0';
1717 setvar_s(V[RT], b+so);
1718 b[eo] = c;
1719 }
1720
1721 rsm->buffer = m;
1722 rsm->adv = a + eo;
1723 rsm->pos = p - eo;
1724 rsm->size = size;
1725
1726 return r;
1727}
1728
1729static int fmt_num(char *b, int size, char *format, double n, int int_as_int) {
1730
1731 int r=0;
1732 char c, *s=format;
1733
1734 if (int_as_int && n == (int)n) {
1735 r = snprintf(b, size, "%d", (int)n);
1736 } else {
1737 do { c = *s; } while (*s && *++s);
1738 if (strchr("diouxX", c)) {
1739 r = snprintf(b, size, format, (int)n);
1740 } else if (strchr("eEfgG", c)) {
1741 r = snprintf(b, size, format, n);
1742 } else {
1743 runtime_error(EMSG_INV_FMT);
1744 }
1745 }
1746 return r;
1747}
1748
1749
1750/* formatted output into an allocated buffer, return ptr to buffer */
1751static char *awk_printf(node *n) {
1752
1753 char *b = NULL;
1754 char *fmt, *s, *s1, *f;
1755 int i, j, incr, bsize;
1756 char c, c1;
1757 var *v, *arg;
1758
1759 v = nvalloc(1);
1760 fmt = f = bb_xstrdup(getvar_s(evaluate(nextarg(&n), v)));
1761
1762 i = 0;
1763 while (*f) {
1764 s = f;
1765 while (*f && (*f != '%' || *(++f) == '%'))
1766 f++;
1767 while (*f && !isalpha(*f))
1768 f++;
1769
1770 incr = (f - s) + MAXVARFMT;
1771 qrealloc(&b, incr+i, &bsize);
1772 c = *f; if (c != '\0') f++;
1773 c1 = *f ; *f = '\0';
1774 arg = evaluate(nextarg(&n), v);
1775
1776 j = i;
1777 if (c == 'c' || !c) {
1778 i += sprintf(b+i, s,
1779 is_numeric(arg) ? (char)getvar_i(arg) : *getvar_s(arg));
1780
1781 } else if (c == 's') {
1782 s1 = getvar_s(arg);
1783 qrealloc(&b, incr+i+bb_strlen(s1), &bsize);
1784 i += sprintf(b+i, s, s1);
1785
1786 } else {
1787 i += fmt_num(b+i, incr, s, getvar_i(arg), FALSE);
1788 }
1789 *f = c1;
1790
1791 /* if there was an error while sprintf, return value is negative */
1792 if (i < j) i = j;
1793
1794 }
1795
1796 b = xrealloc(b, i+1);
1797 free(fmt);
1798 nvfree(v);
1799 b[i] = '\0';
1800 return b;
1801}
1802
1803/* common substitution routine
1804 * replace (nm) substring of (src) that match (n) with (repl), store
1805 * result into (dest), return number of substitutions. If nm=0, replace
1806 * all matches. If src or dst is NULL, use $0. If ex=TRUE, enable
1807 * subexpression matching (\1-\9)
1808 */
1809static int awk_sub(node *rn, char *repl, int nm, var *src, var *dest, int ex) {
1810
1811 char *ds = NULL;
1812 char *sp, *s;
1813 int c, i, j, di, rl, so, eo, nbs, n, dssize;
1814 regmatch_t pmatch[10];
1815 regex_t sreg, *re;
1816
1817 re = as_regex(rn, &sreg);
1818 if (! src) src = V[F0];
1819 if (! dest) dest = V[F0];
1820
1821 i = di = 0;
1822 sp = getvar_s(src);
1823 rl = bb_strlen(repl);
1824 while (regexec(re, sp, 10, pmatch, sp==getvar_s(src) ? 0:REG_NOTBOL) == 0) {
1825 so = pmatch[0].rm_so;
1826 eo = pmatch[0].rm_eo;
1827
1828 qrealloc(&ds, di + eo + rl, &dssize);
1829 memcpy(ds + di, sp, eo);
1830 di += eo;
1831 if (++i >= nm) {
1832 /* replace */
1833 di -= (eo - so);
1834 nbs = 0;
1835 for (s = repl; *s; s++) {
1836 ds[di++] = c = *s;
1837 if (c == '\\') {
1838 nbs++;
1839 continue;
1840 }
1841 if (c == '&' || (ex && c >= '0' && c <= '9')) {
1842 di -= ((nbs + 3) >> 1);
1843 j = 0;
1844 if (c != '&') {
1845 j = c - '0';
1846 nbs++;
1847 }
1848 if (nbs % 2) {
1849 ds[di++] = c;
1850 } else {
1851 n = pmatch[j].rm_eo - pmatch[j].rm_so;
1852 qrealloc(&ds, di + rl + n, &dssize);
1853 memcpy(ds + di, sp + pmatch[j].rm_so, n);
1854 di += n;
1855 }
1856 }
1857 nbs = 0;
1858 }
1859 }
1860
1861 sp += eo;
1862 if (i == nm) break;
1863 if (eo == so) {
1864 if (! (ds[di++] = *sp++)) break;
1865 }
1866 }
1867
1868 qrealloc(&ds, di + strlen(sp), &dssize);
1869 strcpy(ds + di, sp);
1870 setvar_p(dest, ds);
1871 if (re == &sreg) regfree(re);
1872 return i;
1873}
1874
1875static var *exec_builtin(node *op, var *res) {
1876
1877 int (*to_xxx)(int);
1878 var *tv;
1879 node *an[4];
1880 var *av[4];
1881 char *as[4];
1882 regmatch_t pmatch[2];
1883 regex_t sreg, *re;
1884 static tsplitter tspl;
1885 node *spl;
1886 unsigned long isr, info;
1887 int nargs;
1888 time_t tt;
1889 char *s, *s1;
1890 int i, l, ll, n;
1891
1892 tv = nvalloc(4);
1893 isr = info = op->info;
1894 op = op->l.n;
1895
1896 av[2] = av[3] = NULL;
1897 for (i=0 ; i<4 && op ; i++) {
1898 an[i] = nextarg(&op);
1899 if (isr & 0x09000000) av[i] = evaluate(an[i], &tv[i]);
1900 if (isr & 0x08000000) as[i] = getvar_s(av[i]);
1901 isr >>= 1;
1902 }
1903
1904 nargs = i;
1905 if (nargs < (info >> 30))
1906 runtime_error(EMSG_TOO_FEW_ARGS);
1907
1908 switch (info & OPNMASK) {
1909
1910 case B_a2:
1911#ifdef CONFIG_FEATURE_AWK_MATH
1912 setvar_i(res, atan2(getvar_i(av[i]), getvar_i(av[1])));
1913#else
1914 runtime_error(EMSG_NO_MATH);
1915#endif
1916 break;
1917
1918 case B_sp:
1919 if (nargs > 2) {
1920 spl = (an[2]->info & OPCLSMASK) == OC_REGEXP ?
1921 an[2] : mk_splitter(getvar_s(evaluate(an[2], &tv[2])), &tspl);
1922 } else {
1923 spl = &fsplitter.n;
1924 }
1925
1926 n = awk_split(as[0], spl, &s);
1927 s1 = s;
1928 clear_array(iamarray(av[1]));
1929 for (i=1; i<=n; i++)
1930 setari_u(av[1], i, nextword(&s1));
1931 free(s);
1932 setvar_i(res, n);
1933 break;
1934
1935 case B_ss:
1936 l = bb_strlen(as[0]);
1937 i = getvar_i(av[1]) - 1;
1938 if (i>l) i=l; if (i<0) i=0;
1939 n = (nargs > 2) ? getvar_i(av[2]) : l-i;
1940 if (n<0) n=0;
1941 s = xmalloc(n+1);
1942 strncpy(s, as[0]+i, n);
1943 s[n] = '\0';
1944 setvar_p(res, s);
1945 break;
1946
1947 case B_lo:
1948 to_xxx = tolower;
1949 goto lo_cont;
1950
1951 case B_up:
1952 to_xxx = toupper;
1953lo_cont:
1954 s1 = s = bb_xstrdup(as[0]);
1955 while (*s1) {
1956 *s1 = (*to_xxx)(*s1);
1957 s1++;
1958 }
1959 setvar_p(res, s);
1960 break;
1961
1962 case B_ix:
1963 n = 0;
1964 ll = bb_strlen(as[1]);
1965 l = bb_strlen(as[0]) - ll;
1966 if (ll > 0 && l >= 0) {
1967 if (! icase) {
1968 s = strstr(as[0], as[1]);
1969 if (s) n = (s - as[0]) + 1;
1970 } else {
1971 /* this piece of code is terribly slow and
1972 * really should be rewritten
1973 */
1974 for (i=0; i<=l; i++) {
1975 if (strncasecmp(as[0]+i, as[1], ll) == 0) {
1976 n = i+1;
1977 break;
1978 }
1979 }
1980 }
1981 }
1982 setvar_i(res, n);
1983 break;
1984
1985 case B_ti:
1986 if (nargs > 1)
1987 tt = getvar_i(av[1]);
1988 else
1989 time(&tt);
1990 s = (nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y";
1991 i = strftime(buf, MAXVARFMT, s, localtime(&tt));
1992 buf[i] = '\0';
1993 setvar_s(res, buf);
1994 break;
1995
1996 case B_ma:
1997 re = as_regex(an[1], &sreg);
1998 n = regexec(re, as[0], 1, pmatch, 0);
1999 if (n == 0) {
2000 pmatch[0].rm_so++;
2001 pmatch[0].rm_eo++;
2002 } else {
2003 pmatch[0].rm_so = 0;
2004 pmatch[0].rm_eo = -1;
2005 }
2006 setvar_i(newvar("RSTART"), pmatch[0].rm_so);
2007 setvar_i(newvar("RLENGTH"), pmatch[0].rm_eo - pmatch[0].rm_so);
2008 setvar_i(res, pmatch[0].rm_so);
2009 if (re == &sreg) regfree(re);
2010 break;
2011
2012 case B_ge:
2013 awk_sub(an[0], as[1], getvar_i(av[2]), av[3], res, TRUE);
2014 break;
2015
2016 case B_gs:
2017 setvar_i(res, awk_sub(an[0], as[1], 0, av[2], av[2], FALSE));
2018 break;
2019
2020 case B_su:
2021 setvar_i(res, awk_sub(an[0], as[1], 1, av[2], av[2], FALSE));
2022 break;
2023 }
2024
2025 nvfree(tv);
2026 return res;
2027}
2028
2029/*
2030 * Evaluate node - the heart of the program. Supplied with subtree
2031 * and place where to store result. returns ptr to result.
2032 */
2033#define XC(n) ((n) >> 8)
2034
2035static var *evaluate(node *op, var *res) {
2036
2037 /* This procedure is recursive so we should count every byte */
2038 static var *fnargs = NULL;
2039 static unsigned int seed = 1;
2040 static regex_t sreg;
2041 node *op1;
2042 var *v1;
2043 union {
2044 var *v;
2045 char *s;
2046 double d;
2047 int i;
2048 } L, R;
2049 unsigned long opinfo;
2050 short opn;
2051 union {
2052 char *s;
2053 rstream *rsm;
2054 FILE *F;
2055 var *v;
2056 regex_t *re;
2057 unsigned long info;
2058 } X;
2059
2060 if (! op)
2061 return setvar_s(res, NULL);
2062
2063 v1 = nvalloc(2);
2064
2065 while (op) {
2066
2067 opinfo = op->info;
2068 opn = (short)(opinfo & OPNMASK);
2069 lineno = op->lineno;
2070
2071 /* execute inevitable things */
2072 op1 = op->l.n;
2073 if (opinfo & OF_RES1) X.v = L.v = evaluate(op1, v1);
2074 if (opinfo & OF_RES2) R.v = evaluate(op->r.n, v1+1);
2075 if (opinfo & OF_STR1) L.s = getvar_s(L.v);
2076 if (opinfo & OF_STR2) R.s = getvar_s(R.v);
2077 if (opinfo & OF_NUM1) L.d = getvar_i(L.v);
2078
2079 switch (XC(opinfo & OPCLSMASK)) {
2080
2081 /* -- iterative node type -- */
2082
2083 /* test pattern */
2084 case XC( OC_TEST ):
2085 if ((op1->info & OPCLSMASK) == OC_COMMA) {
2086 /* it's range pattern */
2087 if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) {
2088 op->info |= OF_CHECKED;
2089 if (ptest(op1->r.n))
2090 op->info &= ~OF_CHECKED;
2091
2092 op = op->a.n;
2093 } else {
2094 op = op->r.n;
2095 }
2096 } else {
2097 op = (ptest(op1)) ? op->a.n : op->r.n;
2098 }
2099 break;
2100
2101 /* just evaluate an expression, also used as unconditional jump */
2102 case XC( OC_EXEC ):
2103 break;
2104
2105 /* branch, used in if-else and various loops */
2106 case XC( OC_BR ):
2107 op = istrue(L.v) ? op->a.n : op->r.n;
2108 break;
2109
2110 /* initialize for-in loop */
2111 case XC( OC_WALKINIT ):
2112 hashwalk_init(L.v, iamarray(R.v));
2113 break;
2114
2115 /* get next array item */
2116 case XC( OC_WALKNEXT ):
2117 op = hashwalk_next(L.v) ? op->a.n : op->r.n;
2118 break;
2119
2120 case XC( OC_PRINT ):
2121 case XC( OC_PRINTF ):
2122 X.F = stdout;
2123 if (op->r.n) {
2124 X.rsm = newfile(R.s);
2125 if (! X.rsm->F) {
2126 if (opn == '|') {
2127 if((X.rsm->F = popen(R.s, "w")) == NULL)
2128 bb_perror_msg_and_die("popen");
2129 X.rsm->is_pipe = 1;
2130 } else {
2131 X.rsm->F = bb_xfopen(R.s, opn=='w' ? "w" : "a");
2132 }
2133 }
2134 X.F = X.rsm->F;
2135 }
2136
2137 if ((opinfo & OPCLSMASK) == OC_PRINT) {
2138 if (! op1) {
2139 fputs(getvar_s(V[F0]), X.F);
2140 } else {
2141 while (op1) {
2142 L.v = evaluate(nextarg(&op1), v1);
2143 if (L.v->type & VF_NUMBER) {
2144 fmt_num(buf, MAXVARFMT, getvar_s(V[OFMT]),
2145 getvar_i(L.v), TRUE);
2146 fputs(buf, X.F);
2147 } else {
2148 fputs(getvar_s(L.v), X.F);
2149 }
2150
2151 if (op1) fputs(getvar_s(V[OFS]), X.F);
2152 }
2153 }
2154 fputs(getvar_s(V[ORS]), X.F);
2155
2156 } else { /* OC_PRINTF */
2157 L.s = awk_printf(op1);
2158 fputs(L.s, X.F);
2159 free(L.s);
2160 }
2161 fflush(X.F);
2162 break;
2163
2164 case XC( OC_DELETE ):
2165 X.info = op1->info & OPCLSMASK;
2166 if (X.info == OC_VAR) {
2167 R.v = op1->l.v;
2168 } else if (X.info == OC_FNARG) {
2169 R.v = &fnargs[op1->l.i];
2170 } else {
2171 runtime_error(EMSG_NOT_ARRAY);
2172 }
2173
2174 if (op1->r.n) {
2175 clrvar(L.v);
2176 L.s = getvar_s(evaluate(op1->r.n, v1));
2177 hash_remove(iamarray(R.v), L.s);
2178 } else {
2179 clear_array(iamarray(R.v));
2180 }
2181 break;
2182
2183 case XC( OC_NEWSOURCE ):
2184 programname = op->l.s;
2185 break;
2186
2187 case XC( OC_RETURN ):
2188 copyvar(res, L.v);
2189 break;
2190
2191 case XC( OC_NEXTFILE ):
2192 nextfile = TRUE;
2193 case XC( OC_NEXT ):
2194 nextrec = TRUE;
2195 case XC( OC_DONE ):
2196 clrvar(res);
2197 break;
2198
2199 case XC( OC_EXIT ):
2200 awk_exit(L.d);
2201
2202 /* -- recursive node type -- */
2203
2204 case XC( OC_VAR ):
2205 L.v = op->l.v;
2206 if (L.v == V[NF])
2207 split_f0();
2208 goto v_cont;
2209
2210 case XC( OC_FNARG ):
2211 L.v = &fnargs[op->l.i];
2212
2213v_cont:
2214 res = (op->r.n) ? findvar(iamarray(L.v), R.s) : L.v;
2215 break;
2216
2217 case XC( OC_IN ):
2218 setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0);
2219 break;
2220
2221 case XC( OC_REGEXP ):
2222 op1 = op;
2223 L.s = getvar_s(V[F0]);
2224 goto re_cont;
2225
2226 case XC( OC_MATCH ):
2227 op1 = op->r.n;
2228re_cont:
2229 X.re = as_regex(op1, &sreg);
2230 R.i = regexec(X.re, L.s, 0, NULL, 0);
2231 if (X.re == &sreg) regfree(X.re);
2232 setvar_i(res, (R.i == 0 ? 1 : 0) ^ (opn == '!' ? 1 : 0));
2233 break;
2234
2235 case XC( OC_MOVE ):
2236 /* if source is a temporary string, jusk relink it to dest */
2237 if (R.v == v1+1 && R.v->string) {
2238 res = setvar_p(L.v, R.v->string);
2239 R.v->string = NULL;
2240 } else {
2241 res = copyvar(L.v, R.v);
2242 }
2243 break;
2244
2245 case XC( OC_TERNARY ):
2246 if ((op->r.n->info & OPCLSMASK) != OC_COLON)
2247 runtime_error(EMSG_POSSIBLE_ERROR);
2248 res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res);
2249 break;
2250
2251 case XC( OC_FUNC ):
2252 if (! op->r.f->body.first)
2253 runtime_error(EMSG_UNDEF_FUNC);
2254
2255 X.v = R.v = nvalloc(op->r.f->nargs+1);
2256 while (op1) {
2257 L.v = evaluate(nextarg(&op1), v1);
2258 copyvar(R.v, L.v);
2259 R.v->type |= VF_CHILD;
2260 R.v->x.parent = L.v;
2261 if (++R.v - X.v >= op->r.f->nargs)
2262 break;
2263 }
2264
2265 R.v = fnargs;
2266 fnargs = X.v;
2267
2268 L.s = programname;
2269 res = evaluate(op->r.f->body.first, res);
2270 programname = L.s;
2271
2272 nvfree(fnargs);
2273 fnargs = R.v;
2274 break;
2275
2276 case XC( OC_GETLINE ):
2277 case XC( OC_PGETLINE ):
2278 if (op1) {
2279 X.rsm = newfile(L.s);
2280 if (! X.rsm->F) {
2281 if ((opinfo & OPCLSMASK) == OC_PGETLINE) {
2282 X.rsm->F = popen(L.s, "r");
2283 X.rsm->is_pipe = TRUE;
2284 } else {
2285 X.rsm->F = fopen(L.s, "r"); /* not bb_xfopen! */
2286 }
2287 }
2288 } else {
2289 if (! iF) iF = next_input_file();
2290 X.rsm = iF;
2291 }
2292
2293 if (! X.rsm->F) {
2294 setvar_i(V[ERRNO], errno);
2295 setvar_i(res, -1);
2296 break;
2297 }
2298
2299 if (! op->r.n)
2300 R.v = V[F0];
2301
2302 L.i = awk_getline(X.rsm, R.v);
2303 if (L.i > 0) {
2304 if (! op1) {
2305 incvar(V[FNR]);
2306 incvar(V[NR]);
2307 }
2308 }
2309 setvar_i(res, L.i);
2310 break;
2311
2312 /* simple builtins */
2313 case XC( OC_FBLTIN ):
2314 switch (opn) {
2315
2316 case F_in:
2317 R.d = (int)L.d;
2318 break;
2319
2320 case F_rn:
2321 R.d = (double)rand() / (double)RAND_MAX;
2322 break;
2323
2324#ifdef CONFIG_FEATURE_AWK_MATH
2325 case F_co:
2326 R.d = cos(L.d);
2327 break;
2328
2329 case F_ex:
2330 R.d = exp(L.d);
2331 break;
2332
2333 case F_lg:
2334 R.d = log(L.d);
2335 break;
2336
2337 case F_si:
2338 R.d = sin(L.d);
2339 break;
2340
2341 case F_sq:
2342 R.d = sqrt(L.d);
2343 break;
2344#else
2345 case F_co:
2346 case F_ex:
2347 case F_lg:
2348 case F_si:
2349 case F_sq:
2350 runtime_error(EMSG_NO_MATH);
2351 break;
2352#endif
2353
2354 case F_sr:
2355 R.d = (double)seed;
2356 seed = op1 ? (unsigned int)L.d : (unsigned int)time(NULL);
2357 srand(seed);
2358 break;
2359
2360 case F_ti:
2361 R.d = time(NULL);
2362 break;
2363
2364 case F_le:
2365 if (! op1)
2366 L.s = getvar_s(V[F0]);
2367 R.d = bb_strlen(L.s);
2368 break;
2369
2370 case F_sy:
2371 fflush(NULL);
2372 R.d = (L.s && *L.s) ? system(L.s) : 0;
2373 break;
2374
2375 case F_ff:
2376 if (! op1)
2377 fflush(stdout);
2378 else {
2379 if (L.s && *L.s) {
2380 X.rsm = newfile(L.s);
2381 fflush(X.rsm->F);
2382 } else {
2383 fflush(NULL);
2384 }
2385 }
2386 break;
2387
2388 case F_cl:
2389 X.rsm = (rstream *)hash_search(fdhash, L.s);
2390 if (X.rsm) {
2391 R.i = X.rsm->is_pipe ? pclose(X.rsm->F) : fclose(X.rsm->F);
2392 free(X.rsm->buffer);
2393 hash_remove(fdhash, L.s);
2394 }
2395 if (R.i != 0)
2396 setvar_i(V[ERRNO], errno);
2397 R.d = (double)R.i;
2398 break;
2399 }
2400 setvar_i(res, R.d);
2401 break;
2402
2403 case XC( OC_BUILTIN ):
2404 res = exec_builtin(op, res);
2405 break;
2406
2407 case XC( OC_SPRINTF ):
2408 setvar_p(res, awk_printf(op1));
2409 break;
2410
2411 case XC( OC_UNARY ):
2412 X.v = R.v;
2413 L.d = R.d = getvar_i(R.v);
2414 switch (opn) {
2415 case 'P':
2416 L.d = ++R.d;
2417 goto r_op_change;
2418 case 'p':
2419 R.d++;
2420 goto r_op_change;
2421 case 'M':
2422 L.d = --R.d;
2423 goto r_op_change;
2424 case 'm':
2425 R.d--;
2426 goto r_op_change;
2427 case '!':
2428 L.d = istrue(X.v) ? 0 : 1;
2429 break;
2430 case '-':
2431 L.d = -R.d;
2432 break;
2433 r_op_change:
2434 setvar_i(X.v, R.d);
2435 }
2436 setvar_i(res, L.d);
2437 break;
2438
2439 case XC( OC_FIELD ):
2440 R.i = (int)getvar_i(R.v);
2441 if (R.i == 0) {
2442 res = V[F0];
2443 } else {
2444 split_f0();
2445 if (R.i > nfields)
2446 fsrealloc(R.i);
2447
2448 res = &Fields[R.i-1];
2449 }
2450 break;
2451
2452 /* concatenation (" ") and index joining (",") */
2453 case XC( OC_CONCAT ):
2454 case XC( OC_COMMA ):
2455 opn = bb_strlen(L.s) + bb_strlen(R.s) + 2;
2456 X.s = (char *)xmalloc(opn);
2457 strcpy(X.s, L.s);
2458 if ((opinfo & OPCLSMASK) == OC_COMMA) {
2459 L.s = getvar_s(V[SUBSEP]);
2460 X.s = (char *)xrealloc(X.s, opn + bb_strlen(L.s));
2461 strcat(X.s, L.s);
2462 }
2463 strcat(X.s, R.s);
2464 setvar_p(res, X.s);
2465 break;
2466
2467 case XC( OC_LAND ):
2468 setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0);
2469 break;
2470
2471 case XC( OC_LOR ):
2472 setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n));
2473 break;
2474
2475 case XC( OC_BINARY ):
2476 case XC( OC_REPLACE ):
2477 R.d = getvar_i(R.v);
2478 switch (opn) {
2479 case '+':
2480 L.d += R.d;
2481 break;
2482 case '-':
2483 L.d -= R.d;
2484 break;
2485 case '*':
2486 L.d *= R.d;
2487 break;
2488 case '/':
2489 if (R.d == 0) runtime_error(EMSG_DIV_BY_ZERO);
2490 L.d /= R.d;
2491 break;
2492 case '&':
2493#ifdef CONFIG_FEATURE_AWK_MATH
2494 L.d = pow(L.d, R.d);
2495#else
2496 runtime_error(EMSG_NO_MATH);
2497#endif
2498 break;
2499 case '%':
2500 if (R.d == 0) runtime_error(EMSG_DIV_BY_ZERO);
2501 L.d -= (int)(L.d / R.d) * R.d;
2502 break;
2503 }
2504 res = setvar_i(((opinfo&OPCLSMASK) == OC_BINARY) ? res : X.v, L.d);
2505 break;
2506
2507 case XC( OC_COMPARE ):
2508 if (is_numeric(L.v) && is_numeric(R.v)) {
2509 L.d = getvar_i(L.v) - getvar_i(R.v);
2510 } else {
2511 L.s = getvar_s(L.v);
2512 R.s = getvar_s(R.v);
2513 L.d = icase ? strcasecmp(L.s, R.s) : strcmp(L.s, R.s);
2514 }
2515 switch (opn & 0xfe) {
2516 case 0:
2517 R.i = (L.d > 0);
2518 break;
2519 case 2:
2520 R.i = (L.d >= 0);
2521 break;
2522 case 4:
2523 R.i = (L.d == 0);
2524 break;
2525 }
2526 setvar_i(res, (opn & 0x1 ? R.i : !R.i) ? 1 : 0);
2527 break;
2528
2529 default:
2530 runtime_error(EMSG_POSSIBLE_ERROR);
2531 }
2532 if ((opinfo & OPCLSMASK) <= SHIFT_TIL_THIS)
2533 op = op->a.n;
2534 if ((opinfo & OPCLSMASK) >= RECUR_FROM_THIS)
2535 break;
2536 if (nextrec)
2537 break;
2538 }
2539 nvfree(v1);
2540 return res;
2541}
2542
2543
2544/* -------- main & co. -------- */
2545
2546static int awk_exit(int r) {
2547
2548 unsigned int i;
2549 hash_item *hi;
2550 static var tv;
2551
2552 if (! exiting) {
2553 exiting = TRUE;
2554 nextrec = FALSE;
2555 evaluate(endseq.first, &tv);
2556 }
2557
2558 /* waiting for children */
2559 for (i=0; i<fdhash->csize; i++) {
2560 hi = fdhash->items[i];
2561 while(hi) {
2562 if (hi->data.rs.F && hi->data.rs.is_pipe)
2563 pclose(hi->data.rs.F);
2564 hi = hi->next;
2565 }
2566 }
2567
2568 exit(r);
2569}
2570
2571/* if expr looks like "var=value", perform assignment and return 1,
2572 * otherwise return 0 */
2573static int is_assignment(char *expr) {
2574
2575 char *exprc, *s, *s0, *s1;
2576
2577 exprc = bb_xstrdup(expr);
2578 if (!isalnum_(*exprc) || (s = strchr(exprc, '=')) == NULL) {
2579 free(exprc);
2580 return FALSE;
2581 }
2582
2583 *(s++) = '\0';
2584 s0 = s1 = s;
2585 while (*s)
2586 *(s1++) = nextchar(&s);
2587
2588 *s1 = '\0';
2589 setvar_u(newvar(exprc), s0);
2590 free(exprc);
2591 return TRUE;
2592}
2593
2594/* switch to next input file */
2595static rstream *next_input_file(void) {
2596
2597 static rstream rsm;
2598 FILE *F = NULL;
2599 char *fname, *ind;
2600 static int files_happen = FALSE;
2601
2602 if (rsm.F) fclose(rsm.F);
2603 rsm.F = NULL;
2604 rsm.pos = rsm.adv = 0;
2605
2606 do {
2607 if (getvar_i(V[ARGIND])+1 >= getvar_i(V[ARGC])) {
2608 if (files_happen)
2609 return NULL;
2610 fname = "-";
2611 F = stdin;
2612 } else {
2613 ind = getvar_s(incvar(V[ARGIND]));
2614 fname = getvar_s(findvar(iamarray(V[ARGV]), ind));
2615 if (fname && *fname && !is_assignment(fname))
2616 F = afopen(fname, "r");
2617 }
2618 } while (!F);
2619
2620 files_happen = TRUE;
2621 setvar_s(V[FILENAME], fname);
2622 rsm.F = F;
2623 return &rsm;
2624}
2625
2626extern int awk_main(int argc, char **argv) {
2627
2628 char *s, *s1;
2629 int i, j, c;
2630 var *v;
2631 static var tv;
2632 char **envp;
2633 static int from_file = FALSE;
2634 rstream *rsm;
2635 FILE *F, *stdfiles[3];
2636 static char * stdnames = "/dev/stdin\0/dev/stdout\0/dev/stderr";
2637
2638 /* allocate global buffer */
2639 buf = xmalloc(MAXVARFMT+1);
2640
2641 vhash = hash_init();
2642 ahash = hash_init();
2643 fdhash = hash_init();
2644 fnhash = hash_init();
2645
2646 /* initialize variables */
2647 for (i=0; *vNames; i++) {
2648 V[i] = v = newvar(nextword(&vNames));
2649 if (*vValues != '\377')
2650 setvar_s(v, nextword(&vValues));
2651 else
2652 setvar_i(v, 0);
2653
2654 if (*vNames == '*') {
2655 v->type |= VF_SPECIAL;
2656 vNames++;
2657 }
2658 }
2659
2660 handle_special(V[FS]);
2661 handle_special(V[RS]);
2662
2663 stdfiles[0] = stdin;
2664 stdfiles[1] = stdout;
2665 stdfiles[2] = stderr;
2666 for (i=0; i<3; i++) {
2667 rsm = newfile(nextword(&stdnames));
2668 rsm->F = stdfiles[i];
2669 }
2670
2671 for (envp=environ; *envp; envp++) {
2672 s = bb_xstrdup(*envp);
2673 s1 = strchr(s, '=');
2674 if (!s1) {
2675 goto keep_going;
2676 }
2677 *(s1++) = '\0';
2678 setvar_u(findvar(iamarray(V[ENVIRON]), s), s1);
2679keep_going:
2680 free(s);
2681 }
2682
2683 while((c = getopt(argc, argv, "F:v:f:W:")) != EOF) {
2684 switch (c) {
2685 case 'F':
2686 setvar_s(V[FS], optarg);
2687 break;
2688 case 'v':
2689 if (! is_assignment(optarg))
2690 bb_show_usage();
2691 break;
2692 case 'f':
2693 from_file = TRUE;
2694 F = afopen(programname = optarg, "r");
2695 s = NULL;
2696 /* one byte is reserved for some trick in next_token */
2697 for (i=j=1; j>0; i+=j) {
2698 s = (char *)xrealloc(s, i+4096);
2699 j = fread(s+i, 1, 4094, F);
2700 }
2701 s[i] = '\0';
2702 fclose(F);
2703 parse_program(s+1);
2704 free(s);
2705 break;
2706 case 'W':
2707 bb_error_msg("Warning: unrecognized option '-W %s' ignored\n", optarg);
2708 break;
2709
2710 default:
2711 bb_show_usage();
2712 }
2713 }
2714
2715 if (!from_file) {
2716 if (argc == optind)
2717 bb_show_usage();
2718 programname="cmd. line";
2719 parse_program(argv[optind++]);
2720
2721 }
2722
2723 /* fill in ARGV array */
2724 setvar_i(V[ARGC], argc - optind + 1);
2725 setari_u(V[ARGV], 0, "awk");
2726 for(i=optind; i < argc; i++)
2727 setari_u(V[ARGV], i+1-optind, argv[i]);
2728
2729 evaluate(beginseq.first, &tv);
2730 if (! mainseq.first && ! endseq.first)
2731 awk_exit(EXIT_SUCCESS);
2732
2733 /* input file could already be opened in BEGIN block */
2734 if (! iF) iF = next_input_file();
2735
2736 /* passing through input files */
2737 while (iF) {
2738
2739 nextfile = FALSE;
2740 setvar_i(V[FNR], 0);
2741
2742 while ((c = awk_getline(iF, V[F0])) > 0) {
2743
2744 nextrec = FALSE;
2745 incvar(V[NR]);
2746 incvar(V[FNR]);
2747 evaluate(mainseq.first, &tv);
2748
2749 if (nextfile)
2750 break;
2751 }
2752
2753 if (c < 0)
2754 runtime_error(strerror(errno));
2755
2756 iF = next_input_file();
2757
2758 }
2759
2760 awk_exit(EXIT_SUCCESS);
2761
2762 return 0;
2763}
2764
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * busybox patch applet to handle the unified diff format.
4 * Copyright (C) 2003 Glenn McGrath <bug1@iinet.net.au>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 *
20 *
21 *
22 * This applet is written to work with patches generated by GNU diff,
23 * where there is equivalent functionality busybox patch shall behave
24 * as per GNU patch.
25 *
26 * There is a SUSv3 specification for patch, however it looks to be
27 * incomplete, it doesnt even mention unified diff format.
28 * http://www.opengroup.org/onlinepubs/007904975/utilities/patch.html
29 *
30 * Issues
31 * - Non-interactive
32 * - Patches must apply cleanly or the hunk will fail.
33 * - Reject file isnt saved
34 * -
35 */
36
37#include <getopt.h>
38#include <string.h>
39#include <stdlib.h>
40#include <unistd.h>
41#include "busybox.h"
42#include "libbb.h"
43
44static int copy_lines(FILE *src_stream, FILE *dest_stream, const unsigned int lines_count)
45{
46 int i = 0;
47
48 while (src_stream && (i < lines_count)) {
49 char *line;
50 line = bb_get_line_from_file(src_stream);
51 if (line == NULL) {
52 break;
53 }
54 if (fputs(line, dest_stream) == EOF) {
55 bb_perror_msg_and_die("Error writing to new file");
56 }
57 free(line);
58
59 i++;
60 }
61 return(i);
62}
63
64/* If patch_level is -1 it will remove all directory names
65 * char *line must be greater than 4 chars
66 * returns NULL if the file doesnt exist or error
67 * returns malloc'ed filename
68 */
69
70static unsigned char *extract_filename(char *line, unsigned short patch_level)
71{
72 char *filename_start_ptr = line + 4;
73 int i;
74
75 /* Terminate string at end of source filename */
76 {
77 char *line_ptr;
78 line_ptr = strchr(filename_start_ptr, '\t');
79 if (!line_ptr) {
80 bb_perror_msg("Malformed line %s", line);
81 return(NULL);
82 }
83 *line_ptr = '\0';
84 }
85
86 /* Skip over (patch_level) number of leading directories */
87 for (i = 0; i < patch_level; i++) {
88 char *dirname_ptr;
89
90 dirname_ptr = strchr(filename_start_ptr, '/');
91 if (!dirname_ptr) {
92 break;
93 }
94 filename_start_ptr = dirname_ptr + 1;
95 }
96
97 return(bb_xstrdup(filename_start_ptr));
98}
99
100static int file_doesnt_exist(const char *filename)
101{
102 struct stat statbuf;
103 return(stat(filename, &statbuf));
104}
105
106extern int patch_main(int argc, char **argv)
107{
108 unsigned int patch_level = -1;
109 char *patch_line;
110 int ret = 0;
111
112 /* Handle 'p' option */
113 if (argv[1] && (argv[1][0] == '-') && (argv[1][1] == 'p')) {
114 patch_level = atoi(&argv[1][2]);
115 }
116
117 patch_line = bb_get_line_from_file(stdin);
118 while (patch_line) {
119 FILE *src_stream;
120 FILE *dst_stream;
121 char *original_filename;
122 char *new_filename;
123 char *backup_filename;
124 unsigned int src_cur_line = 1;
125 unsigned int dest_cur_line = 0;
126 unsigned int dest_beg_line;
127 unsigned int bad_hunk_count = 0;
128 unsigned int hunk_count = 0;
129 char copy_trailing_lines_flag = 0;
130
131 /* Skip everything upto the "---" marker
132 * No need to parse the lines "Only in <dir>", and "diff <args>"
133 */
134 while (patch_line && strncmp(patch_line, "--- ", 4) != 0) {
135 free(patch_line);
136 patch_line = bb_get_line_from_file(stdin);
137 }
138
139 /* Extract the filename used before the patch was generated */
140 original_filename = extract_filename(patch_line, patch_level);
141 free(patch_line);
142
143 patch_line = bb_get_line_from_file(stdin);
144 if (strncmp(patch_line, "+++ ", 4) != 0) {
145 ret = 2;
146 bb_error_msg("Invalid patch");
147 continue;
148 }
149 new_filename = extract_filename(patch_line, patch_level);
150 free(patch_line);
151
152 if (file_doesnt_exist(new_filename)) {
153 char *line_ptr;
154 /* Create leading directories */
155 line_ptr = strrchr(new_filename, '/');
156 if (line_ptr) {
157 *line_ptr = '\0';
158 bb_make_directory(new_filename, -1, FILEUTILS_RECUR);
159 *line_ptr = '/';
160 }
161 dst_stream = bb_xfopen(new_filename, "w+");
162 backup_filename = NULL;
163 } else {
164 backup_filename = xmalloc(strlen(new_filename) + 6);
165 strcpy(backup_filename, new_filename);
166 strcat(backup_filename, ".orig");
167 if (rename(new_filename, backup_filename) == -1) {
168 bb_perror_msg_and_die("Couldnt create file %s", backup_filename);
169 }
170 dst_stream = bb_xfopen(new_filename, "w");
171 }
172
173 if ((backup_filename == NULL) || file_doesnt_exist(original_filename)) {
174 src_stream = NULL;
175 } else {
176 if (strcmp(original_filename, new_filename) == 0) {
177 src_stream = bb_xfopen(backup_filename, "r");
178 } else {
179 src_stream = bb_xfopen(original_filename, "r");
180 }
181 }
182
183 printf("patching file %s\n", new_filename);
184
185 /* Handle each hunk */
186 patch_line = bb_get_line_from_file(stdin);
187 while (patch_line) {
188 unsigned int count;
189 unsigned int src_beg_line;
190 unsigned int unused;
191 unsigned int hunk_offset_start = 0;
192 int hunk_error = 0;
193
194 /* This bit should be improved */
195 if ((sscanf(patch_line, "@@ -%d,%d +%d,%d @@", &src_beg_line, &unused, &dest_beg_line, &unused) != 4) &&
196 (sscanf(patch_line, "@@ -%d,%d +%d @@", &src_beg_line, &unused, &dest_beg_line) != 3) &&
197 (sscanf(patch_line, "@@ -%d +%d,%d @@", &src_beg_line, &dest_beg_line, &unused) != 3)) {
198 /* No more hunks for this file */
199 break;
200 }
201 free(patch_line);
202 hunk_count++;
203
204 if (src_beg_line && dest_beg_line) {
205 /* Copy unmodified lines upto start of hunk */
206 /* src_beg_line will be 0 if its a new file */
207 count = src_beg_line - src_cur_line;
208 if (copy_lines(src_stream, dst_stream, count) != count) {
209 bb_error_msg_and_die("Bad src file");
210 }
211 src_cur_line += count;
212 dest_cur_line += count;
213 copy_trailing_lines_flag = 1;
214 }
215 hunk_offset_start = src_cur_line;
216
217 while ((patch_line = bb_get_line_from_file(stdin)) != NULL) {
218 if ((*patch_line == '-') || (*patch_line == ' ')) {
219 char *src_line = NULL;
220 if (src_stream) {
221 src_line = bb_get_line_from_file(src_stream);
222 if (!src_line) {
223 hunk_error++;
224 break;
225 } else {
226 src_cur_line++;
227 }
228 if (strcmp(src_line, patch_line + 1) != 0) {
229 bb_error_msg("Hunk #%d FAILED at %d.", hunk_count, hunk_offset_start);
230 hunk_error++;
231 free(patch_line);
232 break;
233 }
234 free(src_line);
235 }
236 if (*patch_line == ' ') {
237 fputs(patch_line + 1, dst_stream);
238 dest_cur_line++;
239 }
240 } else if (*patch_line == '+') {
241 fputs(patch_line + 1, dst_stream);
242 dest_cur_line++;
243 } else {
244 break;
245 }
246 free(patch_line);
247 }
248 if (hunk_error) {
249 bad_hunk_count++;
250 }
251 }
252
253 /* Cleanup last patched file */
254 if (copy_trailing_lines_flag) {
255 copy_lines(src_stream, dst_stream, -1);
256 }
257 if (src_stream) {
258 fclose(src_stream);
259 }
260 if (dst_stream) {
261 fclose(dst_stream);
262 }
263 if (bad_hunk_count) {
264 if (!ret) {
265 ret = 1;
266 }
267 bb_error_msg("%d out of %d hunk FAILED", bad_hunk_count, hunk_count);
268 } else {
269 /* It worked, we can remove the backup */
270 if (backup_filename) {
271 unlink(backup_filename);
272 }
273 if ((dest_cur_line == 0) || (dest_beg_line == 0)) {
274 /* The new patched file is empty, remove it */
275 if (unlink(new_filename) == -1) {
276 bb_perror_msg_and_die("Couldnt remove file %s", new_filename);
277 }
278 if (unlink(original_filename) == -1) {
279 bb_perror_msg_and_die("Couldnt remove original file %s", new_filename);
280 }
281 }
282 }
283 }
284
285 /* 0 = SUCCESS
286 * 1 = Some hunks failed
287 * 2 = More serious problems
288 */
289 return(ret);
290}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * sed.c - very minimalist version of sed
4 *
5 * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley
6 * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
7 * Copyright (C) 2002 Matt Kraai
8 * Copyright (C) 2003 by Glenn McGrath <bug1@iinet.net.au>
9 * Copyright (C) 2003,2004 by Rob Landley <rob@landley.net>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27/* Code overview.
28
29 Files are laid out to avoid unnecessary function declarations. So for
30 example, every function add_cmd calls occurs before add_cmd in this file.
31
32 add_cmd() is called on each line of sed command text (from a file or from
33 the command line). It calls get_address() and parse_cmd_args(). The
34 resulting sed_cmd_t structures are appended to a linked list
35 (sed_cmd_head/sed_cmd_tail).
36
37 process_file() does actual sedding, reading data lines from an input FILE *
38 (which could be stdin) and applying the sed command list (sed_cmd_head) to
39 each of the resulting lines.
40
41 sed_main() is where external code calls into this, with a command line.
42*/
43
44
45/*
46 Supported features and commands in this version of sed:
47
48 - comments ('#')
49 - address matching: num|/matchstr/[,num|/matchstr/|$]command
50 - commands: (p)rint, (d)elete, (s)ubstitue (with g & I flags)
51 - edit commands: (a)ppend, (i)nsert, (c)hange
52 - file commands: (r)ead
53 - backreferences in substitution expressions (\1, \2...\9)
54 - grouped commands: {cmd1;cmd2}
55 - transliteration (y/source-chars/dest-chars/)
56 - pattern space hold space storing / swapping (g, h, x)
57 - labels / branching (: label, b, t)
58
59 (Note: Specifying an address (range) to match is *optional*; commands
60 default to the whole pattern space if no specific address match was
61 requested.)
62
63 Unsupported features:
64
65 - GNU extensions
66 - and more.
67
68 Todo:
69
70 - Create a wrapper around regex to make libc's regex conform with sed
71 - Fix bugs
72
73
74 Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html
75*/
76
77#include <stdio.h>
78#include <unistd.h> /* for getopt() */
79#include <regex.h>
80#include <string.h> /* for strdup() */
81#include <errno.h>
82#include <ctype.h> /* for isspace() */
83#include <stdlib.h>
84#include "busybox.h"
85
86typedef struct sed_cmd_s {
87 /* Ordered by alignment requirements: currently 36 bytes on x86 */
88
89 /* address storage */
90 regex_t *beg_match; /* sed -e '/match/cmd' */
91 regex_t *end_match; /* sed -e '/match/,/end_match/cmd' */
92 regex_t *sub_match; /* For 's/sub_match/string/' */
93 int beg_line; /* 'sed 1p' 0 == apply commands to all lines */
94 int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */
95
96 FILE *file; /* File (sr) command writes to, -1 for none. */
97 char *string; /* Data string for (saicytb) commands. */
98
99 unsigned short which_match; /* (s) Which match to replace (0 for all) */
100
101 /* Bitfields (gcc won't group them if we don't) */
102 unsigned int invert:1; /* the '!' after the address */
103 unsigned int in_match:1; /* Next line also included in match? */
104 unsigned int no_newline:1; /* Last line written by (sr) had no '\n' */
105 unsigned int sub_p:1; /* (s) print option */
106
107
108 /* GENERAL FIELDS */
109 char cmd; /* The command char: abcdDgGhHilnNpPqrstwxy:={} */
110 struct sed_cmd_s *next; /* Next command (linked list, NULL terminated) */
111} sed_cmd_t;
112
113/* globals */
114/* options */
115static int be_quiet = 0, in_place=0, regex_type=0;
116FILE *nonstdout;
117char *outname;
118
119
120static const char bad_format_in_subst[] =
121 "bad format in substitution expression";
122const char *const semicolon_whitespace = "; \n\r\t\v";
123
124regmatch_t regmatch[10];
125static regex_t *previous_regex_ptr = NULL;
126
127/* linked list of sed commands */
128static sed_cmd_t sed_cmd_head;
129static sed_cmd_t *sed_cmd_tail = &sed_cmd_head;
130
131/* Linked list of append lines */
132struct append_list {
133 char *string;
134 struct append_list *next;
135};
136struct append_list *append_head=NULL, *append_tail=NULL;
137
138#ifdef CONFIG_FEATURE_CLEAN_UP
139static void free_and_close_stuff(void)
140{
141 sed_cmd_t *sed_cmd = sed_cmd_head.next;
142
143 while(append_head) {
144 append_tail=append_head->next;
145 free(append_head->string);
146 free(append_head);
147 append_head=append_tail;
148 }
149
150 while (sed_cmd) {
151 sed_cmd_t *sed_cmd_next = sed_cmd->next;
152
153 if(sed_cmd->file)
154 bb_xprint_and_close_file(sed_cmd->file);
155
156 if (sed_cmd->beg_match) {
157 regfree(sed_cmd->beg_match);
158 free(sed_cmd->beg_match);
159 }
160 if (sed_cmd->end_match) {
161 regfree(sed_cmd->end_match);
162 free(sed_cmd->end_match);
163 }
164 if (sed_cmd->sub_match) {
165 regfree(sed_cmd->sub_match);
166 free(sed_cmd->sub_match);
167 }
168 free(sed_cmd->string);
169 free(sed_cmd);
170 sed_cmd = sed_cmd_next;
171 }
172}
173#endif
174
175/* If something bad happens during -i operation, delete temp file */
176
177static void cleanup_outname(void)
178{
179 if(outname) unlink(outname);
180}
181
182/* strdup, replacing "\n" with '\n', and "\delimiter" with 'delimiter' */
183
184static void parse_escapes(char *dest, const char *string, int len, char from, char to)
185{
186 int i=0;
187
188 while(i<len) {
189 if(string[i] == '\\') {
190 if(!to || string[i+1] == from) {
191 *(dest++) = to ? to : string[i+1];
192 i+=2;
193 continue;
194 } else *(dest++)=string[i++];
195 }
196 *(dest++) = string[i++];
197 }
198 *dest=0;
199}
200
201static char *copy_parsing_slashn(const char *string, int len)
202{
203 char *dest=xmalloc(len+1);
204
205 parse_escapes(dest,string,len,'n','\n');
206 return dest;
207}
208
209
210/*
211 * index_of_next_unescaped_regexp_delim - walks left to right through a string
212 * beginning at a specified index and returns the index of the next regular
213 * expression delimiter (typically a forward * slash ('/')) not preceded by
214 * a backslash ('\').
215 */
216static int index_of_next_unescaped_regexp_delim(const char delimiter,
217 const char *str)
218{
219 int bracket = -1;
220 int escaped = 0;
221 int idx = 0;
222 char ch;
223
224 for (; (ch = str[idx]); idx++) {
225 if (bracket != -1) {
226 if (ch == ']' && !(bracket == idx - 1 || (bracket == idx - 2
227 && str[idx - 1] == '^')))
228 bracket = -1;
229 } else if (escaped)
230 escaped = 0;
231 else if (ch == '\\')
232 escaped = 1;
233 else if (ch == '[')
234 bracket = idx;
235 else if (ch == delimiter)
236 return idx;
237 }
238
239 /* if we make it to here, we've hit the end of the string */
240 return -1;
241}
242
243/*
244 * Returns the index of the third delimiter
245 */
246static int parse_regex_delim(const char *cmdstr, char **match, char **replace)
247{
248 const char *cmdstr_ptr = cmdstr;
249 char delimiter;
250 int idx = 0;
251
252 /* verify that the 's' or 'y' is followed by something. That something
253 * (typically a 'slash') is now our regexp delimiter... */
254 if (*cmdstr == '\0') bb_error_msg_and_die(bad_format_in_subst);
255 delimiter = *(cmdstr_ptr++);
256
257 /* save the match string */
258 idx = index_of_next_unescaped_regexp_delim(delimiter, cmdstr_ptr);
259 if (idx == -1) {
260 bb_error_msg_and_die(bad_format_in_subst);
261 }
262 *match = copy_parsing_slashn(cmdstr_ptr, idx);
263
264 /* save the replacement string */
265 cmdstr_ptr += idx + 1;
266 idx = index_of_next_unescaped_regexp_delim(delimiter, cmdstr_ptr);
267 if (idx == -1) {
268 bb_error_msg_and_die(bad_format_in_subst);
269 }
270 *replace = copy_parsing_slashn(cmdstr_ptr, idx);
271
272 return ((cmdstr_ptr - cmdstr) + idx);
273}
274
275/*
276 * returns the index in the string just past where the address ends.
277 */
278static int get_address(char *my_str, int *linenum, regex_t ** regex)
279{
280 char *pos = my_str;
281
282 if (isdigit(*my_str)) {
283 *linenum = strtol(my_str, &pos, 10);
284 /* endstr shouldnt ever equal NULL */
285 } else if (*my_str == '$') {
286 *linenum = -1;
287 pos++;
288 } else if (*my_str == '/' || *my_str == '\\') {
289 int next;
290 char delimiter;
291 char *temp;
292
293 if (*my_str == '\\') delimiter = *(++pos);
294 else delimiter = '/';
295 next = index_of_next_unescaped_regexp_delim(delimiter, ++pos);
296 if (next == -1)
297 bb_error_msg_and_die("unterminated match expression");
298
299 temp=copy_parsing_slashn(pos,next);
300 *regex = (regex_t *) xmalloc(sizeof(regex_t));
301 xregcomp(*regex, temp, regex_type|REG_NEWLINE);
302 free(temp);
303 /* Move position to next character after last delimiter */
304 pos+=(next+1);
305 }
306 return pos - my_str;
307}
308
309/* Grab a filename. Whitespace at start is skipped, then goes to EOL. */
310static int parse_file_cmd(sed_cmd_t * sed_cmd, const char *filecmdstr, char **retval)
311{
312 int start = 0, idx, hack=0;
313
314 /* Skip whitespace, then grab filename to end of line */
315 while (isspace(filecmdstr[start])) start++;
316 idx=start;
317 while(filecmdstr[idx] && filecmdstr[idx]!='\n') idx++;
318 /* If lines glued together, put backslash back. */
319 if(filecmdstr[idx]=='\n') hack=1;
320 if(idx==start) bb_error_msg_and_die("Empty filename");
321 *retval = bb_xstrndup(filecmdstr+start, idx-start+hack+1);
322 if(hack) *(idx+*retval)='\\';
323
324 return idx;
325}
326
327static int parse_subst_cmd(sed_cmd_t * const sed_cmd, char *substr)
328{
329 int cflags = regex_type;
330 char *match;
331 int idx = 0;
332
333 /*
334 * A substitution command should look something like this:
335 * s/match/replace/ #gIpw
336 * || | |||
337 * mandatory optional
338 */
339 idx = parse_regex_delim(substr, &match, &sed_cmd->string);
340
341 /* determine the number of back references in the match string */
342 /* Note: we compute this here rather than in the do_subst_command()
343 * function to save processor time, at the expense of a little more memory
344 * (4 bits) per sed_cmd */
345
346 /* process the flags */
347
348 sed_cmd->which_match=1;
349 while (substr[++idx]) {
350 /* Parse match number */
351 if(isdigit(substr[idx])) {
352 if(match[0]!='^') {
353 /* Match 0 treated as all, multiple matches we take the last one. */
354 char *pos=substr+idx;
355 sed_cmd->which_match=(unsigned short)strtol(substr+idx,&pos,10);
356 idx=pos-substr;
357 }
358 continue;
359 }
360 /* Skip spaces */
361 if(isspace(substr[idx])) continue;
362
363 switch (substr[idx]) {
364 /* Replace all occurrences */
365 case 'g':
366 if (match[0] != '^') sed_cmd->which_match = 0;
367 break;
368 /* Print pattern space */
369 case 'p':
370 sed_cmd->sub_p = 1;
371 break;
372 case 'w':
373 {
374 char *temp;
375 idx+=parse_file_cmd(sed_cmd,substr+idx,&temp);
376
377 break;
378 }
379 /* Ignore case (gnu exension) */
380 case 'I':
381 cflags |= REG_ICASE;
382 break;
383 case ';':
384 case '}':
385 goto out;
386 default:
387 bb_error_msg_and_die("bad option in substitution expression");
388 }
389 }
390out:
391 /* compile the match string into a regex */
392 if (*match != '\0') {
393 /* If match is empty, we use last regex used at runtime */
394 sed_cmd->sub_match = (regex_t *) xmalloc(sizeof(regex_t));
395 xregcomp(sed_cmd->sub_match, match, cflags);
396 }
397 free(match);
398
399 return idx;
400}
401
402/*
403 * Process the commands arguments
404 */
405static char *parse_cmd_args(sed_cmd_t *sed_cmd, char *cmdstr)
406{
407 /* handle (s)ubstitution command */
408 if (sed_cmd->cmd == 's') cmdstr += parse_subst_cmd(sed_cmd, cmdstr);
409 /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */
410 else if (strchr("aic", sed_cmd->cmd)) {
411 if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c')
412 bb_error_msg_and_die
413 ("only a beginning address can be specified for edit commands");
414 for(;;) {
415 if(*cmdstr=='\n' || *cmdstr=='\\') {
416 cmdstr++;
417 break;
418 } else if(isspace(*cmdstr)) cmdstr++;
419 else break;
420 }
421 sed_cmd->string = bb_xstrdup(cmdstr);
422 parse_escapes(sed_cmd->string,sed_cmd->string,strlen(cmdstr),0,0);
423 cmdstr += strlen(cmdstr);
424 /* handle file cmds: (r)ead */
425 } else if(strchr("rw", sed_cmd->cmd)) {
426 if (sed_cmd->end_line || sed_cmd->end_match)
427 bb_error_msg_and_die("Command only uses one address");
428 cmdstr += parse_file_cmd(sed_cmd, cmdstr, &sed_cmd->string);
429 if(sed_cmd->cmd=='w')
430 sed_cmd->file=bb_xfopen(sed_cmd->string,"w");
431 /* handle branch commands */
432 } else if (strchr(":bt", sed_cmd->cmd)) {
433 int length;
434
435 while(isspace(*cmdstr)) cmdstr++;
436 length = strcspn(cmdstr, semicolon_whitespace);
437 if (length) {
438 sed_cmd->string = strndup(cmdstr, length);
439 cmdstr += length;
440 }
441 }
442 /* translation command */
443 else if (sed_cmd->cmd == 'y') {
444 char *match, *replace;
445 int i=cmdstr[0];
446
447 cmdstr+=parse_regex_delim(cmdstr, &match, &replace)+1;
448 /* \n already parsed, but \delimiter needs unescaping. */
449 parse_escapes(match,match,strlen(match),i,i);
450 parse_escapes(replace,replace,strlen(replace),i,i);
451
452 sed_cmd->string = xcalloc(1, (strlen(match) + 1) * 2);
453 for (i = 0; match[i] && replace[i]; i++) {
454 sed_cmd->string[i * 2] = match[i];
455 sed_cmd->string[(i * 2) + 1] = replace[i];
456 }
457 free(match);
458 free(replace);
459 }
460 /* if it wasnt a single-letter command that takes no arguments
461 * then it must be an invalid command.
462 */
463 else if (strchr("dDgGhHlnNpPqx={}", sed_cmd->cmd) == 0) {
464 bb_error_msg_and_die("Unsupported command %c", sed_cmd->cmd);
465 }
466
467 /* give back whatever's left over */
468 return (cmdstr);
469}
470
471
472/* Parse address+command sets, skipping comment lines. */
473
474void add_cmd(char *cmdstr)
475{
476 static char *add_cmd_line=NULL;
477 sed_cmd_t *sed_cmd;
478 int temp;
479
480 /* Append this line to any unfinished line from last time. */
481 if(add_cmd_line) {
482 int lastlen=strlen(add_cmd_line);
483 char *tmp=xmalloc(lastlen+strlen(cmdstr)+2);
484
485 memcpy(tmp,add_cmd_line,lastlen);
486 tmp[lastlen]='\n';
487 strcpy(tmp+lastlen+1,cmdstr);
488 free(add_cmd_line);
489 cmdstr=add_cmd_line=tmp;
490 } else add_cmd_line=NULL;
491
492 /* If this line ends with backslash, request next line. */
493 temp=strlen(cmdstr);
494 if(temp && cmdstr[temp-1]=='\\') {
495 if(!add_cmd_line) add_cmd_line=strdup(cmdstr);
496 add_cmd_line[temp-1]=0;
497 return;
498 }
499
500 /* Loop parsing all commands in this line. */
501 while(*cmdstr) {
502 /* Skip leading whitespace and semicolons */
503 cmdstr += strspn(cmdstr, semicolon_whitespace);
504
505 /* If no more commands, exit. */
506 if(!*cmdstr) break;
507
508 /* if this is a comment, jump past it and keep going */
509 if (*cmdstr == '#') {
510 /* "#n" is the same as using -n on the command line */
511 if (cmdstr[1] == 'n') be_quiet++;
512 if(!(cmdstr=strpbrk(cmdstr, "\n\r"))) break;
513 continue;
514 }
515
516 /* parse the command
517 * format is: [addr][,addr][!]cmd
518 * |----||-----||-|
519 * part1 part2 part3
520 */
521
522 sed_cmd = xcalloc(1, sizeof(sed_cmd_t));
523
524 /* first part (if present) is an address: either a '$', a number or a /regex/ */
525 cmdstr += get_address(cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match);
526
527 /* second part (if present) will begin with a comma */
528 if (*cmdstr == ',') {
529 int idx;
530
531 cmdstr++;
532 idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match);
533 if (!idx) bb_error_msg_and_die("get_address: no address found in string\n");
534 cmdstr += idx;
535 }
536
537 /* skip whitespace before the command */
538 while (isspace(*cmdstr)) cmdstr++;
539
540 /* Check for inversion flag */
541 if (*cmdstr == '!') {
542 sed_cmd->invert = 1;
543 cmdstr++;
544
545 /* skip whitespace before the command */
546 while (isspace(*cmdstr)) cmdstr++;
547 }
548
549 /* last part (mandatory) will be a command */
550 if (!*cmdstr) bb_error_msg_and_die("missing command");
551 sed_cmd->cmd = *(cmdstr++);
552 cmdstr = parse_cmd_args(sed_cmd, cmdstr);
553
554 /* Add the command to the command array */
555 sed_cmd_tail->next = sed_cmd;
556 sed_cmd_tail = sed_cmd_tail->next;
557 }
558
559 /* If we glued multiple lines together, free the memory. */
560 if(add_cmd_line) {
561 free(add_cmd_line);
562 add_cmd_line=NULL;
563 }
564}
565
566struct pipeline {
567 char *buf; /* Space to hold string */
568 int idx; /* Space used */
569 int len; /* Space allocated */
570} pipeline;
571
572#define PIPE_GROW 64
573
574void pipe_putc(char c)
575{
576 if(pipeline.idx==pipeline.len) {
577 pipeline.buf = xrealloc(pipeline.buf, pipeline.len + PIPE_GROW);
578 pipeline.len+=PIPE_GROW;
579 }
580 pipeline.buf[pipeline.idx++] = (c);
581}
582
583static void do_subst_w_backrefs(const char *line, const char *replace)
584{
585 int i,j;
586
587 /* go through the replacement string */
588 for (i = 0; replace[i]; i++) {
589 /* if we find a backreference (\1, \2, etc.) print the backref'ed * text */
590 if (replace[i] == '\\' && replace[i+1]>'0' && replace[i+1]<='9') {
591 int backref=replace[++i]-'0';
592
593 /* print out the text held in regmatch[backref] */
594 if(regmatch[backref].rm_so != -1)
595 for (j = regmatch[backref].rm_so; j < regmatch[backref].rm_eo; j++)
596 pipe_putc(line[j]);
597 }
598
599 /* if we find a backslash escaped character, print the character */
600 else if (replace[i] == '\\') pipe_putc(replace[++i]);
601
602 /* if we find an unescaped '&' print out the whole matched text. */
603 else if (replace[i] == '&')
604 for (j = regmatch[0].rm_so; j < regmatch[0].rm_eo; j++)
605 pipe_putc(line[j]);
606 /* Otherwise just output the character. */
607 else pipe_putc(replace[i]);
608 }
609}
610
611static int do_subst_command(sed_cmd_t * sed_cmd, char **line)
612{
613 char *oldline = *line;
614 int altered = 0;
615 int match_count=0;
616 regex_t *current_regex;
617
618 /* Handle empty regex. */
619 if (sed_cmd->sub_match == NULL) {
620 current_regex = previous_regex_ptr;
621 if(!current_regex)
622 bb_error_msg_and_die("No previous regexp.");
623 } else previous_regex_ptr = current_regex = sed_cmd->sub_match;
624
625 /* Find the first match */
626 if(REG_NOMATCH==regexec(current_regex, oldline, 10, regmatch, 0))
627 return 0;
628
629 /* Initialize temporary output buffer. */
630 pipeline.buf=xmalloc(PIPE_GROW);
631 pipeline.len=PIPE_GROW;
632 pipeline.idx=0;
633
634 /* Now loop through, substituting for matches */
635 do {
636 int i;
637
638 /* Work around bug in glibc regexec, demonstrated by:
639 echo " a.b" | busybox sed 's [^ .]* x g'
640 The match_count check is so not to break
641 echo "hi" | busybox sed 's/^/!/g' */
642 if(!regmatch[0].rm_so && !regmatch[0].rm_eo && match_count) {
643 pipe_putc(*(oldline++));
644 continue;
645 }
646
647 match_count++;
648
649 /* If we aren't interested in this match, output old line to
650 end of match and continue */
651 if(sed_cmd->which_match && sed_cmd->which_match!=match_count) {
652 for(i=0;i<regmatch[0].rm_eo;i++)
653 pipe_putc(oldline[i]);
654 continue;
655 }
656
657 /* print everything before the match */
658 for (i = 0; i < regmatch[0].rm_so; i++) pipe_putc(oldline[i]);
659
660 /* then print the substitution string */
661 do_subst_w_backrefs(oldline, sed_cmd->string);
662
663 /* advance past the match */
664 oldline += regmatch[0].rm_eo;
665 /* flag that something has changed */
666 altered++;
667
668 /* if we're not doing this globally, get out now */
669 if (sed_cmd->which_match) break;
670 } while (*oldline && (regexec(current_regex, oldline, 10, regmatch, 0) != REG_NOMATCH));
671
672 /* Copy rest of string into output pipeline */
673
674 while(*oldline) pipe_putc(*(oldline++));
675 pipe_putc(0);
676
677 free(*line);
678 *line = pipeline.buf;
679 return altered;
680}
681
682/* Set command pointer to point to this label. (Does not handle null label.) */
683static sed_cmd_t *branch_to(const char *label)
684{
685 sed_cmd_t *sed_cmd;
686
687 for (sed_cmd = sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
688 if ((sed_cmd->cmd == ':') && (sed_cmd->string) && (strcmp(sed_cmd->string, label) == 0)) {
689 return (sed_cmd);
690 }
691 }
692 bb_error_msg_and_die("Can't find label for jump to `%s'", label);
693}
694
695/* Append copy of string to append buffer */
696static void append(char *s)
697{
698 struct append_list *temp=calloc(1,sizeof(struct append_list));
699
700 if(append_head)
701 append_tail=(append_tail->next=temp);
702 else append_head=append_tail=temp;
703 temp->string=strdup(s);
704}
705
706static void flush_append(void)
707{
708 /* Output appended lines. */
709 while(append_head) {
710 fprintf(nonstdout,"%s\n",append_head->string);
711 append_tail=append_head->next;
712 free(append_head->string);
713 free(append_head);
714 append_head=append_tail;
715 }
716 append_head=append_tail=NULL;
717}
718
719/* Get next line of input, flushing append buffer and noting if we hit EOF
720 * without a newline on the last line.
721 */
722static char *get_next_line(FILE * file, int *no_newline)
723{
724 char *temp;
725 int len;
726
727 flush_append();
728 temp=bb_get_line_from_file(file);
729 if(temp) {
730 len=strlen(temp);
731 if(len && temp[len-1]=='\n') temp[len-1]=0;
732 else *no_newline=1;
733 }
734
735 return temp;
736}
737
738/* Output line of text. missing_newline means the last line output did not
739 end with a newline. no_newline means this line does not end with a
740 newline. */
741
742static int puts_maybe_newline(char *s, FILE *file, int missing_newline, int no_newline)
743{
744 if(missing_newline) fputc('\n',file);
745 fputs(s,file);
746 if(!no_newline) fputc('\n',file);
747
748 if(ferror(file)) {
749 fprintf(stderr,"Write failed.\n");
750 exit(4); /* It's what gnu sed exits with... */
751 }
752
753 return no_newline;
754}
755
756#define sed_puts(s,n) missing_newline=puts_maybe_newline(s,nonstdout,missing_newline,n)
757
758static void process_file(FILE *file)
759{
760 char *pattern_space, *next_line, *hold_space=NULL;
761 static int linenum = 0, missing_newline=0;
762 int no_newline,next_no_newline=0;
763
764 next_line = get_next_line(file,&next_no_newline);
765
766 /* go through every line in the file */
767 for(;;) {
768 sed_cmd_t *sed_cmd;
769 int substituted=0;
770
771 /* Advance to next line. Stop if out of lines. */
772 if(!(pattern_space=next_line)) break;
773 no_newline=next_no_newline;
774
775 /* Read one line in advance so we can act on the last line, the '$' address */
776 next_line = get_next_line(file,&next_no_newline);
777 linenum++;
778restart:
779 /* for every line, go through all the commands */
780 for (sed_cmd = sed_cmd_head.next; sed_cmd; sed_cmd = sed_cmd->next) {
781 int old_matched, matched;
782
783 old_matched = sed_cmd->in_match;
784
785 /* Determine if this command matches this line: */
786
787 /* Are we continuing a previous multi-line match? */
788
789 sed_cmd->in_match = sed_cmd->in_match
790
791 /* Or is no range necessary? */
792 || (!sed_cmd->beg_line && !sed_cmd->end_line
793 && !sed_cmd->beg_match && !sed_cmd->end_match)
794
795 /* Or did we match the start of a numerical range? */
796 || (sed_cmd->beg_line > 0 && (sed_cmd->beg_line == linenum))
797
798 /* Or does this line match our begin address regex? */
799 || (sed_cmd->beg_match &&
800 !regexec(sed_cmd->beg_match, pattern_space, 0, NULL, 0))
801
802 /* Or did we match last line of input? */
803 || (sed_cmd->beg_line == -1 && next_line == NULL);
804
805 /* Snapshot the value */
806
807 matched = sed_cmd->in_match;
808
809 /* Is this line the end of the current match? */
810
811 if(matched) {
812 sed_cmd->in_match = !(
813 /* has the ending line come, or is this a single address command? */
814 (sed_cmd->end_line ?
815 sed_cmd->end_line==-1 ?
816 !next_line
817 : sed_cmd->end_line<=linenum
818 : !sed_cmd->end_match)
819 /* or does this line matches our last address regex */
820 || (sed_cmd->end_match && old_matched && (regexec(sed_cmd->end_match, pattern_space, 0, NULL, 0) == 0))
821 );
822 }
823
824 /* Skip blocks of commands we didn't match. */
825 if (sed_cmd->cmd == '{') {
826 if(sed_cmd->invert ? matched : !matched)
827 while(sed_cmd && sed_cmd->cmd!='}') sed_cmd=sed_cmd->next;
828 if(!sed_cmd) bb_error_msg_and_die("Unterminated {");
829 continue;
830 }
831
832 /* Okay, so did this line match? */
833 if (sed_cmd->invert ? !matched : matched) {
834 /* Update last used regex in case a blank substitute BRE is found */
835 if (sed_cmd->beg_match) {
836 previous_regex_ptr = sed_cmd->beg_match;
837 }
838
839 /* actual sedding */
840 switch (sed_cmd->cmd) {
841
842 /* Print line number */
843 case '=':
844 fprintf(nonstdout,"%d\n", linenum);
845 break;
846
847 /* Write the current pattern space up to the first newline */
848 case 'P':
849 {
850 char *tmp = strchr(pattern_space, '\n');
851
852 if (tmp) {
853 *tmp = '\0';
854 sed_puts(pattern_space,1);
855 *tmp = '\n';
856 break;
857 }
858 /* Fall Through */
859 }
860
861 /* Write the current pattern space to output */
862 case 'p':
863 sed_puts(pattern_space,no_newline);
864 break;
865 /* Delete up through first newline */
866 case 'D':
867 {
868 char *tmp = strchr(pattern_space,'\n');
869
870 if(tmp) {
871 tmp=bb_xstrdup(tmp+1);
872 free(pattern_space);
873 pattern_space=tmp;
874 goto restart;
875 }
876 }
877 /* discard this line. */
878 case 'd':
879 goto discard_line;
880
881 /* Substitute with regex */
882 case 's':
883 if(do_subst_command(sed_cmd, &pattern_space)) {
884 substituted|=1;
885
886 /* handle p option */
887 if(sed_cmd->sub_p)
888 sed_puts(pattern_space,no_newline);
889 /* handle w option */
890 if(sed_cmd->file)
891 sed_cmd->no_newline=puts_maybe_newline(pattern_space, sed_cmd->file, sed_cmd->no_newline, no_newline);
892
893 }
894 break;
895
896 /* Append line to linked list to be printed later */
897 case 'a':
898 {
899 append(sed_cmd->string);
900 break;
901 }
902
903 /* Insert text before this line */
904 case 'i':
905 sed_puts(sed_cmd->string,1);
906 break;
907
908 /* Cut and paste text (replace) */
909 case 'c':
910 /* Only triggers on last line of a matching range. */
911 if (!sed_cmd->in_match) sed_puts(sed_cmd->string,1);
912 goto discard_line;
913
914 /* Read file, append contents to output */
915 case 'r':
916 {
917 FILE *outfile;
918
919 outfile = fopen(sed_cmd->string, "r");
920 if (outfile) {
921 char *line;
922
923 while ((line = bb_get_chomped_line_from_file(outfile))
924 != NULL)
925 append(line);
926 bb_xprint_and_close_file(outfile);
927 }
928
929 break;
930 }
931
932 /* Write pattern space to file. */
933 case 'w':
934 sed_cmd->no_newline=puts_maybe_newline(pattern_space,sed_cmd->file, sed_cmd->no_newline,no_newline);
935 break;
936
937 /* Read next line from input */
938 case 'n':
939 if (!be_quiet)
940 sed_puts(pattern_space,no_newline);
941 if (next_line) {
942 free(pattern_space);
943 pattern_space = next_line;
944 no_newline=next_no_newline;
945 next_line = get_next_line(file,&next_no_newline);
946 linenum++;
947 break;
948 }
949 /* fall through */
950
951 /* Quit. End of script, end of input. */
952 case 'q':
953 /* Exit the outer while loop */
954 free(next_line);
955 next_line = NULL;
956 goto discard_commands;
957
958 /* Append the next line to the current line */
959 case 'N':
960 {
961 /* If no next line, jump to end of script and exit. */
962 if (next_line == NULL) {
963 /* Jump to end of script and exit */
964 free(next_line);
965 next_line = NULL;
966 goto discard_line;
967 /* append next_line, read new next_line. */
968 } else {
969 int len=strlen(pattern_space);
970
971 pattern_space = realloc(pattern_space, len + strlen(next_line) + 2);
972 pattern_space[len]='\n';
973 strcpy(pattern_space+len+1, next_line);
974 no_newline=next_no_newline;
975 next_line = get_next_line(file,&next_no_newline);
976 linenum++;
977 }
978 break;
979 }
980
981 /* Test if substition worked, branch if so. */
982 case 't':
983 if (!substituted) break;
984 substituted=0;
985 /* Fall through */
986 /* Branch to label */
987 case 'b':
988 if (!sed_cmd->string) goto discard_commands;
989 else sed_cmd = branch_to(sed_cmd->string);
990 break;
991 /* Transliterate characters */
992 case 'y':
993 {
994 int i;
995
996 for (i = 0; pattern_space[i]; i++) {
997 int j;
998
999 for (j = 0; sed_cmd->string[j]; j += 2) {
1000 if (pattern_space[i] == sed_cmd->string[j]) {
1001 pattern_space[i] = sed_cmd->string[j + 1];
1002 }
1003 }
1004 }
1005
1006 break;
1007 }
1008 case 'g': /* Replace pattern space with hold space */
1009 free(pattern_space);
1010 if (hold_space) {
1011 pattern_space = strdup(hold_space);
1012 no_newline=0;
1013 }
1014 break;
1015 case 'G': /* Append newline and hold space to pattern space */
1016 {
1017 int pattern_space_size = 2;
1018 int hold_space_size = 0;
1019
1020 if (pattern_space)
1021 pattern_space_size += strlen(pattern_space);
1022 if (hold_space) hold_space_size = strlen(hold_space);
1023 pattern_space = xrealloc(pattern_space, pattern_space_size + hold_space_size);
1024 if (pattern_space_size == 2) pattern_space[0]=0;
1025 strcat(pattern_space, "\n");
1026 if (hold_space) strcat(pattern_space, hold_space);
1027 no_newline=0;
1028
1029 break;
1030 }
1031 case 'h': /* Replace hold space with pattern space */
1032 free(hold_space);
1033 hold_space = strdup(pattern_space);
1034 break;
1035 case 'H': /* Append newline and pattern space to hold space */
1036 {
1037 int hold_space_size = 2;
1038 int pattern_space_size = 0;
1039
1040 if (hold_space) hold_space_size += strlen(hold_space);
1041 if (pattern_space)
1042 pattern_space_size = strlen(pattern_space);
1043 hold_space = xrealloc(hold_space,
1044 hold_space_size + pattern_space_size);
1045
1046 if (hold_space_size == 2) hold_space[0]=0;
1047 strcat(hold_space, "\n");
1048 if (pattern_space) strcat(hold_space, pattern_space);
1049
1050 break;
1051 }
1052 case 'x': /* Exchange hold and pattern space */
1053 {
1054 char *tmp = pattern_space;
1055 pattern_space = hold_space;
1056 no_newline=0;
1057 hold_space = tmp;
1058 break;
1059 }
1060 }
1061 }
1062 }
1063
1064 /*
1065 * exit point from sedding...
1066 */
1067discard_commands:
1068 /* we will print the line unless we were told to be quiet ('-n')
1069 or if the line was suppressed (ala 'd'elete) */
1070 if (!be_quiet) sed_puts(pattern_space,no_newline);
1071
1072 /* Delete and such jump here. */
1073discard_line:
1074 flush_append();
1075 free(pattern_space);
1076 }
1077}
1078
1079/* It is possible to have a command line argument with embedded
1080 newlines. This counts as multiple command lines. */
1081
1082static void add_cmd_block(char *cmdstr)
1083{
1084 int go=1;
1085 char *temp=bb_xstrdup(cmdstr),*temp2=temp;
1086
1087 while(go) {
1088 int len=strcspn(temp2,"\n");
1089 if(!temp2[len]) go=0;
1090 else temp2[len]=0;
1091 add_cmd(temp2);
1092 temp2+=len+1;
1093 }
1094 free(temp);
1095}
1096
1097extern int sed_main(int argc, char **argv)
1098{
1099 int status = EXIT_SUCCESS;
1100 int opt;
1101 uint8_t getpat = 1;
1102
1103#ifdef CONFIG_FEATURE_CLEAN_UP
1104 /* destroy command strings on exit */
1105 if (atexit(free_and_close_stuff) == -1)
1106 bb_perror_msg_and_die("atexit");
1107#endif
1108
1109#define LIE_TO_AUTOCONF
1110#ifdef LIE_TO_AUTOCONF
1111 if(argc==2 && !strcmp(argv[1],"--version")) {
1112 printf("This is not GNU sed version 4.0\n");
1113 exit(0);
1114 }
1115#endif
1116
1117 /* do normal option parsing */
1118 while ((opt = getopt(argc, argv, "irne:f:")) > 0) {
1119 switch (opt) {
1120 case 'i':
1121 in_place++;
1122 atexit(cleanup_outname);
1123 break;
1124 case 'r':
1125 regex_type|=REG_EXTENDED;
1126 break;
1127 case 'n':
1128 be_quiet++;
1129 break;
1130 case 'e':
1131 add_cmd_block(optarg);
1132 getpat=0;
1133 break;
1134 case 'f':
1135 {
1136 FILE *cmdfile;
1137 char *line;
1138
1139 cmdfile = bb_xfopen(optarg, "r");
1140
1141 while ((line = bb_get_chomped_line_from_file(cmdfile))
1142 != NULL) {
1143 add_cmd(line);
1144 getpat=0;
1145 free(line);
1146 }
1147 bb_xprint_and_close_file(cmdfile);
1148
1149 break;
1150 }
1151 default:
1152 bb_show_usage();
1153 }
1154 }
1155
1156 /* if we didn't get a pattern from a -e and no command file was specified,
1157 * argv[optind] should be the pattern. no pattern, no worky */
1158 if(getpat) {
1159 if (argv[optind] == NULL)
1160 bb_show_usage();
1161 else
1162 add_cmd_block(argv[optind++]);
1163 }
1164 /* Flush any unfinished commands. */
1165 add_cmd("");
1166
1167 /* By default, we write to stdout */
1168 nonstdout=stdout;
1169
1170 /* argv[(optind)..(argc-1)] should be names of file to process. If no
1171 * files were specified or '-' was specified, take input from stdin.
1172 * Otherwise, we process all the files specified. */
1173 if (argv[optind] == NULL) {
1174 if(in_place) {
1175 fprintf(stderr,"sed: Filename required for -i\n");
1176 exit(1);
1177 }
1178 process_file(stdin);
1179 } else {
1180 int i;
1181 FILE *file;
1182
1183 for (i = optind; i < argc; i++) {
1184 if(!strcmp(argv[i], "-") && !in_place) {
1185 process_file(stdin);
1186 } else {
1187 file = bb_wfopen(argv[i], "r");
1188 if (file) {
1189 if(in_place) {
1190 struct stat statbuf;
1191 outname=bb_xstrndup(argv[i],strlen(argv[i])+6);
1192 strcat(outname,"XXXXXX");
1193 /* Set permissions of output file */
1194 fstat(fileno(file),&statbuf);
1195 mkstemp(outname);
1196 nonstdout=bb_wfopen(outname,"w");
1197 /* Set permissions of output file */
1198 fstat(fileno(file),&statbuf);
1199 fchmod(fileno(nonstdout),statbuf.st_mode);
1200 atexit(cleanup_outname);
1201 }
1202 process_file(file);
1203 fclose(file);
1204 if(in_place) {
1205 fclose(nonstdout);
1206 nonstdout=stdout;
1207 unlink(argv[i]);
1208 rename(outname,argv[i]);
1209 free(outname);
1210 outname=0;
1211 }
1212 } else {
1213 status = EXIT_FAILURE;
1214 }
1215 }
1216 }
1217 }
1218
1219 return status;
1220}
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 @@
1/* vi: set sw=8 ts=8: */
2/*
3 * tiny vi.c: A small 'vi' clone
4 * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21static const char vi_Version[] =
22 "$Id: vi.c,v 1.38 2004/08/19 19:15:06 andersen Exp $";
23
24/*
25 * To compile for standalone use:
26 * gcc -Wall -Os -s -DSTANDALONE -o vi vi.c
27 * or
28 * gcc -Wall -Os -s -DSTANDALONE -DCONFIG_FEATURE_VI_CRASHME -o vi vi.c # include testing features
29 * strip vi
30 */
31
32/*
33 * Things To Do:
34 * EXINIT
35 * $HOME/.exrc and ./.exrc
36 * add magic to search /foo.*bar
37 * add :help command
38 * :map macros
39 * how about mode lines: vi: set sw=8 ts=8:
40 * if mark[] values were line numbers rather than pointers
41 * it would be easier to change the mark when add/delete lines
42 * More intelligence in refresh()
43 * ":r !cmd" and "!cmd" to filter text through an external command
44 * A true "undo" facility
45 * An "ex" line oriented mode- maybe using "cmdedit"
46 */
47
48//---- Feature -------------- Bytes to implement
49#ifdef STANDALONE
50#define vi_main main
51#define CONFIG_FEATURE_VI_COLON // 4288
52#define CONFIG_FEATURE_VI_YANKMARK // 1408
53#define CONFIG_FEATURE_VI_SEARCH // 1088
54#define CONFIG_FEATURE_VI_USE_SIGNALS // 1056
55#define CONFIG_FEATURE_VI_DOT_CMD // 576
56#define CONFIG_FEATURE_VI_READONLY // 128
57#define CONFIG_FEATURE_VI_SETOPTS // 576
58#define CONFIG_FEATURE_VI_SET // 224
59#define CONFIG_FEATURE_VI_WIN_RESIZE // 256 WIN_RESIZE
60// To test editor using CRASHME:
61// vi -C filename
62// To stop testing, wait until all to text[] is deleted, or
63// Ctrl-Z and kill -9 %1
64// while in the editor Ctrl-T will toggle the crashme function on and off.
65//#define CONFIG_FEATURE_VI_CRASHME // randomly pick commands to execute
66#endif /* STANDALONE */
67
68#include <stdio.h>
69#include <stdlib.h>
70#include <string.h>
71#include <termios.h>
72#include <unistd.h>
73#include <sys/ioctl.h>
74#include <sys/time.h>
75#include <sys/types.h>
76#include <sys/stat.h>
77#include <time.h>
78#include <fcntl.h>
79#include <signal.h>
80#include <setjmp.h>
81#include <regex.h>
82#include <ctype.h>
83#include <assert.h>
84#include <errno.h>
85#include <stdarg.h>
86#ifndef STANDALONE
87#include "busybox.h"
88#endif /* STANDALONE */
89
90#ifdef CONFIG_LOCALE_SUPPORT
91#define Isprint(c) isprint((c))
92#else
93#define Isprint(c) ( (c) >= ' ' && (c) != 127 && (c) != ((unsigned char)'\233') )
94#endif
95
96#ifndef TRUE
97#define TRUE ((int)1)
98#define FALSE ((int)0)
99#endif /* TRUE */
100#define MAX_SCR_COLS BUFSIZ
101
102// Misc. non-Ascii keys that report an escape sequence
103#define VI_K_UP 128 // cursor key Up
104#define VI_K_DOWN 129 // cursor key Down
105#define VI_K_RIGHT 130 // Cursor Key Right
106#define VI_K_LEFT 131 // cursor key Left
107#define VI_K_HOME 132 // Cursor Key Home
108#define VI_K_END 133 // Cursor Key End
109#define VI_K_INSERT 134 // Cursor Key Insert
110#define VI_K_PAGEUP 135 // Cursor Key Page Up
111#define VI_K_PAGEDOWN 136 // Cursor Key Page Down
112#define VI_K_FUN1 137 // Function Key F1
113#define VI_K_FUN2 138 // Function Key F2
114#define VI_K_FUN3 139 // Function Key F3
115#define VI_K_FUN4 140 // Function Key F4
116#define VI_K_FUN5 141 // Function Key F5
117#define VI_K_FUN6 142 // Function Key F6
118#define VI_K_FUN7 143 // Function Key F7
119#define VI_K_FUN8 144 // Function Key F8
120#define VI_K_FUN9 145 // Function Key F9
121#define VI_K_FUN10 146 // Function Key F10
122#define VI_K_FUN11 147 // Function Key F11
123#define VI_K_FUN12 148 // Function Key F12
124
125/* vt102 typical ESC sequence */
126/* terminal standout start/normal ESC sequence */
127static const char SOs[] = "\033[7m";
128static const char SOn[] = "\033[0m";
129/* terminal bell sequence */
130static const char bell[] = "\007";
131/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
132static const char Ceol[] = "\033[0K";
133static const char Ceos [] = "\033[0J";
134/* Cursor motion arbitrary destination ESC sequence */
135static const char CMrc[] = "\033[%d;%dH";
136/* Cursor motion up and down ESC sequence */
137static const char CMup[] = "\033[A";
138static const char CMdown[] = "\n";
139
140
141static const int YANKONLY = FALSE;
142static const int YANKDEL = TRUE;
143static const int FORWARD = 1; // code depends on "1" for array index
144static const int BACK = -1; // code depends on "-1" for array index
145static const int LIMITED = 0; // how much of text[] in char_search
146static const int FULL = 1; // how much of text[] in char_search
147
148static const int S_BEFORE_WS = 1; // used in skip_thing() for moving "dot"
149static const int S_TO_WS = 2; // used in skip_thing() for moving "dot"
150static const int S_OVER_WS = 3; // used in skip_thing() for moving "dot"
151static const int S_END_PUNCT = 4; // used in skip_thing() for moving "dot"
152static const int S_END_ALNUM = 5; // used in skip_thing() for moving "dot"
153
154typedef unsigned char Byte;
155
156static int vi_setops;
157#define VI_AUTOINDENT 1
158#define VI_SHOWMATCH 2
159#define VI_IGNORECASE 4
160#define VI_ERR_METHOD 8
161#define autoindent (vi_setops & VI_AUTOINDENT)
162#define showmatch (vi_setops & VI_SHOWMATCH )
163#define ignorecase (vi_setops & VI_IGNORECASE)
164/* indicate error with beep or flash */
165#define err_method (vi_setops & VI_ERR_METHOD)
166
167
168static int editing; // >0 while we are editing a file
169static int cmd_mode; // 0=command 1=insert
170static int file_modified; // buffer contents changed
171static int fn_start; // index of first cmd line file name
172static int save_argc; // how many file names on cmd line
173static int cmdcnt; // repetition count
174static fd_set rfds; // use select() for small sleeps
175static struct timeval tv; // use select() for small sleeps
176static int rows, columns; // the terminal screen is this size
177static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset
178static Byte *status_buffer; // mesages to the user
179static Byte *cfn; // previous, current, and next file name
180static Byte *text, *end, *textend; // pointers to the user data in memory
181static Byte *screen; // pointer to the virtual screen buffer
182static int screensize; // and its size
183static Byte *screenbegin; // index into text[], of top line on the screen
184static Byte *dot; // where all the action takes place
185static int tabstop;
186static struct termios term_orig, term_vi; // remember what the cooked mode was
187static Byte erase_char; // the users erase character
188static Byte last_input_char; // last char read from user
189static Byte last_forward_char; // last char searched for with 'f'
190
191#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
192static int last_row; // where the cursor was last moved to
193#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
194#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
195static jmp_buf restart; // catch_sig()
196#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
197#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
198static int my_pid;
199#endif
200#ifdef CONFIG_FEATURE_VI_DOT_CMD
201static int adding2q; // are we currently adding user input to q
202static Byte *last_modifying_cmd; // last modifying cmd for "."
203static Byte *ioq, *ioq_start; // pointer to string for get_one_char to "read"
204#endif /* CONFIG_FEATURE_VI_DOT_CMD */
205#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
206static Byte *modifying_cmds; // cmds that modify text[]
207#endif /* CONFIG_FEATURE_VI_DOT_CMD || CONFIG_FEATURE_VI_YANKMARK */
208#ifdef CONFIG_FEATURE_VI_READONLY
209static int vi_readonly, readonly;
210#endif /* CONFIG_FEATURE_VI_READONLY */
211#ifdef CONFIG_FEATURE_VI_YANKMARK
212static Byte *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
213static int YDreg, Ureg; // default delete register and orig line for "U"
214static Byte *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
215static Byte *context_start, *context_end;
216#endif /* CONFIG_FEATURE_VI_YANKMARK */
217#ifdef CONFIG_FEATURE_VI_SEARCH
218static Byte *last_search_pattern; // last pattern from a '/' or '?' search
219#endif /* CONFIG_FEATURE_VI_SEARCH */
220
221
222static void edit_file(Byte *); // edit one file
223static void do_cmd(Byte); // execute a command
224static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot
225static Byte *begin_line(Byte *); // return pointer to cur line B-o-l
226static Byte *end_line(Byte *); // return pointer to cur line E-o-l
227static Byte *prev_line(Byte *); // return pointer to prev line B-o-l
228static Byte *next_line(Byte *); // return pointer to next line B-o-l
229static Byte *end_screen(void); // get pointer to last char on screen
230static int count_lines(Byte *, Byte *); // count line from start to stop
231static Byte *find_line(int); // find begining of line #li
232static Byte *move_to_col(Byte *, int); // move "p" to column l
233static int isblnk(Byte); // is the char a blank or tab
234static void dot_left(void); // move dot left- dont leave line
235static void dot_right(void); // move dot right- dont leave line
236static void dot_begin(void); // move dot to B-o-l
237static void dot_end(void); // move dot to E-o-l
238static void dot_next(void); // move dot to next line B-o-l
239static void dot_prev(void); // move dot to prev line B-o-l
240static void dot_scroll(int, int); // move the screen up or down
241static void dot_skip_over_ws(void); // move dot pat WS
242static void dot_delete(void); // delete the char at 'dot'
243static Byte *bound_dot(Byte *); // make sure text[0] <= P < "end"
244static Byte *new_screen(int, int); // malloc virtual screen memory
245static Byte *new_text(int); // malloc memory for text[] buffer
246static Byte *char_insert(Byte *, Byte); // insert the char c at 'p'
247static Byte *stupid_insert(Byte *, Byte); // stupidly insert the char c at 'p'
248static Byte find_range(Byte **, Byte **, Byte); // return pointers for an object
249static int st_test(Byte *, int, int, Byte *); // helper for skip_thing()
250static Byte *skip_thing(Byte *, int, int, int); // skip some object
251static Byte *find_pair(Byte *, Byte); // find matching pair () [] {}
252static Byte *text_hole_delete(Byte *, Byte *); // at "p", delete a 'size' byte hole
253static Byte *text_hole_make(Byte *, int); // at "p", make a 'size' byte hole
254static Byte *yank_delete(Byte *, Byte *, int, int); // yank text[] into register then delete
255static void show_help(void); // display some help info
256static void rawmode(void); // set "raw" mode on tty
257static void cookmode(void); // return to "cooked" mode on tty
258static int mysleep(int); // sleep for 'h' 1/100 seconds
259static Byte readit(void); // read (maybe cursor) key from stdin
260static Byte get_one_char(void); // read 1 char from stdin
261static int file_size(const Byte *); // what is the byte size of "fn"
262static int file_insert(Byte *, Byte *, int);
263static int file_write(Byte *, Byte *, Byte *);
264static void place_cursor(int, int, int);
265static void screen_erase(void);
266static void clear_to_eol(void);
267static void clear_to_eos(void);
268static void standout_start(void); // send "start reverse video" sequence
269static void standout_end(void); // send "end reverse video" sequence
270static void flash(int); // flash the terminal screen
271static void show_status_line(void); // put a message on the bottom line
272static void psb(const char *, ...); // Print Status Buf
273static void psbs(const char *, ...); // Print Status Buf in standout mode
274static void ni(Byte *); // display messages
275static void edit_status(void); // show file status on status line
276static void redraw(int); // force a full screen refresh
277static void format_line(Byte*, Byte*, int);
278static void refresh(int); // update the terminal from screen[]
279
280static void Indicate_Error(void); // use flash or beep to indicate error
281#define indicate_error(c) Indicate_Error()
282
283#ifdef CONFIG_FEATURE_VI_SEARCH
284static Byte *char_search(Byte *, Byte *, int, int); // search for pattern starting at p
285static int mycmp(Byte *, Byte *, int); // string cmp based in "ignorecase"
286#endif /* CONFIG_FEATURE_VI_SEARCH */
287#ifdef CONFIG_FEATURE_VI_COLON
288static void Hit_Return(void);
289static Byte *get_one_address(Byte *, int *); // get colon addr, if present
290static Byte *get_address(Byte *, int *, int *); // get two colon addrs, if present
291static void colon(Byte *); // execute the "colon" mode cmds
292#endif /* CONFIG_FEATURE_VI_COLON */
293#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
294static void winch_sig(int); // catch window size changes
295static void suspend_sig(int); // catch ctrl-Z
296static void catch_sig(int); // catch ctrl-C and alarm time-outs
297static void core_sig(int); // catch a core dump signal
298#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
299#ifdef CONFIG_FEATURE_VI_DOT_CMD
300static void start_new_cmd_q(Byte); // new queue for command
301static void end_cmd_q(void); // stop saving input chars
302#else /* CONFIG_FEATURE_VI_DOT_CMD */
303#define end_cmd_q()
304#endif /* CONFIG_FEATURE_VI_DOT_CMD */
305#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
306static void window_size_get(int); // find out what size the window is
307#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
308#ifdef CONFIG_FEATURE_VI_SETOPTS
309static void showmatching(Byte *); // show the matching pair () [] {}
310#endif /* CONFIG_FEATURE_VI_SETOPTS */
311#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
312static Byte *string_insert(Byte *, Byte *); // insert the string at 'p'
313#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
314#ifdef CONFIG_FEATURE_VI_YANKMARK
315static Byte *text_yank(Byte *, Byte *, int); // save copy of "p" into a register
316static Byte what_reg(void); // what is letter of current YDreg
317static void check_context(Byte); // remember context for '' command
318#endif /* CONFIG_FEATURE_VI_YANKMARK */
319#ifdef CONFIG_FEATURE_VI_CRASHME
320static void crash_dummy();
321static void crash_test();
322static int crashme = 0;
323#endif /* CONFIG_FEATURE_VI_CRASHME */
324
325
326static void write1(const char *out)
327{
328 fputs(out, stdout);
329}
330
331extern int vi_main(int argc, char **argv)
332{
333 int c;
334 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, 200);
335
336#ifdef CONFIG_FEATURE_VI_YANKMARK
337 int i;
338#endif /* CONFIG_FEATURE_VI_YANKMARK */
339#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
340 my_pid = getpid();
341#endif
342#ifdef CONFIG_FEATURE_VI_CRASHME
343 (void) srand((long) my_pid);
344#endif /* CONFIG_FEATURE_VI_CRASHME */
345
346 status_buffer = STATUS_BUFFER;
347
348#ifdef CONFIG_FEATURE_VI_READONLY
349 vi_readonly = readonly = FALSE;
350 if (strncmp(argv[0], "view", 4) == 0) {
351 readonly = TRUE;
352 vi_readonly = TRUE;
353 }
354#endif /* CONFIG_FEATURE_VI_READONLY */
355 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
356#ifdef CONFIG_FEATURE_VI_YANKMARK
357 for (i = 0; i < 28; i++) {
358 reg[i] = 0;
359 } // init the yank regs
360#endif /* CONFIG_FEATURE_VI_YANKMARK */
361#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
362 modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
363#endif /* CONFIG_FEATURE_VI_DOT_CMD */
364
365 // 1- process $HOME/.exrc file
366 // 2- process EXINIT variable from environment
367 // 3- process command line args
368 while ((c = getopt(argc, argv, "hCR")) != -1) {
369 switch (c) {
370#ifdef CONFIG_FEATURE_VI_CRASHME
371 case 'C':
372 crashme = 1;
373 break;
374#endif /* CONFIG_FEATURE_VI_CRASHME */
375#ifdef CONFIG_FEATURE_VI_READONLY
376 case 'R': // Read-only flag
377 readonly = TRUE;
378 vi_readonly = TRUE;
379 break;
380#endif /* CONFIG_FEATURE_VI_READONLY */
381 //case 'r': // recover flag- ignore- we don't use tmp file
382 //case 'x': // encryption flag- ignore
383 //case 'c': // execute command first
384 //case 'h': // help -- just use default
385 default:
386 show_help();
387 return 1;
388 }
389 }
390
391 // The argv array can be used by the ":next" and ":rewind" commands
392 // save optind.
393 fn_start = optind; // remember first file name for :next and :rew
394 save_argc = argc;
395
396 //----- This is the main file handling loop --------------
397 if (optind >= argc) {
398 editing = 1; // 0= exit, 1= one file, 2= multiple files
399 edit_file(0);
400 } else {
401 for (; optind < argc; optind++) {
402 editing = 1; // 0=exit, 1=one file, 2+ =many files
403 free(cfn);
404 cfn = (Byte *) bb_xstrdup(argv[optind]);
405 edit_file(cfn);
406 }
407 }
408 //-----------------------------------------------------------
409
410 return (0);
411}
412
413#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
414//----- See what the window size currently is --------------------
415static inline void window_size_get(int fd)
416{
417 get_terminal_width_height(fd, &columns, &rows);
418}
419#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
420
421static void edit_file(Byte * fn)
422{
423 Byte c;
424 int cnt, size, ch;
425
426#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
427 int sig;
428#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
429#ifdef CONFIG_FEATURE_VI_YANKMARK
430 static Byte *cur_line;
431#endif /* CONFIG_FEATURE_VI_YANKMARK */
432
433 rawmode();
434 rows = 24;
435 columns = 80;
436 ch= -1;
437#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
438 window_size_get(0);
439#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
440 new_screen(rows, columns); // get memory for virtual screen
441
442 cnt = file_size(fn); // file size
443 size = 2 * cnt; // 200% of file size
444 new_text(size); // get a text[] buffer
445 screenbegin = dot = end = text;
446 if (fn != 0) {
447 ch= file_insert(fn, text, cnt);
448 }
449 if (ch < 1) {
450 (void) char_insert(text, '\n'); // start empty buf with dummy line
451 }
452 file_modified = FALSE;
453#ifdef CONFIG_FEATURE_VI_YANKMARK
454 YDreg = 26; // default Yank/Delete reg
455 Ureg = 27; // hold orig line for "U" cmd
456 for (cnt = 0; cnt < 28; cnt++) {
457 mark[cnt] = 0;
458 } // init the marks
459 mark[26] = mark[27] = text; // init "previous context"
460#endif /* CONFIG_FEATURE_VI_YANKMARK */
461
462 last_forward_char = last_input_char = '\0';
463 crow = 0;
464 ccol = 0;
465 edit_status();
466
467#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
468 catch_sig(0);
469 core_sig(0);
470 signal(SIGWINCH, winch_sig);
471 signal(SIGTSTP, suspend_sig);
472 sig = setjmp(restart);
473 if (sig != 0) {
474 const char *msg = "";
475
476 if (sig == SIGWINCH)
477 msg = "(window resize)";
478 if (sig == SIGHUP)
479 msg = "(hangup)";
480 if (sig == SIGINT)
481 msg = "(interrupt)";
482 if (sig == SIGTERM)
483 msg = "(terminate)";
484 if (sig == SIGBUS)
485 msg = "(bus error)";
486 if (sig == SIGSEGV)
487 msg = "(I tried to touch invalid memory)";
488 if (sig == SIGALRM)
489 msg = "(alarm)";
490
491 psbs("-- caught signal %d %s--", sig, msg);
492 screenbegin = dot = text;
493 }
494#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
495
496 editing = 1;
497 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
498 cmdcnt = 0;
499 tabstop = 8;
500 offset = 0; // no horizontal offset
501 c = '\0';
502#ifdef CONFIG_FEATURE_VI_DOT_CMD
503 free(last_modifying_cmd);
504 free(ioq_start);
505 ioq = ioq_start = last_modifying_cmd = 0;
506 adding2q = 0;
507#endif /* CONFIG_FEATURE_VI_DOT_CMD */
508 redraw(FALSE); // dont force every col re-draw
509 show_status_line();
510
511 //------This is the main Vi cmd handling loop -----------------------
512 while (editing > 0) {
513#ifdef CONFIG_FEATURE_VI_CRASHME
514 if (crashme > 0) {
515 if ((end - text) > 1) {
516 crash_dummy(); // generate a random command
517 } else {
518 crashme = 0;
519 dot =
520 string_insert(text, (Byte *) "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
521 refresh(FALSE);
522 }
523 }
524#endif /* CONFIG_FEATURE_VI_CRASHME */
525 last_input_char = c = get_one_char(); // get a cmd from user
526#ifdef CONFIG_FEATURE_VI_YANKMARK
527 // save a copy of the current line- for the 'U" command
528 if (begin_line(dot) != cur_line) {
529 cur_line = begin_line(dot);
530 text_yank(begin_line(dot), end_line(dot), Ureg);
531 }
532#endif /* CONFIG_FEATURE_VI_YANKMARK */
533#ifdef CONFIG_FEATURE_VI_DOT_CMD
534 // These are commands that change text[].
535 // Remember the input for the "." command
536 if (!adding2q && ioq_start == 0
537 && strchr((char *) modifying_cmds, c) != NULL) {
538 start_new_cmd_q(c);
539 }
540#endif /* CONFIG_FEATURE_VI_DOT_CMD */
541 do_cmd(c); // execute the user command
542 //
543 // poll to see if there is input already waiting. if we are
544 // not able to display output fast enough to keep up, skip
545 // the display update until we catch up with input.
546 if (mysleep(0) == 0) {
547 // no input pending- so update output
548 refresh(FALSE);
549 show_status_line();
550 }
551#ifdef CONFIG_FEATURE_VI_CRASHME
552 if (crashme > 0)
553 crash_test(); // test editor variables
554#endif /* CONFIG_FEATURE_VI_CRASHME */
555 }
556 //-------------------------------------------------------------------
557
558 place_cursor(rows, 0, FALSE); // go to bottom of screen
559 clear_to_eol(); // Erase to end of line
560 cookmode();
561}
562
563//----- The Colon commands -------------------------------------
564#ifdef CONFIG_FEATURE_VI_COLON
565static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present
566{
567 int st;
568 Byte *q;
569
570#ifdef CONFIG_FEATURE_VI_YANKMARK
571 Byte c;
572#endif /* CONFIG_FEATURE_VI_YANKMARK */
573#ifdef CONFIG_FEATURE_VI_SEARCH
574 Byte *pat, buf[BUFSIZ];
575#endif /* CONFIG_FEATURE_VI_SEARCH */
576
577 *addr = -1; // assume no addr
578 if (*p == '.') { // the current line
579 p++;
580 q = begin_line(dot);
581 *addr = count_lines(text, q);
582#ifdef CONFIG_FEATURE_VI_YANKMARK
583 } else if (*p == '\'') { // is this a mark addr
584 p++;
585 c = tolower(*p);
586 p++;
587 if (c >= 'a' && c <= 'z') {
588 // we have a mark
589 c = c - 'a';
590 q = mark[(int) c];
591 if (q != NULL) { // is mark valid
592 *addr = count_lines(text, q); // count lines
593 }
594 }
595#endif /* CONFIG_FEATURE_VI_YANKMARK */
596#ifdef CONFIG_FEATURE_VI_SEARCH
597 } else if (*p == '/') { // a search pattern
598 q = buf;
599 for (p++; *p; p++) {
600 if (*p == '/')
601 break;
602 *q++ = *p;
603 *q = '\0';
604 }
605 pat = (Byte *) bb_xstrdup((char *) buf); // save copy of pattern
606 if (*p == '/')
607 p++;
608 q = char_search(dot, pat, FORWARD, FULL);
609 if (q != NULL) {
610 *addr = count_lines(text, q);
611 }
612 free(pat);
613#endif /* CONFIG_FEATURE_VI_SEARCH */
614 } else if (*p == '$') { // the last line in file
615 p++;
616 q = begin_line(end - 1);
617 *addr = count_lines(text, q);
618 } else if (isdigit(*p)) { // specific line number
619 sscanf((char *) p, "%d%n", addr, &st);
620 p += st;
621 } else { // I don't reconise this
622 // unrecognised address- assume -1
623 *addr = -1;
624 }
625 return (p);
626}
627
628static Byte *get_address(Byte *p, int *b, int *e) // get two colon addrs, if present
629{
630 //----- get the address' i.e., 1,3 'a,'b -----
631 // get FIRST addr, if present
632 while (isblnk(*p))
633 p++; // skip over leading spaces
634 if (*p == '%') { // alias for 1,$
635 p++;
636 *b = 1;
637 *e = count_lines(text, end-1);
638 goto ga0;
639 }
640 p = get_one_address(p, b);
641 while (isblnk(*p))
642 p++;
643 if (*p == ',') { // is there a address separator
644 p++;
645 while (isblnk(*p))
646 p++;
647 // get SECOND addr, if present
648 p = get_one_address(p, e);
649 }
650ga0:
651 while (isblnk(*p))
652 p++; // skip over trailing spaces
653 return (p);
654}
655
656#ifdef CONFIG_FEATURE_VI_SETOPTS
657static void setops(const Byte *args, const char *opname, int flg_no,
658 const char *short_opname, int opt)
659{
660 const char *a = (char *) args + flg_no;
661 int l = strlen(opname) - 1; /* opname have + ' ' */
662
663 if (strncasecmp(a, opname, l) == 0 ||
664 strncasecmp(a, short_opname, 2) == 0) {
665 if(flg_no)
666 vi_setops &= ~opt;
667 else
668 vi_setops |= opt;
669 }
670}
671#endif
672
673static void colon(Byte * buf)
674{
675 Byte c, *orig_buf, *buf1, *q, *r;
676 Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
677 int i, l, li, ch, st, b, e;
678 int useforce = FALSE, forced = FALSE;
679 struct stat st_buf;
680
681 // :3154 // if (-e line 3154) goto it else stay put
682 // :4,33w! foo // write a portion of buffer to file "foo"
683 // :w // write all of buffer to current file
684 // :q // quit
685 // :q! // quit- dont care about modified file
686 // :'a,'z!sort -u // filter block through sort
687 // :'f // goto mark "f"
688 // :'fl // list literal the mark "f" line
689 // :.r bar // read file "bar" into buffer before dot
690 // :/123/,/abc/d // delete lines from "123" line to "abc" line
691 // :/xyz/ // goto the "xyz" line
692 // :s/find/replace/ // substitute pattern "find" with "replace"
693 // :!<cmd> // run <cmd> then return
694 //
695
696 if (strlen((char *) buf) <= 0)
697 goto vc1;
698 if (*buf == ':')
699 buf++; // move past the ':'
700
701 li = st = ch = i = 0;
702 b = e = -1;
703 q = text; // assume 1,$ for the range
704 r = end - 1;
705 li = count_lines(text, end - 1);
706 fn = cfn; // default to current file
707 memset(cmd, '\0', BUFSIZ); // clear cmd[]
708 memset(args, '\0', BUFSIZ); // clear args[]
709
710 // look for optional address(es) :. :1 :1,9 :'q,'a :%
711 buf = get_address(buf, &b, &e);
712
713 // remember orig command line
714 orig_buf = buf;
715
716 // get the COMMAND into cmd[]
717 buf1 = cmd;
718 while (*buf != '\0') {
719 if (isspace(*buf))
720 break;
721 *buf1++ = *buf++;
722 }
723 // get any ARGuments
724 while (isblnk(*buf))
725 buf++;
726 strcpy((char *) args, (char *) buf);
727 buf1 = last_char_is((char *)cmd, '!');
728 if (buf1) {
729 useforce = TRUE;
730 *buf1 = '\0'; // get rid of !
731 }
732 if (b >= 0) {
733 // if there is only one addr, then the addr
734 // is the line number of the single line the
735 // user wants. So, reset the end
736 // pointer to point at end of the "b" line
737 q = find_line(b); // what line is #b
738 r = end_line(q);
739 li = 1;
740 }
741 if (e >= 0) {
742 // we were given two addrs. change the
743 // end pointer to the addr given by user.
744 r = find_line(e); // what line is #e
745 r = end_line(r);
746 li = e - b + 1;
747 }
748 // ------------ now look for the command ------------
749 i = strlen((char *) cmd);
750 if (i == 0) { // :123CR goto line #123
751 if (b >= 0) {
752 dot = find_line(b); // what line is #b
753 dot_skip_over_ws();
754 }
755 } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd
756 // :!ls run the <cmd>
757 (void) alarm(0); // wait for input- no alarms
758 place_cursor(rows - 1, 0, FALSE); // go to Status line
759 clear_to_eol(); // clear the line
760 cookmode();
761 system(orig_buf+1); // run the cmd
762 rawmode();
763 Hit_Return(); // let user see results
764 (void) alarm(3); // done waiting for input
765 } else if (strncmp((char *) cmd, "=", i) == 0) { // where is the address
766 if (b < 0) { // no addr given- use defaults
767 b = e = count_lines(text, dot);
768 }
769 psb("%d", b);
770 } else if (strncasecmp((char *) cmd, "delete", i) == 0) { // delete lines
771 if (b < 0) { // no addr given- use defaults
772 q = begin_line(dot); // assume .,. for the range
773 r = end_line(dot);
774 }
775 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
776 dot_skip_over_ws();
777 } else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file
778 int sr;
779 sr= 0;
780 // don't edit, if the current file has been modified
781 if (file_modified && ! useforce) {
782 psbs("No write since last change (:edit! overrides)");
783 goto vc1;
784 }
785 if (strlen(args) > 0) {
786 // the user supplied a file name
787 fn= args;
788 } else if (cfn != 0 && strlen(cfn) > 0) {
789 // no user supplied name- use the current filename
790 fn= cfn;
791 goto vc5;
792 } else {
793 // no user file name, no current name- punt
794 psbs("No current filename");
795 goto vc1;
796 }
797
798 // see if file exists- if not, its just a new file request
799 if ((sr=stat((char*)fn, &st_buf)) < 0) {
800 // This is just a request for a new file creation.
801 // The file_insert below will fail but we get
802 // an empty buffer with a file name. Then the "write"
803 // command can do the create.
804 } else {
805 if ((st_buf.st_mode & (S_IFREG)) == 0) {
806 // This is not a regular file
807 psbs("\"%s\" is not a regular file", fn);
808 goto vc1;
809 }
810 if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
811 // dont have any read permissions
812 psbs("\"%s\" is not readable", fn);
813 goto vc1;
814 }
815 }
816
817 // There is a read-able regular file
818 // make this the current file
819 q = (Byte *) bb_xstrdup((char *) fn); // save the cfn
820 free(cfn); // free the old name
821 cfn = q; // remember new cfn
822
823 vc5:
824 // delete all the contents of text[]
825 new_text(2 * file_size(fn));
826 screenbegin = dot = end = text;
827
828 // insert new file
829 ch = file_insert(fn, text, file_size(fn));
830
831 if (ch < 1) {
832 // start empty buf with dummy line
833 (void) char_insert(text, '\n');
834 ch= 1;
835 }
836 file_modified = FALSE;
837#ifdef CONFIG_FEATURE_VI_YANKMARK
838 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
839 free(reg[Ureg]); // free orig line reg- for 'U'
840 reg[Ureg]= 0;
841 }
842 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
843 free(reg[YDreg]); // free default yank/delete register
844 reg[YDreg]= 0;
845 }
846 for (li = 0; li < 28; li++) {
847 mark[li] = 0;
848 } // init the marks
849#endif /* CONFIG_FEATURE_VI_YANKMARK */
850 // how many lines in text[]?
851 li = count_lines(text, end - 1);
852 psb("\"%s\"%s"
853#ifdef CONFIG_FEATURE_VI_READONLY
854 "%s"
855#endif /* CONFIG_FEATURE_VI_READONLY */
856 " %dL, %dC", cfn,
857 (sr < 0 ? " [New file]" : ""),
858#ifdef CONFIG_FEATURE_VI_READONLY
859 ((vi_readonly || readonly) ? " [Read only]" : ""),
860#endif /* CONFIG_FEATURE_VI_READONLY */
861 li, ch);
862 } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this
863 if (b != -1 || e != -1) {
864 ni((Byte *) "No address allowed on this command");
865 goto vc1;
866 }
867 if (strlen((char *) args) > 0) {
868 // user wants a new filename
869 free(cfn);
870 cfn = (Byte *) bb_xstrdup((char *) args);
871 } else {
872 // user wants file status info
873 edit_status();
874 }
875 } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available
876 // print out values of all features
877 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
878 clear_to_eol(); // clear the line
879 cookmode();
880 show_help();
881 rawmode();
882 Hit_Return();
883 } else if (strncasecmp((char *) cmd, "list", i) == 0) { // literal print line
884 if (b < 0) { // no addr given- use defaults
885 q = begin_line(dot); // assume .,. for the range
886 r = end_line(dot);
887 }
888 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
889 clear_to_eol(); // clear the line
890 puts("\r");
891 for (; q <= r; q++) {
892 int c_is_no_print;
893
894 c = *q;
895 c_is_no_print = c > 127 && !Isprint(c);
896 if (c_is_no_print) {
897 c = '.';
898 standout_start();
899 }
900 if (c == '\n') {
901 write1("$\r");
902 } else if (c < ' ' || c == 127) {
903 putchar('^');
904 if(c == 127)
905 c = '?';
906 else
907 c += '@';
908 }
909 putchar(c);
910 if (c_is_no_print)
911 standout_end();
912 }
913#ifdef CONFIG_FEATURE_VI_SET
914 vc2:
915#endif /* CONFIG_FEATURE_VI_SET */
916 Hit_Return();
917 } else if ((strncasecmp((char *) cmd, "quit", i) == 0) || // Quit
918 (strncasecmp((char *) cmd, "next", i) == 0)) { // edit next file
919 if (useforce) {
920 // force end of argv list
921 if (*cmd == 'q') {
922 optind = save_argc;
923 }
924 editing = 0;
925 goto vc1;
926 }
927 // don't exit if the file been modified
928 if (file_modified) {
929 psbs("No write since last change (:%s! overrides)",
930 (*cmd == 'q' ? "quit" : "next"));
931 goto vc1;
932 }
933 // are there other file to edit
934 if (*cmd == 'q' && optind < save_argc - 1) {
935 psbs("%d more file to edit", (save_argc - optind - 1));
936 goto vc1;
937 }
938 if (*cmd == 'n' && optind >= save_argc - 1) {
939 psbs("No more files to edit");
940 goto vc1;
941 }
942 editing = 0;
943 } else if (strncasecmp((char *) cmd, "read", i) == 0) { // read file into text[]
944 fn = args;
945 if (strlen((char *) fn) <= 0) {
946 psbs("No filename given");
947 goto vc1;
948 }
949 if (b < 0) { // no addr given- use defaults
950 q = begin_line(dot); // assume "dot"
951 }
952 // read after current line- unless user said ":0r foo"
953 if (b != 0)
954 q = next_line(q);
955#ifdef CONFIG_FEATURE_VI_READONLY
956 l= readonly; // remember current files' status
957#endif
958 ch = file_insert(fn, q, file_size(fn));
959#ifdef CONFIG_FEATURE_VI_READONLY
960 readonly= l;
961#endif
962 if (ch < 0)
963 goto vc1; // nothing was inserted
964 // how many lines in text[]?
965 li = count_lines(q, q + ch - 1);
966 psb("\"%s\""
967#ifdef CONFIG_FEATURE_VI_READONLY
968 "%s"
969#endif /* CONFIG_FEATURE_VI_READONLY */
970 " %dL, %dC", fn,
971#ifdef CONFIG_FEATURE_VI_READONLY
972 ((vi_readonly || readonly) ? " [Read only]" : ""),
973#endif /* CONFIG_FEATURE_VI_READONLY */
974 li, ch);
975 if (ch > 0) {
976 // if the insert is before "dot" then we need to update
977 if (q <= dot)
978 dot += ch;
979 file_modified = TRUE;
980 }
981 } else if (strncasecmp((char *) cmd, "rewind", i) == 0) { // rewind cmd line args
982 if (file_modified && ! useforce) {
983 psbs("No write since last change (:rewind! overrides)");
984 } else {
985 // reset the filenames to edit
986 optind = fn_start - 1;
987 editing = 0;
988 }
989#ifdef CONFIG_FEATURE_VI_SET
990 } else if (strncasecmp((char *) cmd, "set", i) == 0) { // set or clear features
991 i = 0; // offset into args
992 if (strlen((char *) args) == 0) {
993 // print out values of all options
994 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
995 clear_to_eol(); // clear the line
996 printf("----------------------------------------\r\n");
997#ifdef CONFIG_FEATURE_VI_SETOPTS
998 if (!autoindent)
999 printf("no");
1000 printf("autoindent ");
1001 if (!err_method)
1002 printf("no");
1003 printf("flash ");
1004 if (!ignorecase)
1005 printf("no");
1006 printf("ignorecase ");
1007 if (!showmatch)
1008 printf("no");
1009 printf("showmatch ");
1010 printf("tabstop=%d ", tabstop);
1011#endif /* CONFIG_FEATURE_VI_SETOPTS */
1012 printf("\r\n");
1013 goto vc2;
1014 }
1015 if (strncasecmp((char *) args, "no", 2) == 0)
1016 i = 2; // ":set noautoindent"
1017#ifdef CONFIG_FEATURE_VI_SETOPTS
1018 setops(args, "autoindent ", i, "ai", VI_AUTOINDENT);
1019 setops(args, "flash ", i, "fl", VI_ERR_METHOD);
1020 setops(args, "ignorecase ", i, "ic", VI_IGNORECASE);
1021 setops(args, "showmatch ", i, "ic", VI_SHOWMATCH);
1022 if (strncasecmp((char *) args + i, "tabstop=%d ", 7) == 0) {
1023 sscanf(strchr((char *) args + i, '='), "=%d", &ch);
1024 if (ch > 0 && ch < columns - 1)
1025 tabstop = ch;
1026 }
1027#endif /* CONFIG_FEATURE_VI_SETOPTS */
1028#endif /* CONFIG_FEATURE_VI_SET */
1029#ifdef CONFIG_FEATURE_VI_SEARCH
1030 } else if (strncasecmp((char *) cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
1031 Byte *ls, *F, *R;
1032 int gflag;
1033
1034 // F points to the "find" pattern
1035 // R points to the "replace" pattern
1036 // replace the cmd line delimiters "/" with NULLs
1037 gflag = 0; // global replace flag
1038 c = orig_buf[1]; // what is the delimiter
1039 F = orig_buf + 2; // start of "find"
1040 R = (Byte *) strchr((char *) F, c); // middle delimiter
1041 if (!R) goto colon_s_fail;
1042 *R++ = '\0'; // terminate "find"
1043 buf1 = (Byte *) strchr((char *) R, c);
1044 if (!buf1) goto colon_s_fail;
1045 *buf1++ = '\0'; // terminate "replace"
1046 if (*buf1 == 'g') { // :s/foo/bar/g
1047 buf1++;
1048 gflag++; // turn on gflag
1049 }
1050 q = begin_line(q);
1051 if (b < 0) { // maybe :s/foo/bar/
1052 q = begin_line(dot); // start with cur line
1053 b = count_lines(text, q); // cur line number
1054 }
1055 if (e < 0)
1056 e = b; // maybe :.s/foo/bar/
1057 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
1058 ls = q; // orig line start
1059 vc4:
1060 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
1061 if (buf1 != NULL) {
1062 // we found the "find" pattern- delete it
1063 (void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
1064 // inset the "replace" patern
1065 (void) string_insert(buf1, R); // insert the string
1066 // check for "global" :s/foo/bar/g
1067 if (gflag == 1) {
1068 if ((buf1 + strlen((char *) R)) < end_line(ls)) {
1069 q = buf1 + strlen((char *) R);
1070 goto vc4; // don't let q move past cur line
1071 }
1072 }
1073 }
1074 q = next_line(ls);
1075 }
1076#endif /* CONFIG_FEATURE_VI_SEARCH */
1077 } else if (strncasecmp((char *) cmd, "version", i) == 0) { // show software version
1078 psb("%s", vi_Version);
1079 } else if ((strncasecmp((char *) cmd, "write", i) == 0) || // write text to file
1080 (strncasecmp((char *) cmd, "wq", i) == 0) ||
1081 (strncasecmp((char *) cmd, "x", i) == 0)) {
1082 // is there a file name to write to?
1083 if (strlen((char *) args) > 0) {
1084 fn = args;
1085 }
1086#ifdef CONFIG_FEATURE_VI_READONLY
1087 if ((vi_readonly || readonly) && ! useforce) {
1088 psbs("\"%s\" File is read only", fn);
1089 goto vc3;
1090 }
1091#endif /* CONFIG_FEATURE_VI_READONLY */
1092 // how many lines in text[]?
1093 li = count_lines(q, r);
1094 ch = r - q + 1;
1095 // see if file exists- if not, its just a new file request
1096 if (useforce) {
1097 // if "fn" is not write-able, chmod u+w
1098 // sprintf(syscmd, "chmod u+w %s", fn);
1099 // system(syscmd);
1100 forced = TRUE;
1101 }
1102 l = file_write(fn, q, r);
1103 if (useforce && forced) {
1104 // chmod u-w
1105 // sprintf(syscmd, "chmod u-w %s", fn);
1106 // system(syscmd);
1107 forced = FALSE;
1108 }
1109 psb("\"%s\" %dL, %dC", fn, li, l);
1110 if (q == text && r == end - 1 && l == ch)
1111 file_modified = FALSE;
1112 if ((cmd[0] == 'x' || cmd[1] == 'q') && l == ch) {
1113 editing = 0;
1114 }
1115#ifdef CONFIG_FEATURE_VI_READONLY
1116 vc3:;
1117#endif /* CONFIG_FEATURE_VI_READONLY */
1118#ifdef CONFIG_FEATURE_VI_YANKMARK
1119 } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines
1120 if (b < 0) { // no addr given- use defaults
1121 q = begin_line(dot); // assume .,. for the range
1122 r = end_line(dot);
1123 }
1124 text_yank(q, r, YDreg);
1125 li = count_lines(q, r);
1126 psb("Yank %d lines (%d chars) into [%c]",
1127 li, strlen((char *) reg[YDreg]), what_reg());
1128#endif /* CONFIG_FEATURE_VI_YANKMARK */
1129 } else {
1130 // cmd unknown
1131 ni((Byte *) cmd);
1132 }
1133 vc1:
1134 dot = bound_dot(dot); // make sure "dot" is valid
1135 return;
1136#ifdef CONFIG_FEATURE_VI_SEARCH
1137colon_s_fail:
1138 psb(":s expression missing delimiters");
1139#endif
1140}
1141
1142static void Hit_Return(void)
1143{
1144 char c;
1145
1146 standout_start(); // start reverse video
1147 write1("[Hit return to continue]");
1148 standout_end(); // end reverse video
1149 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1150 ;
1151 redraw(TRUE); // force redraw all
1152}
1153#endif /* CONFIG_FEATURE_VI_COLON */
1154
1155//----- Synchronize the cursor to Dot --------------------------
1156static void sync_cursor(Byte * d, int *row, int *col)
1157{
1158 Byte *beg_cur, *end_cur; // begin and end of "d" line
1159 Byte *beg_scr, *end_scr; // begin and end of screen
1160 Byte *tp;
1161 int cnt, ro, co;
1162
1163 beg_cur = begin_line(d); // first char of cur line
1164 end_cur = end_line(d); // last char of cur line
1165
1166 beg_scr = end_scr = screenbegin; // first char of screen
1167 end_scr = end_screen(); // last char of screen
1168
1169 if (beg_cur < screenbegin) {
1170 // "d" is before top line on screen
1171 // how many lines do we have to move
1172 cnt = count_lines(beg_cur, screenbegin);
1173 sc1:
1174 screenbegin = beg_cur;
1175 if (cnt > (rows - 1) / 2) {
1176 // we moved too many lines. put "dot" in middle of screen
1177 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1178 screenbegin = prev_line(screenbegin);
1179 }
1180 }
1181 } else if (beg_cur > end_scr) {
1182 // "d" is after bottom line on screen
1183 // how many lines do we have to move
1184 cnt = count_lines(end_scr, beg_cur);
1185 if (cnt > (rows - 1) / 2)
1186 goto sc1; // too many lines
1187 for (ro = 0; ro < cnt - 1; ro++) {
1188 // move screen begin the same amount
1189 screenbegin = next_line(screenbegin);
1190 // now, move the end of screen
1191 end_scr = next_line(end_scr);
1192 end_scr = end_line(end_scr);
1193 }
1194 }
1195 // "d" is on screen- find out which row
1196 tp = screenbegin;
1197 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1198 if (tp == beg_cur)
1199 break;
1200 tp = next_line(tp);
1201 }
1202
1203 // find out what col "d" is on
1204 co = 0;
1205 do { // drive "co" to correct column
1206 if (*tp == '\n' || *tp == '\0')
1207 break;
1208 if (*tp == '\t') {
1209 // 7 - (co % 8 )
1210 co += ((tabstop - 1) - (co % tabstop));
1211 } else if (*tp < ' ' || *tp == 127) {
1212 co++; // display as ^X, use 2 columns
1213 }
1214 } while (tp++ < d && ++co);
1215
1216 // "co" is the column where "dot" is.
1217 // The screen has "columns" columns.
1218 // The currently displayed columns are 0+offset -- columns+ofset
1219 // |-------------------------------------------------------------|
1220 // ^ ^ ^
1221 // offset | |------- columns ----------------|
1222 //
1223 // If "co" is already in this range then we do not have to adjust offset
1224 // but, we do have to subtract the "offset" bias from "co".
1225 // If "co" is outside this range then we have to change "offset".
1226 // If the first char of a line is a tab the cursor will try to stay
1227 // in column 7, but we have to set offset to 0.
1228
1229 if (co < 0 + offset) {
1230 offset = co;
1231 }
1232 if (co >= columns + offset) {
1233 offset = co - columns + 1;
1234 }
1235 // if the first char of the line is a tab, and "dot" is sitting on it
1236 // force offset to 0.
1237 if (d == beg_cur && *d == '\t') {
1238 offset = 0;
1239 }
1240 co -= offset;
1241
1242 *row = ro;
1243 *col = co;
1244}
1245
1246//----- Text Movement Routines ---------------------------------
1247static Byte *begin_line(Byte * p) // return pointer to first char cur line
1248{
1249 while (p > text && p[-1] != '\n')
1250 p--; // go to cur line B-o-l
1251 return (p);
1252}
1253
1254static Byte *end_line(Byte * p) // return pointer to NL of cur line line
1255{
1256 while (p < end - 1 && *p != '\n')
1257 p++; // go to cur line E-o-l
1258 return (p);
1259}
1260
1261static inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
1262{
1263 while (p < end - 1 && *p != '\n')
1264 p++; // go to cur line E-o-l
1265 // Try to stay off of the Newline
1266 if (*p == '\n' && (p - begin_line(p)) > 0)
1267 p--;
1268 return (p);
1269}
1270
1271static Byte *prev_line(Byte * p) // return pointer first char prev line
1272{
1273 p = begin_line(p); // goto begining of cur line
1274 if (p[-1] == '\n' && p > text)
1275 p--; // step to prev line
1276 p = begin_line(p); // goto begining of prev line
1277 return (p);
1278}
1279
1280static Byte *next_line(Byte * p) // return pointer first char next line
1281{
1282 p = end_line(p);
1283 if (*p == '\n' && p < end - 1)
1284 p++; // step to next line
1285 return (p);
1286}
1287
1288//----- Text Information Routines ------------------------------
1289static Byte *end_screen(void)
1290{
1291 Byte *q;
1292 int cnt;
1293
1294 // find new bottom line
1295 q = screenbegin;
1296 for (cnt = 0; cnt < rows - 2; cnt++)
1297 q = next_line(q);
1298 q = end_line(q);
1299 return (q);
1300}
1301
1302static int count_lines(Byte * start, Byte * stop) // count line from start to stop
1303{
1304 Byte *q;
1305 int cnt;
1306
1307 if (stop < start) { // start and stop are backwards- reverse them
1308 q = start;
1309 start = stop;
1310 stop = q;
1311 }
1312 cnt = 0;
1313 stop = end_line(stop); // get to end of this line
1314 for (q = start; q <= stop && q <= end - 1; q++) {
1315 if (*q == '\n')
1316 cnt++;
1317 }
1318 return (cnt);
1319}
1320
1321static Byte *find_line(int li) // find begining of line #li
1322{
1323 Byte *q;
1324
1325 for (q = text; li > 1; li--) {
1326 q = next_line(q);
1327 }
1328 return (q);
1329}
1330
1331//----- Dot Movement Routines ----------------------------------
1332static void dot_left(void)
1333{
1334 if (dot > text && dot[-1] != '\n')
1335 dot--;
1336}
1337
1338static void dot_right(void)
1339{
1340 if (dot < end - 1 && *dot != '\n')
1341 dot++;
1342}
1343
1344static void dot_begin(void)
1345{
1346 dot = begin_line(dot); // return pointer to first char cur line
1347}
1348
1349static void dot_end(void)
1350{
1351 dot = end_line(dot); // return pointer to last char cur line
1352}
1353
1354static Byte *move_to_col(Byte * p, int l)
1355{
1356 int co;
1357
1358 p = begin_line(p);
1359 co = 0;
1360 do {
1361 if (*p == '\n' || *p == '\0')
1362 break;
1363 if (*p == '\t') {
1364 // 7 - (co % 8 )
1365 co += ((tabstop - 1) - (co % tabstop));
1366 } else if (*p < ' ' || *p == 127) {
1367 co++; // display as ^X, use 2 columns
1368 }
1369 } while (++co <= l && p++ < end);
1370 return (p);
1371}
1372
1373static void dot_next(void)
1374{
1375 dot = next_line(dot);
1376}
1377
1378static void dot_prev(void)
1379{
1380 dot = prev_line(dot);
1381}
1382
1383static void dot_scroll(int cnt, int dir)
1384{
1385 Byte *q;
1386
1387 for (; cnt > 0; cnt--) {
1388 if (dir < 0) {
1389 // scroll Backwards
1390 // ctrl-Y scroll up one line
1391 screenbegin = prev_line(screenbegin);
1392 } else {
1393 // scroll Forwards
1394 // ctrl-E scroll down one line
1395 screenbegin = next_line(screenbegin);
1396 }
1397 }
1398 // make sure "dot" stays on the screen so we dont scroll off
1399 if (dot < screenbegin)
1400 dot = screenbegin;
1401 q = end_screen(); // find new bottom line
1402 if (dot > q)
1403 dot = begin_line(q); // is dot is below bottom line?
1404 dot_skip_over_ws();
1405}
1406
1407static void dot_skip_over_ws(void)
1408{
1409 // skip WS
1410 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1411 dot++;
1412}
1413
1414static void dot_delete(void) // delete the char at 'dot'
1415{
1416 (void) text_hole_delete(dot, dot);
1417}
1418
1419static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end"
1420{
1421 if (p >= end && end > text) {
1422 p = end - 1;
1423 indicate_error('1');
1424 }
1425 if (p < text) {
1426 p = text;
1427 indicate_error('2');
1428 }
1429 return (p);
1430}
1431
1432//----- Helper Utility Routines --------------------------------
1433
1434//----------------------------------------------------------------
1435//----- Char Routines --------------------------------------------
1436/* Chars that are part of a word-
1437 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1438 * Chars that are Not part of a word (stoppers)
1439 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1440 * Chars that are WhiteSpace
1441 * TAB NEWLINE VT FF RETURN SPACE
1442 * DO NOT COUNT NEWLINE AS WHITESPACE
1443 */
1444
1445static Byte *new_screen(int ro, int co)
1446{
1447 int li;
1448
1449 free(screen);
1450 screensize = ro * co + 8;
1451 screen = (Byte *) xmalloc(screensize);
1452 // initialize the new screen. assume this will be a empty file.
1453 screen_erase();
1454 // non-existent text[] lines start with a tilde (~).
1455 for (li = 1; li < ro - 1; li++) {
1456 screen[(li * co) + 0] = '~';
1457 }
1458 return (screen);
1459}
1460
1461static Byte *new_text(int size)
1462{
1463 if (size < 10240)
1464 size = 10240; // have a minimum size for new files
1465 free(text);
1466 text = (Byte *) xmalloc(size + 8);
1467 memset(text, '\0', size); // clear new text[]
1468 //text += 4; // leave some room for "oops"
1469 textend = text + size - 1;
1470 //textend -= 4; // leave some root for "oops"
1471 return (text);
1472}
1473
1474#ifdef CONFIG_FEATURE_VI_SEARCH
1475static int mycmp(Byte * s1, Byte * s2, int len)
1476{
1477 int i;
1478
1479 i = strncmp((char *) s1, (char *) s2, len);
1480#ifdef CONFIG_FEATURE_VI_SETOPTS
1481 if (ignorecase) {
1482 i = strncasecmp((char *) s1, (char *) s2, len);
1483 }
1484#endif /* CONFIG_FEATURE_VI_SETOPTS */
1485 return (i);
1486}
1487
1488static Byte *char_search(Byte * p, Byte * pat, int dir, int range) // search for pattern starting at p
1489{
1490#ifndef REGEX_SEARCH
1491 Byte *start, *stop;
1492 int len;
1493
1494 len = strlen((char *) pat);
1495 if (dir == FORWARD) {
1496 stop = end - 1; // assume range is p - end-1
1497 if (range == LIMITED)
1498 stop = next_line(p); // range is to next line
1499 for (start = p; start < stop; start++) {
1500 if (mycmp(start, pat, len) == 0) {
1501 return (start);
1502 }
1503 }
1504 } else if (dir == BACK) {
1505 stop = text; // assume range is text - p
1506 if (range == LIMITED)
1507 stop = prev_line(p); // range is to prev line
1508 for (start = p - len; start >= stop; start--) {
1509 if (mycmp(start, pat, len) == 0) {
1510 return (start);
1511 }
1512 }
1513 }
1514 // pattern not found
1515 return (NULL);
1516#else /*REGEX_SEARCH */
1517 char *q;
1518 struct re_pattern_buffer preg;
1519 int i;
1520 int size, range;
1521
1522 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1523 preg.translate = 0;
1524 preg.fastmap = 0;
1525 preg.buffer = 0;
1526 preg.allocated = 0;
1527
1528 // assume a LIMITED forward search
1529 q = next_line(p);
1530 q = end_line(q);
1531 q = end - 1;
1532 if (dir == BACK) {
1533 q = prev_line(p);
1534 q = text;
1535 }
1536 // count the number of chars to search over, forward or backward
1537 size = q - p;
1538 if (size < 0)
1539 size = p - q;
1540 // RANGE could be negative if we are searching backwards
1541 range = q - p;
1542
1543 q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
1544 if (q != 0) {
1545 // The pattern was not compiled
1546 psbs("bad search pattern: \"%s\": %s", pat, q);
1547 i = 0; // return p if pattern not compiled
1548 goto cs1;
1549 }
1550
1551 q = p;
1552 if (range < 0) {
1553 q = p - size;
1554 if (q < text)
1555 q = text;
1556 }
1557 // search for the compiled pattern, preg, in p[]
1558 // range < 0- search backward
1559 // range > 0- search forward
1560 // 0 < start < size
1561 // re_search() < 0 not found or error
1562 // re_search() > 0 index of found pattern
1563 // struct pattern char int int int struct reg
1564 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1565 i = re_search(&preg, q, size, 0, range, 0);
1566 if (i == -1) {
1567 p = 0;
1568 i = 0; // return NULL if pattern not found
1569 }
1570 cs1:
1571 if (dir == FORWARD) {
1572 p = p + i;
1573 } else {
1574 p = p - i;
1575 }
1576 return (p);
1577#endif /*REGEX_SEARCH */
1578}
1579#endif /* CONFIG_FEATURE_VI_SEARCH */
1580
1581static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
1582{
1583 if (c == 22) { // Is this an ctrl-V?
1584 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1585 p--; // backup onto ^
1586 refresh(FALSE); // show the ^
1587 c = get_one_char();
1588 *p = c;
1589 p++;
1590 file_modified = TRUE; // has the file been modified
1591 } else if (c == 27) { // Is this an ESC?
1592 cmd_mode = 0;
1593 cmdcnt = 0;
1594 end_cmd_q(); // stop adding to q
1595 strcpy((char *) status_buffer, " "); // clear the status buffer
1596 if ((p[-1] != '\n') && (dot>text)) {
1597 p--;
1598 }
1599 } else if (c == erase_char) { // Is this a BS
1600 // 123456789
1601 if ((p[-1] != '\n') && (dot>text)) {
1602 p--;
1603 p = text_hole_delete(p, p); // shrink buffer 1 char
1604#ifdef CONFIG_FEATURE_VI_DOT_CMD
1605 // also rmove char from last_modifying_cmd
1606 if (last_modifying_cmd != 0 && strlen((char *) last_modifying_cmd) > 0) {
1607 Byte *q;
1608
1609 q = last_modifying_cmd;
1610 q[strlen((char *) q) - 1] = '\0'; // erase BS
1611 q[strlen((char *) q) - 1] = '\0'; // erase prev char
1612 }
1613#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1614 }
1615 } else {
1616 // insert a char into text[]
1617 Byte *sp; // "save p"
1618
1619 if (c == 13)
1620 c = '\n'; // translate \r to \n
1621 sp = p; // remember addr of insert
1622 p = stupid_insert(p, c); // insert the char
1623#ifdef CONFIG_FEATURE_VI_SETOPTS
1624 if (showmatch && strchr(")]}", *sp) != NULL) {
1625 showmatching(sp);
1626 }
1627 if (autoindent && c == '\n') { // auto indent the new line
1628 Byte *q;
1629
1630 q = prev_line(p); // use prev line as templet
1631 for (; isblnk(*q); q++) {
1632 p = stupid_insert(p, *q); // insert the char
1633 }
1634 }
1635#endif /* CONFIG_FEATURE_VI_SETOPTS */
1636 }
1637 return (p);
1638}
1639
1640static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
1641{
1642 p = text_hole_make(p, 1);
1643 if (p != 0) {
1644 *p = c;
1645 file_modified = TRUE; // has the file been modified
1646 p++;
1647 }
1648 return (p);
1649}
1650
1651static Byte find_range(Byte ** start, Byte ** stop, Byte c)
1652{
1653 Byte *save_dot, *p, *q;
1654 int cnt;
1655
1656 save_dot = dot;
1657 p = q = dot;
1658
1659 if (strchr("cdy><", c)) {
1660 // these cmds operate on whole lines
1661 p = q = begin_line(p);
1662 for (cnt = 1; cnt < cmdcnt; cnt++) {
1663 q = next_line(q);
1664 }
1665 q = end_line(q);
1666 } else if (strchr("^%$0bBeEft", c)) {
1667 // These cmds operate on char positions
1668 do_cmd(c); // execute movement cmd
1669 q = dot;
1670 } else if (strchr("wW", c)) {
1671 do_cmd(c); // execute movement cmd
1672 // if we are at the next word's first char
1673 // step back one char
1674 // but check the possibilities when it is true
1675 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
1676 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1677 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1678 dot--; // move back off of next word
1679 if (dot > text && *dot == '\n')
1680 dot--; // stay off NL
1681 q = dot;
1682 } else if (strchr("H-k{", c)) {
1683 // these operate on multi-lines backwards
1684 q = end_line(dot); // find NL
1685 do_cmd(c); // execute movement cmd
1686 dot_begin();
1687 p = dot;
1688 } else if (strchr("L+j}\r\n", c)) {
1689 // these operate on multi-lines forwards
1690 p = begin_line(dot);
1691 do_cmd(c); // execute movement cmd
1692 dot_end(); // find NL
1693 q = dot;
1694 } else {
1695 c = 27; // error- return an ESC char
1696 //break;
1697 }
1698 *start = p;
1699 *stop = q;
1700 if (q < p) {
1701 *start = q;
1702 *stop = p;
1703 }
1704 dot = save_dot;
1705 return (c);
1706}
1707
1708static int st_test(Byte * p, int type, int dir, Byte * tested)
1709{
1710 Byte c, c0, ci;
1711 int test, inc;
1712
1713 inc = dir;
1714 c = c0 = p[0];
1715 ci = p[inc];
1716 test = 0;
1717
1718 if (type == S_BEFORE_WS) {
1719 c = ci;
1720 test = ((!isspace(c)) || c == '\n');
1721 }
1722 if (type == S_TO_WS) {
1723 c = c0;
1724 test = ((!isspace(c)) || c == '\n');
1725 }
1726 if (type == S_OVER_WS) {
1727 c = c0;
1728 test = ((isspace(c)));
1729 }
1730 if (type == S_END_PUNCT) {
1731 c = ci;
1732 test = ((ispunct(c)));
1733 }
1734 if (type == S_END_ALNUM) {
1735 c = ci;
1736 test = ((isalnum(c)) || c == '_');
1737 }
1738 *tested = c;
1739 return (test);
1740}
1741
1742static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
1743{
1744 Byte c;
1745
1746 while (st_test(p, type, dir, &c)) {
1747 // make sure we limit search to correct number of lines
1748 if (c == '\n' && --linecnt < 1)
1749 break;
1750 if (dir >= 0 && p >= end - 1)
1751 break;
1752 if (dir < 0 && p <= text)
1753 break;
1754 p += dir; // move to next char
1755 }
1756 return (p);
1757}
1758
1759// find matching char of pair () [] {}
1760static Byte *find_pair(Byte * p, Byte c)
1761{
1762 Byte match, *q;
1763 int dir, level;
1764
1765 match = ')';
1766 level = 1;
1767 dir = 1; // assume forward
1768 switch (c) {
1769 case '(':
1770 match = ')';
1771 break;
1772 case '[':
1773 match = ']';
1774 break;
1775 case '{':
1776 match = '}';
1777 break;
1778 case ')':
1779 match = '(';
1780 dir = -1;
1781 break;
1782 case ']':
1783 match = '[';
1784 dir = -1;
1785 break;
1786 case '}':
1787 match = '{';
1788 dir = -1;
1789 break;
1790 }
1791 for (q = p + dir; text <= q && q < end; q += dir) {
1792 // look for match, count levels of pairs (( ))
1793 if (*q == c)
1794 level++; // increase pair levels
1795 if (*q == match)
1796 level--; // reduce pair level
1797 if (level == 0)
1798 break; // found matching pair
1799 }
1800 if (level != 0)
1801 q = NULL; // indicate no match
1802 return (q);
1803}
1804
1805#ifdef CONFIG_FEATURE_VI_SETOPTS
1806// show the matching char of a pair, () [] {}
1807static void showmatching(Byte * p)
1808{
1809 Byte *q, *save_dot;
1810
1811 // we found half of a pair
1812 q = find_pair(p, *p); // get loc of matching char
1813 if (q == NULL) {
1814 indicate_error('3'); // no matching char
1815 } else {
1816 // "q" now points to matching pair
1817 save_dot = dot; // remember where we are
1818 dot = q; // go to new loc
1819 refresh(FALSE); // let the user see it
1820 (void) mysleep(40); // give user some time
1821 dot = save_dot; // go back to old loc
1822 refresh(FALSE);
1823 }
1824}
1825#endif /* CONFIG_FEATURE_VI_SETOPTS */
1826
1827// open a hole in text[]
1828static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
1829{
1830 Byte *src, *dest;
1831 int cnt;
1832
1833 if (size <= 0)
1834 goto thm0;
1835 src = p;
1836 dest = p + size;
1837 cnt = end - src; // the rest of buffer
1838 if (memmove(dest, src, cnt) != dest) {
1839 psbs("can't create room for new characters");
1840 }
1841 memset(p, ' ', size); // clear new hole
1842 end = end + size; // adjust the new END
1843 file_modified = TRUE; // has the file been modified
1844 thm0:
1845 return (p);
1846}
1847
1848// close a hole in text[]
1849static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
1850{
1851 Byte *src, *dest;
1852 int cnt, hole_size;
1853
1854 // move forwards, from beginning
1855 // assume p <= q
1856 src = q + 1;
1857 dest = p;
1858 if (q < p) { // they are backward- swap them
1859 src = p + 1;
1860 dest = q;
1861 }
1862 hole_size = q - p + 1;
1863 cnt = end - src;
1864 if (src < text || src > end)
1865 goto thd0;
1866 if (dest < text || dest >= end)
1867 goto thd0;
1868 if (src >= end)
1869 goto thd_atend; // just delete the end of the buffer
1870 if (memmove(dest, src, cnt) != dest) {
1871 psbs("can't delete the character");
1872 }
1873 thd_atend:
1874 end = end - hole_size; // adjust the new END
1875 if (dest >= end)
1876 dest = end - 1; // make sure dest in below end-1
1877 if (end <= text)
1878 dest = end = text; // keep pointers valid
1879 file_modified = TRUE; // has the file been modified
1880 thd0:
1881 return (dest);
1882}
1883
1884// copy text into register, then delete text.
1885// if dist <= 0, do not include, or go past, a NewLine
1886//
1887static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
1888{
1889 Byte *p;
1890
1891 // make sure start <= stop
1892 if (start > stop) {
1893 // they are backwards, reverse them
1894 p = start;
1895 start = stop;
1896 stop = p;
1897 }
1898 if (dist <= 0) {
1899 // we can not cross NL boundaries
1900 p = start;
1901 if (*p == '\n')
1902 return (p);
1903 // dont go past a NewLine
1904 for (; p + 1 <= stop; p++) {
1905 if (p[1] == '\n') {
1906 stop = p; // "stop" just before NewLine
1907 break;
1908 }
1909 }
1910 }
1911 p = start;
1912#ifdef CONFIG_FEATURE_VI_YANKMARK
1913 text_yank(start, stop, YDreg);
1914#endif /* CONFIG_FEATURE_VI_YANKMARK */
1915 if (yf == YANKDEL) {
1916 p = text_hole_delete(start, stop);
1917 } // delete lines
1918 return (p);
1919}
1920
1921static void show_help(void)
1922{
1923 puts("These features are available:"
1924#ifdef CONFIG_FEATURE_VI_SEARCH
1925 "\n\tPattern searches with / and ?"
1926#endif /* CONFIG_FEATURE_VI_SEARCH */
1927#ifdef CONFIG_FEATURE_VI_DOT_CMD
1928 "\n\tLast command repeat with \'.\'"
1929#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1930#ifdef CONFIG_FEATURE_VI_YANKMARK
1931 "\n\tLine marking with 'x"
1932 "\n\tNamed buffers with \"x"
1933#endif /* CONFIG_FEATURE_VI_YANKMARK */
1934#ifdef CONFIG_FEATURE_VI_READONLY
1935 "\n\tReadonly if vi is called as \"view\""
1936 "\n\tReadonly with -R command line arg"
1937#endif /* CONFIG_FEATURE_VI_READONLY */
1938#ifdef CONFIG_FEATURE_VI_SET
1939 "\n\tSome colon mode commands with \':\'"
1940#endif /* CONFIG_FEATURE_VI_SET */
1941#ifdef CONFIG_FEATURE_VI_SETOPTS
1942 "\n\tSettable options with \":set\""
1943#endif /* CONFIG_FEATURE_VI_SETOPTS */
1944#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
1945 "\n\tSignal catching- ^C"
1946 "\n\tJob suspend and resume with ^Z"
1947#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
1948#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
1949 "\n\tAdapt to window re-sizes"
1950#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
1951 );
1952}
1953
1954static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
1955{
1956 Byte c, b[2];
1957
1958 b[1] = '\0';
1959 strcpy((char *) buf, ""); // init buf
1960 if (strlen((char *) s) <= 0)
1961 s = (Byte *) "(NULL)";
1962 for (; *s > '\0'; s++) {
1963 int c_is_no_print;
1964
1965 c = *s;
1966 c_is_no_print = c > 127 && !Isprint(c);
1967 if (c_is_no_print) {
1968 strcat((char *) buf, SOn);
1969 c = '.';
1970 }
1971 if (c < ' ' || c == 127) {
1972 strcat((char *) buf, "^");
1973 if(c == 127)
1974 c = '?';
1975 else
1976 c += '@';
1977 }
1978 b[0] = c;
1979 strcat((char *) buf, (char *) b);
1980 if (c_is_no_print)
1981 strcat((char *) buf, SOs);
1982 if (*s == '\n') {
1983 strcat((char *) buf, "$");
1984 }
1985 }
1986}
1987
1988#ifdef CONFIG_FEATURE_VI_DOT_CMD
1989static void start_new_cmd_q(Byte c)
1990{
1991 // release old cmd
1992 free(last_modifying_cmd);
1993 // get buffer for new cmd
1994 last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
1995 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
1996 // if there is a current cmd count put it in the buffer first
1997 if (cmdcnt > 0)
1998 sprintf((char *) last_modifying_cmd, "%d", cmdcnt);
1999 // save char c onto queue
2000 last_modifying_cmd[strlen((char *) last_modifying_cmd)] = c;
2001 adding2q = 1;
2002 return;
2003}
2004
2005static void end_cmd_q(void)
2006{
2007#ifdef CONFIG_FEATURE_VI_YANKMARK
2008 YDreg = 26; // go back to default Yank/Delete reg
2009#endif /* CONFIG_FEATURE_VI_YANKMARK */
2010 adding2q = 0;
2011 return;
2012}
2013#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2014
2015#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
2016static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
2017{
2018 int cnt, i;
2019
2020 i = strlen((char *) s);
2021 p = text_hole_make(p, i);
2022 strncpy((char *) p, (char *) s, i);
2023 for (cnt = 0; *s != '\0'; s++) {
2024 if (*s == '\n')
2025 cnt++;
2026 }
2027#ifdef CONFIG_FEATURE_VI_YANKMARK
2028 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2029#endif /* CONFIG_FEATURE_VI_YANKMARK */
2030 return (p);
2031}
2032#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
2033
2034#ifdef CONFIG_FEATURE_VI_YANKMARK
2035static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
2036{
2037 Byte *t;
2038 int cnt;
2039
2040 if (q < p) { // they are backwards- reverse them
2041 t = q;
2042 q = p;
2043 p = t;
2044 }
2045 cnt = q - p + 1;
2046 t = reg[dest];
2047 free(t); // if already a yank register, free it
2048 t = (Byte *) xmalloc(cnt + 1); // get a new register
2049 memset(t, '\0', cnt + 1); // clear new text[]
2050 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
2051 reg[dest] = t;
2052 return (p);
2053}
2054
2055static Byte what_reg(void)
2056{
2057 Byte c;
2058 int i;
2059
2060 i = 0;
2061 c = 'D'; // default to D-reg
2062 if (0 <= YDreg && YDreg <= 25)
2063 c = 'a' + (Byte) YDreg;
2064 if (YDreg == 26)
2065 c = 'D';
2066 if (YDreg == 27)
2067 c = 'U';
2068 return (c);
2069}
2070
2071static void check_context(Byte cmd)
2072{
2073 // A context is defined to be "modifying text"
2074 // Any modifying command establishes a new context.
2075
2076 if (dot < context_start || dot > context_end) {
2077 if (strchr((char *) modifying_cmds, cmd) != NULL) {
2078 // we are trying to modify text[]- make this the current context
2079 mark[27] = mark[26]; // move cur to prev
2080 mark[26] = dot; // move local to cur
2081 context_start = prev_line(prev_line(dot));
2082 context_end = next_line(next_line(dot));
2083 //loiter= start_loiter= now;
2084 }
2085 }
2086 return;
2087}
2088
2089static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
2090{
2091 Byte *tmp;
2092
2093 // the current context is in mark[26]
2094 // the previous context is in mark[27]
2095 // only swap context if other context is valid
2096 if (text <= mark[27] && mark[27] <= end - 1) {
2097 tmp = mark[27];
2098 mark[27] = mark[26];
2099 mark[26] = tmp;
2100 p = mark[26]; // where we are going- previous context
2101 context_start = prev_line(prev_line(prev_line(p)));
2102 context_end = next_line(next_line(next_line(p)));
2103 }
2104 return (p);
2105}
2106#endif /* CONFIG_FEATURE_VI_YANKMARK */
2107
2108static int isblnk(Byte c) // is the char a blank or tab
2109{
2110 return (c == ' ' || c == '\t');
2111}
2112
2113//----- Set terminal attributes --------------------------------
2114static void rawmode(void)
2115{
2116 tcgetattr(0, &term_orig);
2117 term_vi = term_orig;
2118 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2119 term_vi.c_iflag &= (~IXON & ~ICRNL);
2120 term_vi.c_oflag &= (~ONLCR);
2121 term_vi.c_cc[VMIN] = 1;
2122 term_vi.c_cc[VTIME] = 0;
2123 erase_char = term_vi.c_cc[VERASE];
2124 tcsetattr(0, TCSANOW, &term_vi);
2125}
2126
2127static void cookmode(void)
2128{
2129 fflush(stdout);
2130 tcsetattr(0, TCSANOW, &term_orig);
2131}
2132
2133//----- Come here when we get a window resize signal ---------
2134#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
2135static void winch_sig(int sig)
2136{
2137 signal(SIGWINCH, winch_sig);
2138#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2139 window_size_get(0);
2140#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2141 new_screen(rows, columns); // get memory for virtual screen
2142 redraw(TRUE); // re-draw the screen
2143}
2144
2145//----- Come here when we get a continue signal -------------------
2146static void cont_sig(int sig)
2147{
2148 rawmode(); // terminal to "raw"
2149 *status_buffer = '\0'; // clear the status buffer
2150 redraw(TRUE); // re-draw the screen
2151
2152 signal(SIGTSTP, suspend_sig);
2153 signal(SIGCONT, SIG_DFL);
2154 kill(my_pid, SIGCONT);
2155}
2156
2157//----- Come here when we get a Suspend signal -------------------
2158static void suspend_sig(int sig)
2159{
2160 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2161 clear_to_eol(); // Erase to end of line
2162 cookmode(); // terminal to "cooked"
2163
2164 signal(SIGCONT, cont_sig);
2165 signal(SIGTSTP, SIG_DFL);
2166 kill(my_pid, SIGTSTP);
2167}
2168
2169//----- Come here when we get a signal ---------------------------
2170static void catch_sig(int sig)
2171{
2172 signal(SIGHUP, catch_sig);
2173 signal(SIGINT, catch_sig);
2174 signal(SIGTERM, catch_sig);
2175 signal(SIGALRM, catch_sig);
2176 if(sig)
2177 longjmp(restart, sig);
2178}
2179
2180//----- Come here when we get a core dump signal -----------------
2181static void core_sig(int sig)
2182{
2183 signal(SIGQUIT, core_sig);
2184 signal(SIGILL, core_sig);
2185 signal(SIGTRAP, core_sig);
2186 signal(SIGIOT, core_sig);
2187 signal(SIGABRT, core_sig);
2188 signal(SIGFPE, core_sig);
2189 signal(SIGBUS, core_sig);
2190 signal(SIGSEGV, core_sig);
2191#ifdef SIGSYS
2192 signal(SIGSYS, core_sig);
2193#endif
2194
2195 if(sig) { // signaled
2196 dot = bound_dot(dot); // make sure "dot" is valid
2197
2198 longjmp(restart, sig);
2199 }
2200}
2201#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2202
2203static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2204{
2205 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
2206 fflush(stdout);
2207 FD_ZERO(&rfds);
2208 FD_SET(0, &rfds);
2209 tv.tv_sec = 0;
2210 tv.tv_usec = hund * 10000;
2211 select(1, &rfds, NULL, NULL, &tv);
2212 return (FD_ISSET(0, &rfds));
2213}
2214
2215static Byte readbuffer[BUFSIZ];
2216static int readed_for_parse;
2217
2218//----- IO Routines --------------------------------------------
2219static Byte readit(void) // read (maybe cursor) key from stdin
2220{
2221 Byte c;
2222 int n;
2223 struct esc_cmds {
2224 const char *seq;
2225 Byte val;
2226 };
2227
2228 static const struct esc_cmds esccmds[] = {
2229 {"OA", (Byte) VI_K_UP}, // cursor key Up
2230 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2231 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2232 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2233 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2234 {"OF", (Byte) VI_K_END}, // Cursor Key End
2235 {"[A", (Byte) VI_K_UP}, // cursor key Up
2236 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2237 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2238 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2239 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2240 {"[F", (Byte) VI_K_END}, // Cursor Key End
2241 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2242 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2243 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2244 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2245 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2246 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2247 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2248 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2249 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2250 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2251 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2252 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2253 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2254 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2255 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2256 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2257 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2258 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2259 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2260 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2261 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
2262 };
2263
2264#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2265
2266 (void) alarm(0); // turn alarm OFF while we wait for input
2267 fflush(stdout);
2268 n = readed_for_parse;
2269 // get input from User- are there already input chars in Q?
2270 if (n <= 0) {
2271 ri0:
2272 // the Q is empty, wait for a typed char
2273 n = read(0, readbuffer, BUFSIZ - 1);
2274 if (n < 0) {
2275 if (errno == EINTR)
2276 goto ri0; // interrupted sys call
2277 if (errno == EBADF)
2278 editing = 0;
2279 if (errno == EFAULT)
2280 editing = 0;
2281 if (errno == EINVAL)
2282 editing = 0;
2283 if (errno == EIO)
2284 editing = 0;
2285 errno = 0;
2286 }
2287 if(n <= 0)
2288 return 0; // error
2289 if (readbuffer[0] == 27) {
2290 // This is an ESC char. Is this Esc sequence?
2291 // Could be bare Esc key. See if there are any
2292 // more chars to read after the ESC. This would
2293 // be a Function or Cursor Key sequence.
2294 FD_ZERO(&rfds);
2295 FD_SET(0, &rfds);
2296 tv.tv_sec = 0;
2297 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2298
2299 // keep reading while there are input chars and room in buffer
2300 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
2301 // read the rest of the ESC string
2302 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2303 if (r > 0) {
2304 n += r;
2305 }
2306 }
2307 }
2308 readed_for_parse = n;
2309 }
2310 c = readbuffer[0];
2311 if(c == 27 && n > 1) {
2312 // Maybe cursor or function key?
2313 const struct esc_cmds *eindex;
2314
2315 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2316 int cnt = strlen(eindex->seq);
2317
2318 if(n <= cnt)
2319 continue;
2320 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2321 continue;
2322 // is a Cursor key- put derived value back into Q
2323 c = eindex->val;
2324 // for squeeze out the ESC sequence
2325 n = cnt + 1;
2326 break;
2327 }
2328 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2329 /* defined ESC sequence not found, set only one ESC */
2330 n = 1;
2331 }
2332 } else {
2333 n = 1;
2334 }
2335 // remove key sequence from Q
2336 readed_for_parse -= n;
2337 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
2338 (void) alarm(3); // we are done waiting for input, turn alarm ON
2339 return (c);
2340}
2341
2342//----- IO Routines --------------------------------------------
2343static Byte get_one_char()
2344{
2345 static Byte c;
2346
2347#ifdef CONFIG_FEATURE_VI_DOT_CMD
2348 // ! adding2q && ioq == 0 read()
2349 // ! adding2q && ioq != 0 *ioq
2350 // adding2q *last_modifying_cmd= read()
2351 if (!adding2q) {
2352 // we are not adding to the q.
2353 // but, we may be reading from a q
2354 if (ioq == 0) {
2355 // there is no current q, read from STDIN
2356 c = readit(); // get the users input
2357 } else {
2358 // there is a queue to get chars from first
2359 c = *ioq++;
2360 if (c == '\0') {
2361 // the end of the q, read from STDIN
2362 free(ioq_start);
2363 ioq_start = ioq = 0;
2364 c = readit(); // get the users input
2365 }
2366 }
2367 } else {
2368 // adding STDIN chars to q
2369 c = readit(); // get the users input
2370 if (last_modifying_cmd != 0) {
2371 int len = strlen((char *) last_modifying_cmd);
2372 if (len + 1 >= BUFSIZ) {
2373 psbs("last_modifying_cmd overrun");
2374 } else {
2375 // add new char to q
2376 last_modifying_cmd[len] = c;
2377 }
2378 }
2379 }
2380#else /* CONFIG_FEATURE_VI_DOT_CMD */
2381 c = readit(); // get the users input
2382#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2383 return (c); // return the char, where ever it came from
2384}
2385
2386static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2387{
2388 Byte buf[BUFSIZ];
2389 Byte c;
2390 int i;
2391 static Byte *obufp = NULL;
2392
2393 strcpy((char *) buf, (char *) prompt);
2394 *status_buffer = '\0'; // clear the status buffer
2395 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2396 clear_to_eol(); // clear the line
2397 write1(prompt); // write out the :, /, or ? prompt
2398
2399 for (i = strlen((char *) buf); i < BUFSIZ;) {
2400 c = get_one_char(); // read user input
2401 if (c == '\n' || c == '\r' || c == 27)
2402 break; // is this end of input
2403 if (c == erase_char) { // user wants to erase prev char
2404 i--; // backup to prev char
2405 buf[i] = '\0'; // erase the char
2406 buf[i + 1] = '\0'; // null terminate buffer
2407 write1("\b \b"); // erase char on screen
2408 if (i <= 0) { // user backs up before b-o-l, exit
2409 break;
2410 }
2411 } else {
2412 buf[i] = c; // save char in buffer
2413 buf[i + 1] = '\0'; // make sure buffer is null terminated
2414 putchar(c); // echo the char back to user
2415 i++;
2416 }
2417 }
2418 refresh(FALSE);
2419 free(obufp);
2420 obufp = (Byte *) bb_xstrdup((char *) buf);
2421 return (obufp);
2422}
2423
2424static int file_size(const Byte * fn) // what is the byte size of "fn"
2425{
2426 struct stat st_buf;
2427 int cnt, sr;
2428
2429 if (fn == 0 || strlen(fn) <= 0)
2430 return (-1);
2431 cnt = -1;
2432 sr = stat((char *) fn, &st_buf); // see if file exists
2433 if (sr >= 0) {
2434 cnt = (int) st_buf.st_size;
2435 }
2436 return (cnt);
2437}
2438
2439static int file_insert(Byte * fn, Byte * p, int size)
2440{
2441 int fd, cnt;
2442
2443 cnt = -1;
2444#ifdef CONFIG_FEATURE_VI_READONLY
2445 readonly = FALSE;
2446#endif /* CONFIG_FEATURE_VI_READONLY */
2447 if (fn == 0 || strlen((char*) fn) <= 0) {
2448 psbs("No filename given");
2449 goto fi0;
2450 }
2451 if (size == 0) {
2452 // OK- this is just a no-op
2453 cnt = 0;
2454 goto fi0;
2455 }
2456 if (size < 0) {
2457 psbs("Trying to insert a negative number (%d) of characters", size);
2458 goto fi0;
2459 }
2460 if (p < text || p > end) {
2461 psbs("Trying to insert file outside of memory");
2462 goto fi0;
2463 }
2464
2465 // see if we can open the file
2466#ifdef CONFIG_FEATURE_VI_READONLY
2467 if (vi_readonly) goto fi1; // do not try write-mode
2468#endif
2469 fd = open((char *) fn, O_RDWR); // assume read & write
2470 if (fd < 0) {
2471 // could not open for writing- maybe file is read only
2472#ifdef CONFIG_FEATURE_VI_READONLY
2473 fi1:
2474#endif
2475 fd = open((char *) fn, O_RDONLY); // try read-only
2476 if (fd < 0) {
2477 psbs("\"%s\" %s", fn, "could not open file");
2478 goto fi0;
2479 }
2480#ifdef CONFIG_FEATURE_VI_READONLY
2481 // got the file- read-only
2482 readonly = TRUE;
2483#endif /* CONFIG_FEATURE_VI_READONLY */
2484 }
2485 p = text_hole_make(p, size);
2486 cnt = read(fd, p, size);
2487 close(fd);
2488 if (cnt < 0) {
2489 cnt = -1;
2490 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2491 psbs("could not read file \"%s\"", fn);
2492 } else if (cnt < size) {
2493 // There was a partial read, shrink unused space text[]
2494 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
2495 psbs("could not read all of file \"%s\"", fn);
2496 }
2497 if (cnt >= size)
2498 file_modified = TRUE;
2499 fi0:
2500 return (cnt);
2501}
2502
2503static int file_write(Byte * fn, Byte * first, Byte * last)
2504{
2505 int fd, cnt, charcnt;
2506
2507 if (fn == 0) {
2508 psbs("No current filename");
2509 return (-1);
2510 }
2511 charcnt = 0;
2512 // FIXIT- use the correct umask()
2513 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2514 if (fd < 0)
2515 return (-1);
2516 cnt = last - first + 1;
2517 charcnt = write(fd, first, cnt);
2518 if (charcnt == cnt) {
2519 // good write
2520 //file_modified= FALSE; // the file has not been modified
2521 } else {
2522 charcnt = 0;
2523 }
2524 close(fd);
2525 return (charcnt);
2526}
2527
2528//----- Terminal Drawing ---------------------------------------
2529// The terminal is made up of 'rows' line of 'columns' columns.
2530// classically this would be 24 x 80.
2531// screen coordinates
2532// 0,0 ... 0,79
2533// 1,0 ... 1,79
2534// . ... .
2535// . ... .
2536// 22,0 ... 22,79
2537// 23,0 ... 23,79 status line
2538//
2539
2540//----- Move the cursor to row x col (count from 0, not 1) -------
2541static void place_cursor(int row, int col, int opti)
2542{
2543 char cm1[BUFSIZ];
2544 char *cm;
2545#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2546 char cm2[BUFSIZ];
2547 Byte *screenp;
2548 // char cm3[BUFSIZ];
2549 int Rrow= last_row;
2550#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2551
2552 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2553
2554 if (row < 0) row = 0;
2555 if (row >= rows) row = rows - 1;
2556 if (col < 0) col = 0;
2557 if (col >= columns) col = columns - 1;
2558
2559 //----- 1. Try the standard terminal ESC sequence
2560 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2561 cm= cm1;
2562 if (! opti) goto pc0;
2563
2564#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2565 //----- find the minimum # of chars to move cursor -------------
2566 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2567 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
2568
2569 // move to the correct row
2570 while (row < Rrow) {
2571 // the cursor has to move up
2572 strcat(cm2, CMup);
2573 Rrow--;
2574 }
2575 while (row > Rrow) {
2576 // the cursor has to move down
2577 strcat(cm2, CMdown);
2578 Rrow++;
2579 }
2580
2581 // now move to the correct column
2582 strcat(cm2, "\r"); // start at col 0
2583 // just send out orignal source char to get to correct place
2584 screenp = &screen[row * columns]; // start of screen line
2585 strncat(cm2, screenp, col);
2586
2587 //----- 3. Try some other way of moving cursor
2588 //---------------------------------------------
2589
2590 // pick the shortest cursor motion to send out
2591 cm= cm1;
2592 if (strlen(cm2) < strlen(cm)) {
2593 cm= cm2;
2594 } /* else if (strlen(cm3) < strlen(cm)) {
2595 cm= cm3;
2596 } */
2597#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2598 pc0:
2599 write1(cm); // move the cursor
2600}
2601
2602//----- Erase from cursor to end of line -----------------------
2603static void clear_to_eol()
2604{
2605 write1(Ceol); // Erase from cursor to end of line
2606}
2607
2608//----- Erase from cursor to end of screen -----------------------
2609static void clear_to_eos()
2610{
2611 write1(Ceos); // Erase from cursor to end of screen
2612}
2613
2614//----- Start standout mode ------------------------------------
2615static void standout_start() // send "start reverse video" sequence
2616{
2617 write1(SOs); // Start reverse video mode
2618}
2619
2620//----- End standout mode --------------------------------------
2621static void standout_end() // send "end reverse video" sequence
2622{
2623 write1(SOn); // End reverse video mode
2624}
2625
2626//----- Flash the screen --------------------------------------
2627static void flash(int h)
2628{
2629 standout_start(); // send "start reverse video" sequence
2630 redraw(TRUE);
2631 (void) mysleep(h);
2632 standout_end(); // send "end reverse video" sequence
2633 redraw(TRUE);
2634}
2635
2636static void Indicate_Error(void)
2637{
2638#ifdef CONFIG_FEATURE_VI_CRASHME
2639 if (crashme > 0)
2640 return; // generate a random command
2641#endif /* CONFIG_FEATURE_VI_CRASHME */
2642 if (!err_method) {
2643 write1(bell); // send out a bell character
2644 } else {
2645 flash(10);
2646 }
2647}
2648
2649//----- Screen[] Routines --------------------------------------
2650//----- Erase the Screen[] memory ------------------------------
2651static void screen_erase()
2652{
2653 memset(screen, ' ', screensize); // clear new screen
2654}
2655
2656//----- Draw the status line at bottom of the screen -------------
2657static void show_status_line(void)
2658{
2659 static int last_cksum;
2660 int l, cnt, cksum;
2661
2662 edit_status();
2663 cnt = strlen((char *) status_buffer);
2664 for (cksum= l= 0; l < cnt; l++) { cksum += (int)(status_buffer[l]); }
2665 // don't write the status line unless it changes
2666 if (cnt > 0 && last_cksum != cksum) {
2667 last_cksum= cksum; // remember if we have seen this line
2668 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
2669 write1(status_buffer);
2670 clear_to_eol();
2671 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2672 }
2673 fflush(stdout);
2674}
2675
2676//----- format the status buffer, the bottom line of screen ------
2677// print status buffer, with STANDOUT mode
2678static void psbs(const char *format, ...)
2679{
2680 va_list args;
2681
2682 va_start(args, format);
2683 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
2684 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format,
2685 args);
2686 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2687 va_end(args);
2688
2689 return;
2690}
2691
2692// print status buffer
2693static void psb(const char *format, ...)
2694{
2695 va_list args;
2696
2697 va_start(args, format);
2698 vsprintf((char *) status_buffer, format, args);
2699 va_end(args);
2700 return;
2701}
2702
2703static void ni(Byte * s) // display messages
2704{
2705 Byte buf[BUFSIZ];
2706
2707 print_literal(buf, s);
2708 psbs("\'%s\' is not implemented", buf);
2709}
2710
2711static void edit_status(void) // show file status on status line
2712{
2713 int cur, tot, percent;
2714
2715 cur = count_lines(text, dot);
2716 tot = count_lines(text, end - 1);
2717 // current line percent
2718 // ------------- ~~ ----------
2719 // total lines 100
2720 if (tot > 0) {
2721 percent = (100 * cur) / tot;
2722 } else {
2723 cur = tot = 0;
2724 percent = 100;
2725 }
2726 psb("\"%s\""
2727#ifdef CONFIG_FEATURE_VI_READONLY
2728 "%s"
2729#endif /* CONFIG_FEATURE_VI_READONLY */
2730 "%s line %d of %d --%d%%--",
2731 (cfn != 0 ? (char *) cfn : "No file"),
2732#ifdef CONFIG_FEATURE_VI_READONLY
2733 ((vi_readonly || readonly) ? " [Read only]" : ""),
2734#endif /* CONFIG_FEATURE_VI_READONLY */
2735 (file_modified ? " [modified]" : ""),
2736 cur, tot, percent);
2737}
2738
2739//----- Force refresh of all Lines -----------------------------
2740static void redraw(int full_screen)
2741{
2742 place_cursor(0, 0, FALSE); // put cursor in correct place
2743 clear_to_eos(); // tel terminal to erase display
2744 screen_erase(); // erase the internal screen buffer
2745 refresh(full_screen); // this will redraw the entire display
2746}
2747
2748//----- Format a text[] line into a buffer ---------------------
2749static void format_line(Byte *dest, Byte *src, int li)
2750{
2751 int co;
2752 Byte c;
2753
2754 for (co= 0; co < MAX_SCR_COLS; co++) {
2755 c= ' '; // assume blank
2756 if (li > 0 && co == 0) {
2757 c = '~'; // not first line, assume Tilde
2758 }
2759 // are there chars in text[] and have we gone past the end
2760 if (text < end && src < end) {
2761 c = *src++;
2762 }
2763 if (c == '\n')
2764 break;
2765 if (c > 127 && !Isprint(c)) {
2766 c = '.';
2767 }
2768 if (c < ' ' || c == 127) {
2769 if (c == '\t') {
2770 c = ' ';
2771 // co % 8 != 7
2772 for (; (co % tabstop) != (tabstop - 1); co++) {
2773 dest[co] = c;
2774 }
2775 } else {
2776 dest[co++] = '^';
2777 if(c == 127)
2778 c = '?';
2779 else
2780 c += '@'; // make it visible
2781 }
2782 }
2783 // the co++ is done here so that the column will
2784 // not be overwritten when we blank-out the rest of line
2785 dest[co] = c;
2786 if (src >= end)
2787 break;
2788 }
2789}
2790
2791//----- Refresh the changed screen lines -----------------------
2792// Copy the source line from text[] into the buffer and note
2793// if the current screenline is different from the new buffer.
2794// If they differ then that line needs redrawing on the terminal.
2795//
2796static void refresh(int full_screen)
2797{
2798 static int old_offset;
2799 int li, changed;
2800 Byte buf[MAX_SCR_COLS];
2801 Byte *tp, *sp; // pointer into text[] and screen[]
2802#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2803 int last_li= -2; // last line that changed- for optimizing cursor movement
2804#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2805
2806#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2807 window_size_get(0);
2808#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2809 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2810 tp = screenbegin; // index into text[] of top line
2811
2812 // compare text[] to screen[] and mark screen[] lines that need updating
2813 for (li = 0; li < rows - 1; li++) {
2814 int cs, ce; // column start & end
2815 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2816 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2817 // format current text line into buf
2818 format_line(buf, tp, li);
2819
2820 // skip to the end of the current text[] line
2821 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2822
2823 // see if there are any changes between vitual screen and buf
2824 changed = FALSE; // assume no change
2825 cs= 0;
2826 ce= columns-1;
2827 sp = &screen[li * columns]; // start of screen line
2828 if (full_screen) {
2829 // force re-draw of every single column from 0 - columns-1
2830 goto re0;
2831 }
2832 // compare newly formatted buffer with virtual screen
2833 // look forward for first difference between buf and screen
2834 for ( ; cs <= ce; cs++) {
2835 if (buf[cs + offset] != sp[cs]) {
2836 changed = TRUE; // mark for redraw
2837 break;
2838 }
2839 }
2840
2841 // look backward for last difference between buf and screen
2842 for ( ; ce >= cs; ce--) {
2843 if (buf[ce + offset] != sp[ce]) {
2844 changed = TRUE; // mark for redraw
2845 break;
2846 }
2847 }
2848 // now, cs is index of first diff, and ce is index of last diff
2849
2850 // if horz offset has changed, force a redraw
2851 if (offset != old_offset) {
2852 re0:
2853 changed = TRUE;
2854 }
2855
2856 // make a sanity check of columns indexes
2857 if (cs < 0) cs= 0;
2858 if (ce > columns-1) ce= columns-1;
2859 if (cs > ce) { cs= 0; ce= columns-1; }
2860 // is there a change between vitual screen and buf
2861 if (changed) {
2862 // copy changed part of buffer to virtual screen
2863 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2864
2865 // move cursor to column of first change
2866 if (offset != old_offset) {
2867 // opti_cur_move is still too stupid
2868 // to handle offsets correctly
2869 place_cursor(li, cs, FALSE);
2870 } else {
2871#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2872 // if this just the next line
2873 // try to optimize cursor movement
2874 // otherwise, use standard ESC sequence
2875 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2876 last_li= li;
2877#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2878 place_cursor(li, cs, FALSE); // use standard ESC sequence
2879#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2880 }
2881
2882 // write line out to terminal
2883 {
2884 int nic = ce-cs+1;
2885 char *out = sp+cs;
2886
2887 while(nic-- > 0) {
2888 putchar(*out);
2889 out++;
2890 }
2891 }
2892#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2893 last_row = li;
2894#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2895 }
2896 }
2897
2898#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2899 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2900 last_row = crow;
2901#else
2902 place_cursor(crow, ccol, FALSE);
2903#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2904
2905 if (offset != old_offset)
2906 old_offset = offset;
2907}
2908
2909//---------------------------------------------------------------------
2910//----- the Ascii Chart -----------------------------------------------
2911//
2912// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2913// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2914// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2915// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2916// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2917// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2918// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2919// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2920// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2921// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2922// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2923// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2924// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2925// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2926// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2927// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2928//---------------------------------------------------------------------
2929
2930//----- Execute a Vi Command -----------------------------------
2931static void do_cmd(Byte c)
2932{
2933 Byte c1, *p, *q, *msg, buf[9], *save_dot;
2934 int cnt, i, j, dir, yf;
2935
2936 c1 = c; // quiet the compiler
2937 cnt = yf = dir = 0; // quiet the compiler
2938 p = q = save_dot = msg = buf; // quiet the compiler
2939 memset(buf, '\0', 9); // clear buf
2940
2941 /* if this is a cursor key, skip these checks */
2942 switch (c) {
2943 case VI_K_UP:
2944 case VI_K_DOWN:
2945 case VI_K_LEFT:
2946 case VI_K_RIGHT:
2947 case VI_K_HOME:
2948 case VI_K_END:
2949 case VI_K_PAGEUP:
2950 case VI_K_PAGEDOWN:
2951 goto key_cmd_mode;
2952 }
2953
2954 if (cmd_mode == 2) {
2955 // flip-flop Insert/Replace mode
2956 if (c == VI_K_INSERT) goto dc_i;
2957 // we are 'R'eplacing the current *dot with new char
2958 if (*dot == '\n') {
2959 // don't Replace past E-o-l
2960 cmd_mode = 1; // convert to insert
2961 } else {
2962 if (1 <= c || Isprint(c)) {
2963 if (c != 27)
2964 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
2965 dot = char_insert(dot, c); // insert new char
2966 }
2967 goto dc1;
2968 }
2969 }
2970 if (cmd_mode == 1) {
2971 // hitting "Insert" twice means "R" replace mode
2972 if (c == VI_K_INSERT) goto dc5;
2973 // insert the char c at "dot"
2974 if (1 <= c || Isprint(c)) {
2975 dot = char_insert(dot, c);
2976 }
2977 goto dc1;
2978 }
2979
2980key_cmd_mode:
2981 switch (c) {
2982 //case 0x01: // soh
2983 //case 0x09: // ht
2984 //case 0x0b: // vt
2985 //case 0x0e: // so
2986 //case 0x0f: // si
2987 //case 0x10: // dle
2988 //case 0x11: // dc1
2989 //case 0x13: // dc3
2990#ifdef CONFIG_FEATURE_VI_CRASHME
2991 case 0x14: // dc4 ctrl-T
2992 crashme = (crashme == 0) ? 1 : 0;
2993 break;
2994#endif /* CONFIG_FEATURE_VI_CRASHME */
2995 //case 0x16: // syn
2996 //case 0x17: // etb
2997 //case 0x18: // can
2998 //case 0x1c: // fs
2999 //case 0x1d: // gs
3000 //case 0x1e: // rs
3001 //case 0x1f: // us
3002 //case '!': // !-
3003 //case '#': // #-
3004 //case '&': // &-
3005 //case '(': // (-
3006 //case ')': // )-
3007 //case '*': // *-
3008 //case ',': // ,-
3009 //case '=': // =-
3010 //case '@': // @-
3011 //case 'F': // F-
3012 //case 'K': // K-
3013 //case 'Q': // Q-
3014 //case 'S': // S-
3015 //case 'T': // T-
3016 //case 'V': // V-
3017 //case '[': // [-
3018 //case '\\': // \-
3019 //case ']': // ]-
3020 //case '_': // _-
3021 //case '`': // `-
3022 //case 'g': // g-
3023 //case 'u': // u- FIXME- there is no undo
3024 //case 'v': // v-
3025 default: // unrecognised command
3026 buf[0] = c;
3027 buf[1] = '\0';
3028 if (c < ' ') {
3029 buf[0] = '^';
3030 buf[1] = c + '@';
3031 buf[2] = '\0';
3032 }
3033 ni((Byte *) buf);
3034 end_cmd_q(); // stop adding to q
3035 case 0x00: // nul- ignore
3036 break;
3037 case 2: // ctrl-B scroll up full screen
3038 case VI_K_PAGEUP: // Cursor Key Page Up
3039 dot_scroll(rows - 2, -1);
3040 break;
3041#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
3042 case 0x03: // ctrl-C interrupt
3043 longjmp(restart, 1);
3044 break;
3045 case 26: // ctrl-Z suspend
3046 suspend_sig(SIGTSTP);
3047 break;
3048#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
3049 case 4: // ctrl-D scroll down half screen
3050 dot_scroll((rows - 2) / 2, 1);
3051 break;
3052 case 5: // ctrl-E scroll down one line
3053 dot_scroll(1, 1);
3054 break;
3055 case 6: // ctrl-F scroll down full screen
3056 case VI_K_PAGEDOWN: // Cursor Key Page Down
3057 dot_scroll(rows - 2, 1);
3058 break;
3059 case 7: // ctrl-G show current status
3060 edit_status();
3061 break;
3062 case 'h': // h- move left
3063 case VI_K_LEFT: // cursor key Left
3064 case 8: // ctrl-H- move left (This may be ERASE char)
3065 case 127: // DEL- move left (This may be ERASE char)
3066 if (cmdcnt-- > 1) {
3067 do_cmd(c);
3068 } // repeat cnt
3069 dot_left();
3070 break;
3071 case 10: // Newline ^J
3072 case 'j': // j- goto next line, same col
3073 case VI_K_DOWN: // cursor key Down
3074 if (cmdcnt-- > 1) {
3075 do_cmd(c);
3076 } // repeat cnt
3077 dot_next(); // go to next B-o-l
3078 dot = move_to_col(dot, ccol + offset); // try stay in same col
3079 break;
3080 case 12: // ctrl-L force redraw whole screen
3081 case 18: // ctrl-R force redraw
3082 place_cursor(0, 0, FALSE); // put cursor in correct place
3083 clear_to_eos(); // tel terminal to erase display
3084 (void) mysleep(10);
3085 screen_erase(); // erase the internal screen buffer
3086 refresh(TRUE); // this will redraw the entire display
3087 break;
3088 case 13: // Carriage Return ^M
3089 case '+': // +- goto next line
3090 if (cmdcnt-- > 1) {
3091 do_cmd(c);
3092 } // repeat cnt
3093 dot_next();
3094 dot_skip_over_ws();
3095 break;
3096 case 21: // ctrl-U scroll up half screen
3097 dot_scroll((rows - 2) / 2, -1);
3098 break;
3099 case 25: // ctrl-Y scroll up one line
3100 dot_scroll(1, -1);
3101 break;
3102 case 27: // esc
3103 if (cmd_mode == 0)
3104 indicate_error(c);
3105 cmd_mode = 0; // stop insrting
3106 end_cmd_q();
3107 *status_buffer = '\0'; // clear status buffer
3108 break;
3109 case ' ': // move right
3110 case 'l': // move right
3111 case VI_K_RIGHT: // Cursor Key Right
3112 if (cmdcnt-- > 1) {
3113 do_cmd(c);
3114 } // repeat cnt
3115 dot_right();
3116 break;
3117#ifdef CONFIG_FEATURE_VI_YANKMARK
3118 case '"': // "- name a register to use for Delete/Yank
3119 c1 = get_one_char();
3120 c1 = tolower(c1);
3121 if (islower(c1)) {
3122 YDreg = c1 - 'a';
3123 } else {
3124 indicate_error(c);
3125 }
3126 break;
3127 case '\'': // '- goto a specific mark
3128 c1 = get_one_char();
3129 c1 = tolower(c1);
3130 if (islower(c1)) {
3131 c1 = c1 - 'a';
3132 // get the b-o-l
3133 q = mark[(int) c1];
3134 if (text <= q && q < end) {
3135 dot = q;
3136 dot_begin(); // go to B-o-l
3137 dot_skip_over_ws();
3138 }
3139 } else if (c1 == '\'') { // goto previous context
3140 dot = swap_context(dot); // swap current and previous context
3141 dot_begin(); // go to B-o-l
3142 dot_skip_over_ws();
3143 } else {
3144 indicate_error(c);
3145 }
3146 break;
3147 case 'm': // m- Mark a line
3148 // this is really stupid. If there are any inserts or deletes
3149 // between text[0] and dot then this mark will not point to the
3150 // correct location! It could be off by many lines!
3151 // Well..., at least its quick and dirty.
3152 c1 = get_one_char();
3153 c1 = tolower(c1);
3154 if (islower(c1)) {
3155 c1 = c1 - 'a';
3156 // remember the line
3157 mark[(int) c1] = dot;
3158 } else {
3159 indicate_error(c);
3160 }
3161 break;
3162 case 'P': // P- Put register before
3163 case 'p': // p- put register after
3164 p = reg[YDreg];
3165 if (p == 0) {
3166 psbs("Nothing in register %c", what_reg());
3167 break;
3168 }
3169 // are we putting whole lines or strings
3170 if (strchr((char *) p, '\n') != NULL) {
3171 if (c == 'P') {
3172 dot_begin(); // putting lines- Put above
3173 }
3174 if (c == 'p') {
3175 // are we putting after very last line?
3176 if (end_line(dot) == (end - 1)) {
3177 dot = end; // force dot to end of text[]
3178 } else {
3179 dot_next(); // next line, then put before
3180 }
3181 }
3182 } else {
3183 if (c == 'p')
3184 dot_right(); // move to right, can move to NL
3185 }
3186 dot = string_insert(dot, p); // insert the string
3187 end_cmd_q(); // stop adding to q
3188 break;
3189 case 'U': // U- Undo; replace current line with original version
3190 if (reg[Ureg] != 0) {
3191 p = begin_line(dot);
3192 q = end_line(dot);
3193 p = text_hole_delete(p, q); // delete cur line
3194 p = string_insert(p, reg[Ureg]); // insert orig line
3195 dot = p;
3196 dot_skip_over_ws();
3197 }
3198 break;
3199#endif /* CONFIG_FEATURE_VI_YANKMARK */
3200 case '$': // $- goto end of line
3201 case VI_K_END: // Cursor Key End
3202 if (cmdcnt-- > 1) {
3203 do_cmd(c);
3204 } // repeat cnt
3205 dot = end_line(dot);
3206 break;
3207 case '%': // %- find matching char of pair () [] {}
3208 for (q = dot; q < end && *q != '\n'; q++) {
3209 if (strchr("()[]{}", *q) != NULL) {
3210 // we found half of a pair
3211 p = find_pair(q, *q);
3212 if (p == NULL) {
3213 indicate_error(c);
3214 } else {
3215 dot = p;
3216 }
3217 break;
3218 }
3219 }
3220 if (*q == '\n')
3221 indicate_error(c);
3222 break;
3223 case 'f': // f- forward to a user specified char
3224 last_forward_char = get_one_char(); // get the search char
3225 //
3226 // dont separate these two commands. 'f' depends on ';'
3227 //
3228 //**** fall thru to ... 'i'
3229 case ';': // ;- look at rest of line for last forward char
3230 if (cmdcnt-- > 1) {
3231 do_cmd(';');
3232 } // repeat cnt
3233 if (last_forward_char == 0) break;
3234 q = dot + 1;
3235 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3236 q++;
3237 }
3238 if (*q == last_forward_char)
3239 dot = q;
3240 break;
3241 case '-': // -- goto prev line
3242 if (cmdcnt-- > 1) {
3243 do_cmd(c);
3244 } // repeat cnt
3245 dot_prev();
3246 dot_skip_over_ws();
3247 break;
3248#ifdef CONFIG_FEATURE_VI_DOT_CMD
3249 case '.': // .- repeat the last modifying command
3250 // Stuff the last_modifying_cmd back into stdin
3251 // and let it be re-executed.
3252 if (last_modifying_cmd != 0) {
3253 ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd);
3254 }
3255 break;
3256#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3257#ifdef CONFIG_FEATURE_VI_SEARCH
3258 case '?': // /- search for a pattern
3259 case '/': // /- search for a pattern
3260 buf[0] = c;
3261 buf[1] = '\0';
3262 q = get_input_line(buf); // get input line- use "status line"
3263 if (strlen((char *) q) == 1)
3264 goto dc3; // if no pat re-use old pat
3265 if (strlen((char *) q) > 1) { // new pat- save it and find
3266 // there is a new pat
3267 free(last_search_pattern);
3268 last_search_pattern = (Byte *) bb_xstrdup((char *) q);
3269 goto dc3; // now find the pattern
3270 }
3271 // user changed mind and erased the "/"- do nothing
3272 break;
3273 case 'N': // N- backward search for last pattern
3274 if (cmdcnt-- > 1) {
3275 do_cmd(c);
3276 } // repeat cnt
3277 dir = BACK; // assume BACKWARD search
3278 p = dot - 1;
3279 if (last_search_pattern[0] == '?') {
3280 dir = FORWARD;
3281 p = dot + 1;
3282 }
3283 goto dc4; // now search for pattern
3284 break;
3285 case 'n': // n- repeat search for last pattern
3286 // search rest of text[] starting at next char
3287 // if search fails return orignal "p" not the "p+1" address
3288 if (cmdcnt-- > 1) {
3289 do_cmd(c);
3290 } // repeat cnt
3291 dc3:
3292 if (last_search_pattern == 0) {
3293 msg = (Byte *) "No previous regular expression";
3294 goto dc2;
3295 }
3296 if (last_search_pattern[0] == '/') {
3297 dir = FORWARD; // assume FORWARD search
3298 p = dot + 1;
3299 }
3300 if (last_search_pattern[0] == '?') {
3301 dir = BACK;
3302 p = dot - 1;
3303 }
3304 dc4:
3305 q = char_search(p, last_search_pattern + 1, dir, FULL);
3306 if (q != NULL) {
3307 dot = q; // good search, update "dot"
3308 msg = (Byte *) "";
3309 goto dc2;
3310 }
3311 // no pattern found between "dot" and "end"- continue at top
3312 p = text;
3313 if (dir == BACK) {
3314 p = end - 1;
3315 }
3316 q = char_search(p, last_search_pattern + 1, dir, FULL);
3317 if (q != NULL) { // found something
3318 dot = q; // found new pattern- goto it
3319 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3320 if (dir == BACK) {
3321 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3322 }
3323 } else {
3324 msg = (Byte *) "Pattern not found";
3325 }
3326 dc2:
3327 psbs("%s", msg);
3328 break;
3329 case '{': // {- move backward paragraph
3330 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3331 if (q != NULL) { // found blank line
3332 dot = next_line(q); // move to next blank line
3333 }
3334 break;
3335 case '}': // }- move forward paragraph
3336 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3337 if (q != NULL) { // found blank line
3338 dot = next_line(q); // move to next blank line
3339 }
3340 break;
3341#endif /* CONFIG_FEATURE_VI_SEARCH */
3342 case '0': // 0- goto begining of line
3343 case '1': // 1-
3344 case '2': // 2-
3345 case '3': // 3-
3346 case '4': // 4-
3347 case '5': // 5-
3348 case '6': // 6-
3349 case '7': // 7-
3350 case '8': // 8-
3351 case '9': // 9-
3352 if (c == '0' && cmdcnt < 1) {
3353 dot_begin(); // this was a standalone zero
3354 } else {
3355 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3356 }
3357 break;
3358 case ':': // :- the colon mode commands
3359 p = get_input_line((Byte *) ":"); // get input line- use "status line"
3360#ifdef CONFIG_FEATURE_VI_COLON
3361 colon(p); // execute the command
3362#else /* CONFIG_FEATURE_VI_COLON */
3363 if (*p == ':')
3364 p++; // move past the ':'
3365 cnt = strlen((char *) p);
3366 if (cnt <= 0)
3367 break;
3368 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3369 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
3370 if (file_modified && p[1] != '!') {
3371 psbs("No write since last change (:quit! overrides)");
3372 } else {
3373 editing = 0;
3374 }
3375 } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
3376 strncasecmp((char *) p, "wq", cnt) == 0 ||
3377 strncasecmp((char *) p, "x", cnt) == 0) {
3378 cnt = file_write(cfn, text, end - 1);
3379 file_modified = FALSE;
3380 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
3381 if (p[0] == 'x' || p[1] == 'q') {
3382 editing = 0;
3383 }
3384 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
3385 edit_status(); // show current file status
3386 } else if (sscanf((char *) p, "%d", &j) > 0) {
3387 dot = find_line(j); // go to line # j
3388 dot_skip_over_ws();
3389 } else { // unrecognised cmd
3390 ni((Byte *) p);
3391 }
3392#endif /* CONFIG_FEATURE_VI_COLON */
3393 break;
3394 case '<': // <- Left shift something
3395 case '>': // >- Right shift something
3396 cnt = count_lines(text, dot); // remember what line we are on
3397 c1 = get_one_char(); // get the type of thing to delete
3398 find_range(&p, &q, c1);
3399 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3400 p = begin_line(p);
3401 q = end_line(q);
3402 i = count_lines(p, q); // # of lines we are shifting
3403 for ( ; i > 0; i--, p = next_line(p)) {
3404 if (c == '<') {
3405 // shift left- remove tab or 8 spaces
3406 if (*p == '\t') {
3407 // shrink buffer 1 char
3408 (void) text_hole_delete(p, p);
3409 } else if (*p == ' ') {
3410 // we should be calculating columns, not just SPACE
3411 for (j = 0; *p == ' ' && j < tabstop; j++) {
3412 (void) text_hole_delete(p, p);
3413 }
3414 }
3415 } else if (c == '>') {
3416 // shift right -- add tab or 8 spaces
3417 (void) char_insert(p, '\t');
3418 }
3419 }
3420 dot = find_line(cnt); // what line were we on
3421 dot_skip_over_ws();
3422 end_cmd_q(); // stop adding to q
3423 break;
3424 case 'A': // A- append at e-o-l
3425 dot_end(); // go to e-o-l
3426 //**** fall thru to ... 'a'
3427 case 'a': // a- append after current char
3428 if (*dot != '\n')
3429 dot++;
3430 goto dc_i;
3431 break;
3432 case 'B': // B- back a blank-delimited Word
3433 case 'E': // E- end of a blank-delimited word
3434 case 'W': // W- forward a blank-delimited word
3435 if (cmdcnt-- > 1) {
3436 do_cmd(c);
3437 } // repeat cnt
3438 dir = FORWARD;
3439 if (c == 'B')
3440 dir = BACK;
3441 if (c == 'W' || isspace(dot[dir])) {
3442 dot = skip_thing(dot, 1, dir, S_TO_WS);
3443 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3444 }
3445 if (c != 'W')
3446 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3447 break;
3448 case 'C': // C- Change to e-o-l
3449 case 'D': // D- delete to e-o-l
3450 save_dot = dot;
3451 dot = dollar_line(dot); // move to before NL
3452 // copy text into a register and delete
3453 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3454 if (c == 'C')
3455 goto dc_i; // start inserting
3456#ifdef CONFIG_FEATURE_VI_DOT_CMD
3457 if (c == 'D')
3458 end_cmd_q(); // stop adding to q
3459#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3460 break;
3461 case 'G': // G- goto to a line number (default= E-O-F)
3462 dot = end - 1; // assume E-O-F
3463 if (cmdcnt > 0) {
3464 dot = find_line(cmdcnt); // what line is #cmdcnt
3465 }
3466 dot_skip_over_ws();
3467 break;
3468 case 'H': // H- goto top line on screen
3469 dot = screenbegin;
3470 if (cmdcnt > (rows - 1)) {
3471 cmdcnt = (rows - 1);
3472 }
3473 if (cmdcnt-- > 1) {
3474 do_cmd('+');
3475 } // repeat cnt
3476 dot_skip_over_ws();
3477 break;
3478 case 'I': // I- insert before first non-blank
3479 dot_begin(); // 0
3480 dot_skip_over_ws();
3481 //**** fall thru to ... 'i'
3482 case 'i': // i- insert before current char
3483 case VI_K_INSERT: // Cursor Key Insert
3484 dc_i:
3485 cmd_mode = 1; // start insrting
3486 psb("-- Insert --");
3487 break;
3488 case 'J': // J- join current and next lines together
3489 if (cmdcnt-- > 2) {
3490 do_cmd(c);
3491 } // repeat cnt
3492 dot_end(); // move to NL
3493 if (dot < end - 1) { // make sure not last char in text[]
3494 *dot++ = ' '; // replace NL with space
3495 while (isblnk(*dot)) { // delete leading WS
3496 dot_delete();
3497 }
3498 }
3499 end_cmd_q(); // stop adding to q
3500 break;
3501 case 'L': // L- goto bottom line on screen
3502 dot = end_screen();
3503 if (cmdcnt > (rows - 1)) {
3504 cmdcnt = (rows - 1);
3505 }
3506 if (cmdcnt-- > 1) {
3507 do_cmd('-');
3508 } // repeat cnt
3509 dot_begin();
3510 dot_skip_over_ws();
3511 break;
3512 case 'M': // M- goto middle line on screen
3513 dot = screenbegin;
3514 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3515 dot = next_line(dot);
3516 break;
3517 case 'O': // O- open a empty line above
3518 // 0i\n ESC -i
3519 p = begin_line(dot);
3520 if (p[-1] == '\n') {
3521 dot_prev();
3522 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3523 dot_end();
3524 dot = char_insert(dot, '\n');
3525 } else {
3526 dot_begin(); // 0
3527 dot = char_insert(dot, '\n'); // i\n ESC
3528 dot_prev(); // -
3529 }
3530 goto dc_i;
3531 break;
3532 case 'R': // R- continuous Replace char
3533 dc5:
3534 cmd_mode = 2;
3535 psb("-- Replace --");
3536 break;
3537 case 'X': // X- delete char before dot
3538 case 'x': // x- delete the current char
3539 case 's': // s- substitute the current char
3540 if (cmdcnt-- > 1) {
3541 do_cmd(c);
3542 } // repeat cnt
3543 dir = 0;
3544 if (c == 'X')
3545 dir = -1;
3546 if (dot[dir] != '\n') {
3547 if (c == 'X')
3548 dot--; // delete prev char
3549 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3550 }
3551 if (c == 's')
3552 goto dc_i; // start insrting
3553 end_cmd_q(); // stop adding to q
3554 break;
3555 case 'Z': // Z- if modified, {write}; exit
3556 // ZZ means to save file (if necessary), then exit
3557 c1 = get_one_char();
3558 if (c1 != 'Z') {
3559 indicate_error(c);
3560 break;
3561 }
3562 if (file_modified
3563#ifdef CONFIG_FEATURE_VI_READONLY
3564 && ! vi_readonly
3565 && ! readonly
3566#endif /* CONFIG_FEATURE_VI_READONLY */
3567 ) {
3568 cnt = file_write(cfn, text, end - 1);
3569 if (cnt == (end - 1 - text + 1)) {
3570 editing = 0;
3571 }
3572 } else {
3573 editing = 0;
3574 }
3575 break;
3576 case '^': // ^- move to first non-blank on line
3577 dot_begin();
3578 dot_skip_over_ws();
3579 break;
3580 case 'b': // b- back a word
3581 case 'e': // e- end of word
3582 if (cmdcnt-- > 1) {
3583 do_cmd(c);
3584 } // repeat cnt
3585 dir = FORWARD;
3586 if (c == 'b')
3587 dir = BACK;
3588 if ((dot + dir) < text || (dot + dir) > end - 1)
3589 break;
3590 dot += dir;
3591 if (isspace(*dot)) {
3592 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3593 }
3594 if (isalnum(*dot) || *dot == '_') {
3595 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3596 } else if (ispunct(*dot)) {
3597 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3598 }
3599 break;
3600 case 'c': // c- change something
3601 case 'd': // d- delete something
3602#ifdef CONFIG_FEATURE_VI_YANKMARK
3603 case 'y': // y- yank something
3604 case 'Y': // Y- Yank a line
3605#endif /* CONFIG_FEATURE_VI_YANKMARK */
3606 yf = YANKDEL; // assume either "c" or "d"
3607#ifdef CONFIG_FEATURE_VI_YANKMARK
3608 if (c == 'y' || c == 'Y')
3609 yf = YANKONLY;
3610#endif /* CONFIG_FEATURE_VI_YANKMARK */
3611 c1 = 'y';
3612 if (c != 'Y')
3613 c1 = get_one_char(); // get the type of thing to delete
3614 find_range(&p, &q, c1);
3615 if (c1 == 27) { // ESC- user changed mind and wants out
3616 c = c1 = 27; // Escape- do nothing
3617 } else if (strchr("wW", c1)) {
3618 if (c == 'c') {
3619 // don't include trailing WS as part of word
3620 while (isblnk(*q)) {
3621 if (q <= text || q[-1] == '\n')
3622 break;
3623 q--;
3624 }
3625 }
3626 dot = yank_delete(p, q, 0, yf); // delete word
3627 } else if (strchr("^0bBeEft$", c1)) {
3628 // single line copy text into a register and delete
3629 dot = yank_delete(p, q, 0, yf); // delete word
3630 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
3631 // multiple line copy text into a register and delete
3632 dot = yank_delete(p, q, 1, yf); // delete lines
3633 if (c == 'c') {
3634 dot = char_insert(dot, '\n');
3635 // on the last line of file don't move to prev line
3636 if (dot != (end-1)) {
3637 dot_prev();
3638 }
3639 } else if (c == 'd') {
3640 dot_begin();
3641 dot_skip_over_ws();
3642 }
3643 } else {
3644 // could not recognize object
3645 c = c1 = 27; // error-
3646 indicate_error(c);
3647 }
3648 if (c1 != 27) {
3649 // if CHANGING, not deleting, start inserting after the delete
3650 if (c == 'c') {
3651 strcpy((char *) buf, "Change");
3652 goto dc_i; // start inserting
3653 }
3654 if (c == 'd') {
3655 strcpy((char *) buf, "Delete");
3656 }
3657#ifdef CONFIG_FEATURE_VI_YANKMARK
3658 if (c == 'y' || c == 'Y') {
3659 strcpy((char *) buf, "Yank");
3660 }
3661 p = reg[YDreg];
3662 q = p + strlen((char *) p);
3663 for (cnt = 0; p <= q; p++) {
3664 if (*p == '\n')
3665 cnt++;
3666 }
3667 psb("%s %d lines (%d chars) using [%c]",
3668 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
3669#endif /* CONFIG_FEATURE_VI_YANKMARK */
3670 end_cmd_q(); // stop adding to q
3671 }
3672 break;
3673 case 'k': // k- goto prev line, same col
3674 case VI_K_UP: // cursor key Up
3675 if (cmdcnt-- > 1) {
3676 do_cmd(c);
3677 } // repeat cnt
3678 dot_prev();
3679 dot = move_to_col(dot, ccol + offset); // try stay in same col
3680 break;
3681 case 'r': // r- replace the current char with user input
3682 c1 = get_one_char(); // get the replacement char
3683 if (*dot != '\n') {
3684 *dot = c1;
3685 file_modified = TRUE; // has the file been modified
3686 }
3687 end_cmd_q(); // stop adding to q
3688 break;
3689 case 't': // t- move to char prior to next x
3690 last_forward_char = get_one_char();
3691 do_cmd(';');
3692 if (*dot == last_forward_char)
3693 dot_left();
3694 last_forward_char= 0;
3695 break;
3696 case 'w': // w- forward a word
3697 if (cmdcnt-- > 1) {
3698 do_cmd(c);
3699 } // repeat cnt
3700 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3701 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3702 } else if (ispunct(*dot)) { // we are on PUNCT
3703 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3704 }
3705 if (dot < end - 1)
3706 dot++; // move over word
3707 if (isspace(*dot)) {
3708 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3709 }
3710 break;
3711 case 'z': // z-
3712 c1 = get_one_char(); // get the replacement char
3713 cnt = 0;
3714 if (c1 == '.')
3715 cnt = (rows - 2) / 2; // put dot at center
3716 if (c1 == '-')
3717 cnt = rows - 2; // put dot at bottom
3718 screenbegin = begin_line(dot); // start dot at top
3719 dot_scroll(cnt, -1);
3720 break;
3721 case '|': // |- move to column "cmdcnt"
3722 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3723 break;
3724 case '~': // ~- flip the case of letters a-z -> A-Z
3725 if (cmdcnt-- > 1) {
3726 do_cmd(c);
3727 } // repeat cnt
3728 if (islower(*dot)) {
3729 *dot = toupper(*dot);
3730 file_modified = TRUE; // has the file been modified
3731 } else if (isupper(*dot)) {
3732 *dot = tolower(*dot);
3733 file_modified = TRUE; // has the file been modified
3734 }
3735 dot_right();
3736 end_cmd_q(); // stop adding to q
3737 break;
3738 //----- The Cursor and Function Keys -----------------------------
3739 case VI_K_HOME: // Cursor Key Home
3740 dot_begin();
3741 break;
3742 // The Fn keys could point to do_macro which could translate them
3743 case VI_K_FUN1: // Function Key F1
3744 case VI_K_FUN2: // Function Key F2
3745 case VI_K_FUN3: // Function Key F3
3746 case VI_K_FUN4: // Function Key F4
3747 case VI_K_FUN5: // Function Key F5
3748 case VI_K_FUN6: // Function Key F6
3749 case VI_K_FUN7: // Function Key F7
3750 case VI_K_FUN8: // Function Key F8
3751 case VI_K_FUN9: // Function Key F9
3752 case VI_K_FUN10: // Function Key F10
3753 case VI_K_FUN11: // Function Key F11
3754 case VI_K_FUN12: // Function Key F12
3755 break;
3756 }
3757
3758 dc1:
3759 // if text[] just became empty, add back an empty line
3760 if (end == text) {
3761 (void) char_insert(text, '\n'); // start empty buf with dummy line
3762 dot = text;
3763 }
3764 // it is OK for dot to exactly equal to end, otherwise check dot validity
3765 if (dot != end) {
3766 dot = bound_dot(dot); // make sure "dot" is valid
3767 }
3768#ifdef CONFIG_FEATURE_VI_YANKMARK
3769 check_context(c); // update the current context
3770#endif /* CONFIG_FEATURE_VI_YANKMARK */
3771
3772 if (!isdigit(c))
3773 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3774 cnt = dot - begin_line(dot);
3775 // Try to stay off of the Newline
3776 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3777 dot--;
3778}
3779
3780#ifdef CONFIG_FEATURE_VI_CRASHME
3781static int totalcmds = 0;
3782static int Mp = 85; // Movement command Probability
3783static int Np = 90; // Non-movement command Probability
3784static int Dp = 96; // Delete command Probability
3785static int Ip = 97; // Insert command Probability
3786static int Yp = 98; // Yank command Probability
3787static int Pp = 99; // Put command Probability
3788static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3789char chars[20] = "\t012345 abcdABCD-=.$";
3790char *words[20] = { "this", "is", "a", "test",
3791 "broadcast", "the", "emergency", "of",
3792 "system", "quick", "brown", "fox",
3793 "jumped", "over", "lazy", "dogs",
3794 "back", "January", "Febuary", "March"
3795};
3796char *lines[20] = {
3797 "You should have received a copy of the GNU General Public License\n",
3798 "char c, cm, *cmd, *cmd1;\n",
3799 "generate a command by percentages\n",
3800 "Numbers may be typed as a prefix to some commands.\n",
3801 "Quit, discarding changes!\n",
3802 "Forced write, if permission originally not valid.\n",
3803 "In general, any ex or ed command (such as substitute or delete).\n",
3804 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3805 "Please get w/ me and I will go over it with you.\n",
3806 "The following is a list of scheduled, committed changes.\n",
3807 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3808 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3809 "Any question about transactions please contact Sterling Huxley.\n",
3810 "I will try to get back to you by Friday, December 31.\n",
3811 "This Change will be implemented on Friday.\n",
3812 "Let me know if you have problems accessing this;\n",
3813 "Sterling Huxley recently added you to the access list.\n",
3814 "Would you like to go to lunch?\n",
3815 "The last command will be automatically run.\n",
3816 "This is too much english for a computer geek.\n",
3817};
3818char *multilines[20] = {
3819 "You should have received a copy of the GNU General Public License\n",
3820 "char c, cm, *cmd, *cmd1;\n",
3821 "generate a command by percentages\n",
3822 "Numbers may be typed as a prefix to some commands.\n",
3823 "Quit, discarding changes!\n",
3824 "Forced write, if permission originally not valid.\n",
3825 "In general, any ex or ed command (such as substitute or delete).\n",
3826 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3827 "Please get w/ me and I will go over it with you.\n",
3828 "The following is a list of scheduled, committed changes.\n",
3829 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3830 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3831 "Any question about transactions please contact Sterling Huxley.\n",
3832 "I will try to get back to you by Friday, December 31.\n",
3833 "This Change will be implemented on Friday.\n",
3834 "Let me know if you have problems accessing this;\n",
3835 "Sterling Huxley recently added you to the access list.\n",
3836 "Would you like to go to lunch?\n",
3837 "The last command will be automatically run.\n",
3838 "This is too much english for a computer geek.\n",
3839};
3840
3841// create a random command to execute
3842static void crash_dummy()
3843{
3844 static int sleeptime; // how long to pause between commands
3845 char c, cm, *cmd, *cmd1;
3846 int i, cnt, thing, rbi, startrbi, percent;
3847
3848 // "dot" movement commands
3849 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3850
3851 // is there already a command running?
3852 if (readed_for_parse > 0)
3853 goto cd1;
3854 cd0:
3855 startrbi = rbi = 0;
3856 sleeptime = 0; // how long to pause between commands
3857 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3858 // generate a command by percentages
3859 percent = (int) lrand48() % 100; // get a number from 0-99
3860 if (percent < Mp) { // Movement commands
3861 // available commands
3862 cmd = cmd1;
3863 M++;
3864 } else if (percent < Np) { // non-movement commands
3865 cmd = "mz<>\'\""; // available commands
3866 N++;
3867 } else if (percent < Dp) { // Delete commands
3868 cmd = "dx"; // available commands
3869 D++;
3870 } else if (percent < Ip) { // Inset commands
3871 cmd = "iIaAsrJ"; // available commands
3872 I++;
3873 } else if (percent < Yp) { // Yank commands
3874 cmd = "yY"; // available commands
3875 Y++;
3876 } else if (percent < Pp) { // Put commands
3877 cmd = "pP"; // available commands
3878 P++;
3879 } else {
3880 // We do not know how to handle this command, try again
3881 U++;
3882 goto cd0;
3883 }
3884 // randomly pick one of the available cmds from "cmd[]"
3885 i = (int) lrand48() % strlen(cmd);
3886 cm = cmd[i];
3887 if (strchr(":\024", cm))
3888 goto cd0; // dont allow colon or ctrl-T commands
3889 readbuffer[rbi++] = cm; // put cmd into input buffer
3890
3891 // now we have the command-
3892 // there are 1, 2, and multi char commands
3893 // find out which and generate the rest of command as necessary
3894 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3895 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3896 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3897 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3898 }
3899 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3900 c = cmd1[thing];
3901 readbuffer[rbi++] = c; // add movement to input buffer
3902 }
3903 if (strchr("iIaAsc", cm)) { // multi-char commands
3904 if (cm == 'c') {
3905 // change some thing
3906 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3907 c = cmd1[thing];
3908 readbuffer[rbi++] = c; // add movement to input buffer
3909 }
3910 thing = (int) lrand48() % 4; // what thing to insert
3911 cnt = (int) lrand48() % 10; // how many to insert
3912 for (i = 0; i < cnt; i++) {
3913 if (thing == 0) { // insert chars
3914 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3915 } else if (thing == 1) { // insert words
3916 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3917 strcat((char *) readbuffer, " ");
3918 sleeptime = 0; // how fast to type
3919 } else if (thing == 2) { // insert lines
3920 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3921 sleeptime = 0; // how fast to type
3922 } else { // insert multi-lines
3923 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
3924 sleeptime = 0; // how fast to type
3925 }
3926 }
3927 strcat((char *) readbuffer, "\033");
3928 }
3929 readed_for_parse = strlen(readbuffer);
3930 cd1:
3931 totalcmds++;
3932 if (sleeptime > 0)
3933 (void) mysleep(sleeptime); // sleep 1/100 sec
3934}
3935
3936// test to see if there are any errors
3937static void crash_test()
3938{
3939 static time_t oldtim;
3940 time_t tim;
3941 char d[2], msg[BUFSIZ];
3942
3943 msg[0] = '\0';
3944 if (end < text) {
3945 strcat((char *) msg, "end<text ");
3946 }
3947 if (end > textend) {
3948 strcat((char *) msg, "end>textend ");
3949 }
3950 if (dot < text) {
3951 strcat((char *) msg, "dot<text ");
3952 }
3953 if (dot > end) {
3954 strcat((char *) msg, "dot>end ");
3955 }
3956 if (screenbegin < text) {
3957 strcat((char *) msg, "screenbegin<text ");
3958 }
3959 if (screenbegin > end - 1) {
3960 strcat((char *) msg, "screenbegin>end-1 ");
3961 }
3962
3963 if (strlen(msg) > 0) {
3964 alarm(0);
3965 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
3966 totalcmds, last_input_char, msg, SOs, SOn);
3967 fflush(stdout);
3968 while (read(0, d, 1) > 0) {
3969 if (d[0] == '\n' || d[0] == '\r')
3970 break;
3971 }
3972 alarm(3);
3973 }
3974 tim = (time_t) time((time_t *) 0);
3975 if (tim >= (oldtim + 3)) {
3976 sprintf((char *) status_buffer,
3977 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
3978 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
3979 oldtim = tim;
3980 }
3981 return;
3982}
3983#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 @@
1Building a Busybox Boot Floppy
2==============================
3
4This document describes how to buid a boot floppy using the following
5components:
6
7 - Linux Kernel (http://www.kernel.org)
8 - uClibc: C library (http://cvs.uclinux.org/uClibc.html)
9 - Busybox: Unix utilities (http://busybox.net/)
10 - Syslinux: bootloader (http://syslinux.zytor.com)
11
12It is based heavily on a paper presented by Erik Andersen at the 2001 Embedded
13Systems Conference.
14
15
16
17Building The Software Components
18--------------------------------
19
20Detailed instructions on how to build Busybox, uClibc, or a working Linux
21kernel are beyond the scope of this document. The following guidelines will
22help though:
23
24 - Stock Busybox from CVS or a tarball will work with no modifications to
25 any files. Just extract and go.
26 - Ditto uClibc.
27 - Your Linux kernel must include support for initrd or else the floppy
28 won't be able to mount it's root file system.
29
30If you require further information on building Busybox uClibc or Linux, please
31refer to the web pages and documentation for those individual programs.
32
33
34
35Making a Root File System
36-------------------------
37
38The following steps will create a root file system.
39
40 - Create an empty file that you can format as a filesystem:
41
42 dd if=/dev/zero of=rootfs bs=1k count=4000
43
44 - Set up the rootfs file we just created to be used as a loop device (may not
45 be necessary)
46
47 losetup /dev/loop0 rootfs
48
49 - Format the rootfs file with a filesystem:
50
51 mkfs.ext2 -F -i 2000 rootfs
52
53 - Mount the file on a mountpoint so we can place files in it:
54
55 mkdir loop
56 mount -o loop rootfs loop/
57
58 (you will probably need to be root to do this)
59
60 - Copy on the C library, the dynamic linking library, and other necessary
61 libraries. For this example, we copy the following files from the uClibc
62 tree:
63
64 mkdir loop/lib
65 (chdir to uClibc directory)
66 cp -a libc.so* uClibc*.so \
67 ld.so-1/d-link/ld-linux-uclibc.so* \
68 ld.so-1/libdl/libdl.so* \
69 crypt/libcrypt.so* \
70 (path to)loop/lib
71
72 - Install the Busybox binary and accompanying symlinks:
73
74 (chdir to busybox directory)
75 make PREFIX=(path to)loop/ install
76
77 - Make device files in /dev:
78
79 This can be done by running the 'mkdevs.sh' script. If you want the gory
80 details, you can read the script.
81
82 - Make necessary files in /etc:
83
84 For this, just cp -a the etc/ directory onto rootfs. Again, if you want
85 all the details, you can just look at the files in the dir.
86
87 - Unmount the rootfs from the mountpoint:
88
89 umount loop
90
91 - Compress it:
92
93 gzip -9 rootfs
94
95
96Making a SYSLINUX boot floppy
97-----------------------------
98
99The following steps will create the boot floppy.
100
101Note: You will need to have the mtools package installed beforehand.
102
103 - Insert a floppy in the drive and format it with an MSDOS filesystem:
104
105 mformat a:
106
107 (if the system doesn't know what device 'a:' is, look at /etc/mtools.conf)
108
109 - Run syslinux on the floppy:
110
111 syslinux -s /dev/fd0
112
113 (the -s stands for "safe, slow, and stupid" and should work better with
114 buggy BIOSes; it can be omitted)
115
116 - Put on a syslinux.cfg file:
117
118 mcopy syslinux.cfg a:
119
120 (more on syslinux.cfg below)
121
122 - Copy the root file system you made onto the MSDOS formatted floppy
123
124 mcopy rootfs.gz a:
125
126 - Build a linux kernel and copy it onto the disk with the filename 'linux'
127
128 mcopy bzImage a:linux
129
130
131Sample syslinux.cfg
132~~~~~~~~~~~~~~~~~~~
133
134The following simple syslinux.cfg file should work. You can tweak it if you
135like.
136
137----begin-syslinux.cfg---------------
138DEFAULT linux
139APPEND initrd=rootfs.gz root=/dev/ram0
140TIMEOUT 10
141PROMPT 1
142----end-syslinux.cfg---------------
143
144Some changes you could make to syslinux.cfg:
145
146 - This value is the number seconds it will wait before booting. You can set
147 the timeout to 0 (or omit) to boot instantly, or you can set it as high as
148 10 to wait awhile.
149
150 - PROMPT can be set to 0 to disable the 'boot:' prompt.
151
152 - you can add this line to display the contents of a file as a welcome
153 message:
154
155 DISPLAY display.txt
156
157
158
159Additional Resources
160--------------------
161
162Other useful information on making a Linux bootfloppy is available at the
163following URLs:
164
165http://www.linuxdoc.org/HOWTO/Bootdisk-HOWTO/index.html
166http://www.linux-embedded.com/howto/Embedded-Linux-Howto.html
167http://linux-embedded.org/howto/LFS-HOWTO.html
168http://linux-embedded.org/pmhowto.html
169http://recycle.lbl.gov/~ldoolitt/embedded/ (Larry Doolittle's stuff)
170
171
172
173Possible TODOs
174--------------
175
176The following features that we might want to add later:
177
178 - support for additional filesystems besides ext2, i.e. minix
179 - different libc, static vs dynamic loading
180 - 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 @@
1
2This boot floppy is made with Busybox, uClibc, and the Linux kernel.
3Hit RETURN to boot or enter boot parameters at the prompt below.
4
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 @@
1proc /proc proc defaults 0 0
2
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 @@
1#! /bin/sh
2
3/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 @@
1::sysinit:/etc/init.d/rcS
2::respawn:-/bin/sh
3tty2::askfirst:-/bin/sh
4::ctrlaltdel:/bin/umount -a -r
5
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 @@
1# /etc/profile: system-wide .profile file for the Bourne shells
2
3echo
4echo -n "Processing /etc/profile... "
5# no-op
6echo "Done"
7echo
8
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 @@
1#!/bin/sh
2#
3# makedev.sh - creates device files for a busybox boot floppy image
4
5
6# we do our work in the dev/ directory
7if [ -z "$1" ]; then
8 echo "usage: `basename $0` path/to/dev/dir"
9 exit 1
10fi
11
12cd $1
13
14
15# miscellaneous one-of-a-kind stuff
16mknod console c 5 1
17mknod full c 1 7
18mknod kmem c 1 2
19mknod mem c 1 1
20mknod null c 1 3
21mknod port c 1 4
22mknod random c 1 8
23mknod urandom c 1 9
24mknod zero c 1 5
25ln -s /proc/kcore core
26
27# IDE HD devs
28# note: not going to bother creating all concievable partitions; you can do
29# that yourself as you need 'em.
30mknod hda b 3 0
31mknod hdb b 3 64
32mknod hdc b 22 0
33mknod hdd b 22 64
34
35# loop devs
36for i in `seq 0 7`; do
37 mknod loop$i b 7 $i
38done
39
40# ram devs
41for i in `seq 0 9`; do
42 mknod ram$i b 1 $i
43done
44ln -s ram1 ram
45
46# ttys
47mknod tty c 5 0
48for i in `seq 0 9`; do
49 mknod tty$i c 4 $i
50done
51
52# virtual console screen devs
53for i in `seq 0 9`; do
54 mknod vcs$i b 7 $i
55done
56ln -s vcs0 vcs
57
58# virtual console screen w/ attributes devs
59for i in `seq 0 9`; do
60 mknod vcsa$i b 7 $i
61done
62ln -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 @@
1#!/bin/bash
2#
3# mkrootfs.sh - creates a root file system
4#
5
6# TODO: need to add checks here to verify that busybox, uClibc and bzImage
7# exist
8
9
10# command-line settable variables
11BUSYBOX_DIR=..
12UCLIBC_DIR=../../uClibc
13TARGET_DIR=./loop
14FSSIZE=4000
15CLEANUP=1
16MKFS='mkfs.ext2 -F'
17
18# don't-touch variables
19BASE_DIR=`pwd`
20
21
22while getopts 'b:u:s:t:Cm' opt
23do
24 case $opt in
25 b) BUSYBOX_DIR=$OPTARG ;;
26 u) UCLIBC_DIR=$OPTARG ;;
27 t) TARGET_DIR=$OPTARG ;;
28 s) FSSIZE=$OPTARG ;;
29 C) CLEANUP=0 ;;
30 m) MKFS='mkfs.minix' ;;
31 *)
32 echo "usage: `basename $0` [-bu]"
33 echo " -b DIR path to busybox direcory (default ..)"
34 echo " -u DIR path to uClibc direcory (default ../../uClibc)"
35 echo " -t DIR path to target direcory (default ./loop)"
36 echo " -s SIZE size of root filesystem in Kbytes (default 4000)"
37 echo " -C don't perform cleanup (umount target dir, gzip rootfs, etc.)"
38 echo " (this allows you to 'chroot loop/ /bin/sh' to test it)"
39 echo " -m use minix filesystem (default is ext2)"
40 exit 1
41 ;;
42 esac
43done
44
45
46
47
48# clean up from any previous work
49mount | grep -q loop
50[ $? -eq 0 ] && umount $TARGET_DIR
51[ -d $TARGET_DIR ] && rm -rf $TARGET_DIR/
52[ -f rootfs ] && rm -f rootfs
53[ -f rootfs.gz ] && rm -f rootfs.gz
54
55
56# prepare root file system and mount as loopback
57dd if=/dev/zero of=rootfs bs=1k count=$FSSIZE
58$MKFS -i 2000 rootfs
59mkdir $TARGET_DIR
60mount -o loop,exec rootfs $TARGET_DIR # must be root
61
62
63# install uClibc
64mkdir -p $TARGET_DIR/lib
65cd $UCLIBC_DIR
66make INSTALL_DIR=
67cp -a libc.so* $BASE_DIR/$TARGET_DIR/lib
68cp -a uClibc*.so $BASE_DIR/$TARGET_DIR/lib
69cp -a ld.so-1/d-link/ld-linux-uclibc.so* $BASE_DIR/$TARGET_DIR/lib
70cp -a ld.so-1/libdl/libdl.so* $BASE_DIR/$TARGET_DIR/lib
71cp -a crypt/libcrypt.so* $BASE_DIR/$TARGET_DIR/lib
72cd $BASE_DIR
73
74
75# install busybox and components
76cd $BUSYBOX_DIR
77make distclean
78make CC=$BASE_DIR/$UCLIBC_DIR/extra/gcc-uClibc/i386-uclibc-gcc
79make PREFIX=$BASE_DIR/$TARGET_DIR install
80cd $BASE_DIR
81
82
83# make files in /dev
84mkdir $TARGET_DIR/dev
85./mkdevs.sh $TARGET_DIR/dev
86
87
88# make files in /etc
89cp -a etc $TARGET_DIR
90ln -s /proc/mounts $TARGET_DIR/etc/mtab
91
92
93# other miscellaneous setup
94mkdir $TARGET_DIR/initrd
95mkdir $TARGET_DIR/proc
96
97
98# Done. Maybe do cleanup.
99if [ $CLEANUP -eq 1 ]
100then
101 umount $TARGET_DIR
102 rmdir $TARGET_DIR
103 gzip -9 rootfs
104fi
105
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 @@
1#!/bin/bash
2#
3# Formats a floppy to use Syslinux
4
5dummy=""
6
7
8# need to have mtools installed
9if [ -z `which mformat` -o -z `which mcopy` ]; then
10 echo "You must have the mtools package installed to run this script"
11 exit 1
12fi
13
14
15# need an arg for the location of the kernel
16if [ -z "$1" ]; then
17 echo "usage: `basename $0` path/to/linux/kernel"
18 exit 1
19fi
20
21
22# need to have a root file system built
23if [ ! -f rootfs.gz ]; then
24 echo "You need to have a rootfs built first."
25 echo "Hit RETURN to make one now or Control-C to quit."
26 read dummy
27 ./mkrootfs.sh
28fi
29
30
31# prepare the floppy
32echo "Please insert a blank floppy in the drive and press RETURN to format"
33echo "(WARNING: All data will be erased! Hit Control-C to abort)"
34read dummy
35
36echo "Formatting the floppy..."
37mformat a:
38echo "Making it bootable with Syslinux..."
39syslinux -s /dev/fd0
40echo "Copying Syslinux configuration files..."
41mcopy syslinux.cfg display.txt a:
42echo "Copying root filesystem file..."
43mcopy rootfs.gz a:
44# XXX: maybe check for "no space on device" errors here
45echo "Copying linux kernel..."
46mcopy $1 a:linux
47# XXX: maybe check for "no space on device" errors here too
48echo "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 @@
1Quickstart on making the Busybox boot-floppy:
2
3 1) Download Busybox and uClibc from CVS or tarballs. Make sure they share a
4 common parent directory. (i.e. busybox/ and uclibc/ are both right off of
5 /tmp, or wherever.)
6
7 2) Build a Linux kernel. Make sure you include support for initrd.
8
9 3) Put a floppy in the drive. Make sure it is a floppy you don't care about
10 because the contents will be overwritten.
11
12 4) As root, type ./mksyslinux.sh path/to/linux/kernel from this directory.
13 Wait patiently while the magic happens.
14
15 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 @@
1display display.txt
2default linux
3timeout 10
4prompt 1
5label linux
6 kernel linux
7 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 @@
1%define name busybox
2%define epoch 0
3%define version 0.61.pre
4%define release %(date -I | sed -e 's/-/_/g')
5%define serial 1
6
7Name: %{name}
8#Epoch: %{epoch}
9Version: %{version}
10Release: %{release}
11Serial: %{serial}
12Copyright: GPL
13Group: System/Utilities
14Summary: BusyBox is a tiny suite of Unix utilities in a multi-call binary.
15URL: http://busybox.net/
16Source: ftp://busybox.net/busybox/%{name}-%{version}.tar.gz
17Buildroot: /var/tmp/%{name}-%{version}
18Packager : Erik Andersen <andersen@codepoet.org>
19
20%Description
21BusyBox combines tiny versions of many common UNIX utilities into a single
22small executable. It provides minimalist replacements for most of the utilities
23you usually find in fileutils, shellutils, findutils, textutils, grep, gzip,
24tar, etc. BusyBox provides a fairly complete POSIX environment for any small
25or emdedded system. The utilities in BusyBox generally have fewer options then
26their full featured GNU cousins; however, the options that are provided behave
27very much like their GNU counterparts.
28
29%Prep
30%setup -q -n %{name}-%{version}
31
32%Build
33make
34
35%Install
36rm -rf $RPM_BUILD_ROOT
37make PREFIX=$RPM_BUILD_ROOT install
38
39%Clean
40rm -rf $RPM_BUILD_ROOT
41
42%Files
43%defattr(-,root,root)
44/
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 @@
1#!/usr/bin/perl -w
2# vi: set ts=4:
3# Copyright (c) 2001 David Schleef <ds@schleef.org>
4# Copyright (c) 2001 Erik Andersen <andersen@codepoet.org>
5# Copyright (c) 2001 Stuart Hughes <stuarth@lineo.com>
6# Copyright (c) 2002 Steven J. Hill <shill@broadcom.com>
7# This program is free software; you can redistribute it and/or modify it
8# under the same terms as Perl itself.
9
10# TODO -- use strict mode...
11#use strict;
12
13use Getopt::Long;
14use File::Find;
15
16
17# Set up some default values
18
19my $basedir="";
20my $kernel;
21my $kernelsyms;
22my $stdout=0;
23my $verbose=0;
24
25
26# get command-line options
27
28my %opt;
29
30GetOptions(
31 \%opt,
32 "help|h",
33 "basedir|b=s" => \$basedir,
34 "kernel|k=s" => \$kernel,
35 "kernelsyms|F=s" => \$kernelsyms,
36 "stdout|n" => \$stdout,
37 "verbose|v" => \$verbose,
38);
39
40if (defined $opt{help}) {
41 print
42 " $0 [OPTION]... [basedir]\n",
43 " -h --help\t\tShow this help screen\n",
44 " -b --basedir\tModules base directory (defaults to /lib/modules)\n",
45 " -k --kernel\tKernel binary for the target\n",
46 " -F --kernelsyms\tKernel symbol file\n",
47 " -n --stdout\tWrite to stdout instead of <basedir>/modules.dep\n",
48 " -v --verbose\tPrint out lots of debugging stuff\n",
49 ;
50 exit 1;
51}
52
53if($basedir !~ m-/lib/modules-) {
54 warn "WARNING: base directory does not match ..../lib/modules\n";
55}
56
57# Find the list of .o files living under $basedir
58#if ($verbose) { printf "Locating all modules\n"; }
59my($ofile) = "";
60my($file) = "";
61my(@liblist) = ();
62find sub {
63 if ( -f $_ && ! -d $_ ) {
64 $file = $File::Find::name;
65 if ( $file =~ /.o$/ ) {
66 push(@liblist, $file);
67 if ($verbose) { printf "$file\n"; }
68 }
69 }
70}, $basedir;
71if ($verbose) { printf "Finished locating modules\n"; }
72
73foreach $obj ( @liblist, $kernel ){
74 # turn the input file name into a target tag name
75 # vmlinux is a special that is only used to resolve symbols
76 if($obj =~ /vmlinux/) {
77 $tgtname = "vmlinux";
78 } else {
79 ($tgtname) = $obj =~ m-(/lib/modules/.*)$-;
80 }
81
82 warn "MODULE = $tgtname\n" if $verbose;
83
84 # get a list of symbols
85 @output=`nm $obj`;
86 $ksymtab=grep m/ __ksymtab/, @output;
87
88 # gather the exported symbols
89 if($ksymtab){
90 # explicitly exported
91 foreach ( @output ) {
92 / __ksymtab_(.*)$/ and do {
93 warn "sym = $1\n" if $verbose;
94 $exp->{$1} = $tgtname;
95 };
96 }
97 } else {
98 # exporting all symbols
99 foreach ( @output) {
100 / [ABCDGRST] (.*)$/ and do {
101 warn "syma = $1\n" if $verbose;
102 $exp->{$1} = $tgtname;
103 };
104 }
105 }
106 # gather the unresolved symbols
107 foreach ( @output ) {
108 !/ __this_module/ && / U (.*)$/ and do {
109 warn "und = $1\n" if $verbose;
110 push @{$dep->{$tgtname}}, $1;
111 };
112 }
113}
114
115
116# reduce dependancies: remove unresolvable and resolved from vmlinux
117# remove duplicates
118foreach $module (keys %$dep) {
119 $mod->{$module} = {};
120 foreach (@{$dep->{$module}}) {
121 if( $exp->{$_} ) {
122 warn "resolved symbol $_ in file $exp->{$_}\n" if $verbose;
123 next if $exp->{$_} =~ /vmlinux/;
124 $mod->{$module}{$exp->{$_}} = 1;
125 } else {
126 warn "unresolved symbol $_ in file $module\n";
127 }
128 }
129}
130
131# resolve the dependancies for each module
132if ($stdout == 1) {
133 foreach $module ( keys %$mod ) {
134 print "$module:\t";
135 @sorted = sort bydep keys %{$mod->{$module}};
136 print join(" \\\n\t",@sorted);
137 print "\n\n";
138 }
139} else {
140 open(OFILE, ">$basedir/modules.dep");
141 foreach $module ( keys %$mod ) {
142 print OFILE "$module:\t";
143 @sorted = sort bydep keys %{$mod->{$module}};
144 print OFILE join(" \\\n\t",@sorted);
145 print OFILE "\n\n";
146 }
147}
148
149
150sub bydep
151{
152 foreach my $f ( keys %{$mod->{$b}} ) {
153 if($f eq $a) {
154 return 1;
155 }
156 }
157 return -1;
158}
159
160
161
162__END__
163
164=head1 NAME
165
166depmod.pl - a cross platform script to generate kernel module dependency
167 lists which can then be used by modprobe.
168
169=head1 SYNOPSIS
170
171depmod.pl [OPTION]... [basedir]...
172
173Example:
174
175 depmod.pl -F linux/System.map target/lib/modules
176
177=head1 DESCRIPTION
178
179The purpose of this script is to automagically generate a list of of kernel
180module dependancies. This script produces dependancy lists that should be
181identical to the depmod program from the modutils package. Unlike the depmod
182binary, however, depmod.pl is designed to be run on your host system, not
183on your target system.
184
185This script was written by David Schleef <ds@schleef.org> to be used in
186conjunction with the BusyBox modprobe applet.
187
188=head1 OPTIONS
189
190=over 4
191
192=item B<-h --help>
193
194This displays the help message.
195
196=item B<-b --basedir>
197
198The base directory uner which the target's modules will be found. This
199defaults to the /lib/modules directory.
200
201=item B<-k --kernel>
202
203Kernel binary for the target. You must either supply a kernel binary
204or a kernel symbol file (using the -F option).
205
206=item B<-F --kernelsyms>
207
208Kernel symbol file for the target. You must supply either a kernel symbol file
209kernel binary for the target (using the -k option).
210
211=item B<-n --stdout>
212
213Write to stdout instead of modules.dep. This is currently hard coded...
214kernel binary for the target (using the -k option).
215
216=item B<--verbose>
217
218Be verbose (not implemented)
219
220=back
221
222=head1 COPYRIGHT
223
224Copyright (c) 2001 David Schleef <ds@schleef.org>
225Copyright (c) 2001 Erik Andersen <andersen@codepoet.org>
226Copyright (c) 2001 Stuart Hughes <stuarth@lineo.com>
227This program is free software; you can redistribute it and/or modify it
228under the same terms as Perl itself.
229
230=head1 AUTHOR
231
232David Schleef <ds@schleef.org>
233
234=cut
235
236# $Id: depmod.pl,v 1.4 2004/03/15 08:28:33 andersen Exp $
237
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 @@
1# Sample /etc/devfsd.conf configuration file.
2# Richard Gooch <rgooch@atnf.csiro.au> 17-FEB-2002
3#
4# adapted for busybox devfsd implementation by Tito <farmatito@tiscali.it>
5#
6# Enable full compatibility mode for old device names. You may comment these
7# out if you don't use the old device names. Make sure you know what you're
8# doing!
9REGISTER .* MKOLDCOMPAT
10UNREGISTER .* RMOLDCOMPAT
11
12# You may comment out the above and uncomment the following if you've
13# configured your system to use the original "new" devfs names or the really
14# new names
15#REGISTER ^vc/ MKOLDCOMPAT
16#UNREGISTER ^vc/ RMOLDCOMPAT
17#REGISTER ^pty/ MKOLDCOMPAT
18#UNREGISTER ^pty/ RMOLDCOMPAT
19#REGISTER ^misc/ MKOLDCOMPAT
20#UNREGISTER ^misc/ RMOLDCOMPAT
21
22# You may comment these out if you don't use the original "new" names
23REGISTER .* MKNEWCOMPAT
24UNREGISTER .* RMNEWCOMPAT
25
26# Enable module autoloading. You may comment this out if you don't use
27# autoloading
28# Supported by busybox when CONFIG_DEVFSD_MODLOAD is set.
29# This actually doesn't work with busybox modutils but needs
30# the real modutils' modprobe
31LOOKUP .* MODLOAD
32
33# Uncomment the following if you want to set the group to "tty" for the
34# pseudo-tty devices. This is necessary so that mesg(1) can later be used to
35# enable/disable talk requests and wall(1) messages.
36REGISTER ^pty/s.* PERMISSIONS -1.tty 0600
37#REGISTER ^pts/.* PERMISSIONS -1.tty 0600
38
39# Restoring /dev/log on startup would trigger the minilogd/initlog deadlock
40# (minilogd falsely assuming syslogd has been started).
41REGISTER ^log$ IGNORE
42CREATE ^log$ IGNORE
43CHANGE ^log$ IGNORE
44DELETE ^log$ IGNORE
45
46#
47# Uncomment this if you want permissions to be saved and restored
48# Do not do this for pseudo-terminal devices
49REGISTER ^pt[sy] IGNORE
50CREATE ^pt[sy] IGNORE
51CHANGE ^pt[sy] IGNORE
52DELETE ^pt[sy] IGNORE
53REGISTER .* COPY /lib/dev-state/$devname $devpath
54CREATE .* COPY $devpath /lib/dev-state/$devname
55CHANGE .* COPY $devpath /lib/dev-state/$devname
56#DELETE .* CFUNCTION GLOBAL unlink /lib/dev-state/$devname
57# Busybox
58DELETE .* EXECUTE /bin/rm -f /lib/dev-state/$devname
59
60RESTORE /lib/dev-state
61
62#
63# Uncomment this if you want the old /dev/cdrom symlink
64#REGISTER ^cdroms/cdrom0$ CFUNCTION GLOBAL mksymlink $devname cdrom
65#UNREGISTER ^cdroms/cdrom0$ CFUNCTION GLOBAL unlink cdrom
66# busybox
67REGISTER ^cdroms/cdrom0$ EXECUTE /bin/ln -sf $devname cdrom
68UNREGISTER ^cdroms/cdrom0$ EXECUTE /bin/rm -f cdrom
69
70#REGISTER ^v4l/video0$ CFUNCTION GLOBAL mksymlink v4l/video0 video
71#UNREGISTER ^v4l/video0$ CFUNCTION GLOBAL unlink video
72#REGISTER ^radio0$ CFUNCTION GLOBAL mksymlink radio0 radio
73#UNREGISTER ^radio0$ CFUNCTION GLOBAL unlink radio
74# Busybox
75REGISTER ^v4l/video0$ EXECUTE /bin/ln -sf v4l/video0 video
76UNREGISTER ^v4l/video0$ EXECUTE /bin/rm -f video
77REGISTER ^radio0$ EXECUTE /bin/ln -sf radio0 radio
78UNREGISTER ^radio0$ EXECUTE /bin/rm -f radio
79
80# ALSA stuff
81#LOOKUP snd MODLOAD ACTION snd
82
83# Uncomment this to let PAM manage devfs
84# Not supported by busybox
85#REGISTER .* CFUNCTION /lib/security/pam_console_apply_devfsd.so pam_console_apply_single $devpath
86
87# Uncomment this to manage USB mouse
88# Not supported by busybox
89#REGISTER ^input/mouse0$ CFUNCTION GLOBAL mksymlink $devname usbmouse
90#UNREGISTER ^input/mouse0$ CFUNCTION GLOBAL unlink usbmouse
91# Busybox
92#REGISTER ^input/mouse0$ EXECUTE /bin/ln -sf $devname usbmouse
93#UNREGISTER ^input/mouse0$ EXECUTE /bin/rm -f usbmouse
94# Not supported by busybox
95#REGISTER ^input/mice$ CFUNCTION GLOBAL mksymlink $devname usbmouse
96#UNREGISTER ^input/mice$ CFUNCTION GLOBAL unlink usbmouse
97# Busybox
98REGISTER ^input/mice$ EXECUTE /bin/ln -sf $devname usbmouse
99UNREGISTER ^input/mice$ EXECUTE /bin/rm -f usbmouse
100
101# If you have removable media and want to force media revalidation when looking
102# up new or old compatibility names, uncomment the following lines
103# SCSI NEWCOMPAT /dev/sd/* names
104LOOKUP ^(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
105# SCSI OLDCOMPAT /dev/sd?? names
106LOOKUP ^(sd[a-z]+)[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
107# IDE NEWCOMPAT /dev/ide/hd/* names
108LOOKUP ^(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
109# IDE OLDCOMPAT /dev/hd?? names
110LOOKUP ^(hd[a-z])[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
111# IDE-SCSI NEWCOMPAT /dev/sd/* names
112#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
113#SCSI OLDCOMPAT /dev/scd? names
114LOOKUP ^(scd+)[0-9]+$ EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
115
116
117REGISTER ^dvb/card[0-9]+/[^/]+$ PERMISSIONS root.video 0660
118# Not supported by busybox
119#REGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ CFUNCTION GLOBAL mksymlink /dev/$devname ost/\2\1
120#UNREGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ CFUNCTION GLOBAL unlink ost/\2\1
121# Busybox
122REGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ EXECUTE /bin/ln -sf /dev/$devname ost/\2\1
123UNREGISTER ^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$ EXECUTE /bin/rm -f ost/\2\1
124
125# Include package-generated files from /etc/devfs/conf.d
126# Supported by busybox
127# INCLUDE /etc/devfs/conf.d/
128INCLUDE /etc/devfs/busybox/
129# Busybox: just for testing
130#INCLUDE /etc/devfs/nothing/
131#INCLUDE /etc/devfs/nothing/nothing
132#OPTIONAL_INCLUDE /etc/devfs/nothing/
133#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 @@
1# /etc/inetd.conf: see inetd(8) for further informations.
2#
3# Internet server configuration database
4#
5#
6# If you want to disable an entry so it isn't touched during
7# package updates just comment it out with a single '#' character.
8#
9# If you make changes to this file, either reboot your machine or
10# send the inetd process a HUP signal:
11# Do a "ps x" as root and look up the pid of inetd. Then do a
12# kill -HUP <pid of inetd>
13# inetd will re-read this file whenever it gets that signal.
14# <service_name> <sock_type> <proto> <flags> <user> <server_path> <args>
15#
16#:INTERNAL: Internal services
17# It is generally considered safer to keep these off.
18echo stream tcp nowait root internal
19echo dgram udp wait root internal
20#discard stream tcp nowait root internal
21#discard dgram udp wait root internal
22daytime stream tcp nowait root internal
23daytime dgram udp wait root internal
24#chargen stream tcp nowait root internal
25#chargen dgram udp wait root internal
26time stream tcp nowait root internal
27time dgram udp wait root internal
28
29# These are standard services.
30#
31#ftp stream tcp nowait root /usr/sbin/tcpd in.ftpd
32#telnet stream tcp nowait root /sbin/telnetd /sbin/telnetd
33#nntp stream tcp nowait root tcpd in.nntpd
34#smtp stream tcp nowait root tcpd sendmail -v
35#
36# Shell, login, exec and talk are BSD protocols.
37#
38# If you run an ntalk daemon (such as netkit-ntalk) on the old talk
39# port, that is, "talk" as opposed to "ntalk", it won't work and may
40# cause certain broken talk clients to malfunction.
41#
42# The talkd from netkit-ntalk 0.12 and higher, however, can speak the
43# old talk protocol and can be used safely.
44#
45#shell stream tcp nowait root /usr/sbin/tcpd in.rshd -L
46#login stream tcp nowait root /usr/sbin/tcpd in.rlogind -L
47#exec stream tcp nowait root /usr/sbin/tcpd in.rexecd
48#talk dgram udp wait root /usr/sbin/tcpd in.talkd
49#ntalk dgram udp wait root /usr/sbin/tcpd in.talkd
50#
51# Pop et al
52# Leave these off unless you're using them.
53#pop2 stream tcp nowait root /usr/sbin/tcpd in.pop2d
54#pop3 stream tcp nowait root /usr/sbin/tcpd in.pop3d
55#
56# The Internet UUCP service.
57# uucp stream tcp nowait uucp /usr/sbin/tcpd /usr/lib/uucp/uucico -l
58#
59# Tftp service is provided primarily for booting. Most sites
60# run this only on machines acting as "boot servers." If you don't
61# need it, don't use it.
62#
63#tftp dgram udp wait nobody /usr/sbin/tcpd in.tftpd
64#bootps dgram udp wait root /usr/sbin/in.bootpd in.bootpd
65#
66# Finger, systat and netstat give out user information which may be
67# valuable to potential "system crackers." Many sites choose to disable
68# some or all of these services to improve security.
69#
70#finger stream tcp nowait nobody /usr/sbin/tcpd in.fingerd -w
71#systat stream tcp nowait nobody /usr/sbin/tcpd /bin/ps -auwwx
72#netstat stream tcp nowait root /bin/netstat /bin/netstat -a
73#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 @@
1# /etc/inittab init(8) configuration for BusyBox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5#
6# Note, BusyBox init doesn't support runlevels. The runlevels field is
7# completely ignored by BusyBox init. If you want runlevels, use sysvinit.
8#
9#
10# Format for each entry: <id>:<runlevels>:<action>:<process>
11#
12# <id>: WARNING: This field has a non-traditional meaning for BusyBox init!
13#
14# The id field is used by BusyBox init to specify the controlling tty for
15# the specified process to run on. The contents of this field are
16# appended to "/dev/" and used as-is. There is no need for this field to
17# be unique, although if it isn't you may have strange results. If this
18# field is left blank, it is completely ignored. Also note that if
19# BusyBox detects that a serial console is in use, then all entries
20# containing non-empty id fields will _not_ be run. BusyBox init does
21# nothing with utmp. We don't need no stinkin' utmp.
22#
23# <runlevels>: The runlevels field is completely ignored.
24#
25# <action>: Valid actions include: sysinit, respawn, askfirst, wait, once,
26# restart, ctrlaltdel, and shutdown.
27#
28# Note: askfirst acts just like respawn, but before running the specified
29# process it displays the line "Please press Enter to activate this
30# console." and then waits for the user to press enter before starting
31# the specified process.
32#
33# Note: unrecognised actions (like initdefault) will cause init to emit
34# an error message, and then go along with its business.
35#
36# <process>: Specifies the process to be executed and it's command line.
37#
38# Note: BusyBox init works just fine without an inittab. If no inittab is
39# found, it has the following default behavior:
40# ::sysinit:/etc/init.d/rcS
41# ::askfirst:/bin/sh
42# ::ctrlaltdel:/sbin/reboot
43# ::shutdown:/sbin/swapoff -a
44# ::shutdown:/bin/umount -a -r
45# ::restart:/sbin/init
46#
47# if it detects that /dev/console is _not_ a serial console, it will
48# also run:
49# tty2::askfirst:/bin/sh
50# tty3::askfirst:/bin/sh
51# tty4::askfirst:/bin/sh
52#
53# Boot-time system configuration/initialization script.
54# This is run first except when booting in single-user mode.
55#
56::sysinit:/etc/init.d/rcS
57
58# /bin/sh invocations on selected ttys
59#
60# Note below that we prefix the shell commands with a "-" to indicate to the
61# shell that it is supposed to be a login shell. Normally this is handled by
62# login, but since we are bypassing login in this case, BusyBox lets you do
63# this yourself...
64#
65# Start an "askfirst" shell on the console (whatever that may be)
66::askfirst:-/bin/sh
67# Start an "askfirst" shell on /dev/tty2-4
68tty2::askfirst:-/bin/sh
69tty3::askfirst:-/bin/sh
70tty4::askfirst:-/bin/sh
71
72# /sbin/getty invocations for selected ttys
73tty4::respawn:/sbin/getty 38400 tty5
74tty5::respawn:/sbin/getty 38400 tty6
75
76# Example of how to put a getty on a serial line (for a terminal)
77#::respawn:/sbin/getty -L ttyS0 9600 vt100
78#::respawn:/sbin/getty -L ttyS1 9600 vt100
79#
80# Example how to put a getty on a modem line.
81#::respawn:/sbin/getty 57600 ttyS2
82
83# Stuff to do when restarting the init process
84::restart:/sbin/init
85
86# Stuff to do before rebooting
87::ctrlaltdel:/sbin/reboot
88::shutdown:/bin/umount -a -r
89::shutdown:/sbin/swapoff -a
90
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 @@
1#!/usr/bin/perl -w
2#
3# @(#) mk2knr.pl - generates a perl script that converts lexemes to K&R-style
4#
5# How to use this script:
6# - In the busybox directory type 'examples/mk2knr.pl files-to-convert'
7# - Review the 'convertme.pl' script generated and remove / edit any of the
8# substitutions in there (please especially check for false positives)
9# - Type './convertme.pl same-files-as-before'
10# - Compile and see if it works
11#
12# BUGS: This script does not ignore strings inside comments or strings inside
13# quotes (it probably should).
14
15# set this to something else if you want
16$convertme = 'convertme.pl';
17
18# internal-use variables (don't touch)
19$convert = 0;
20%converted = ();
21
22# if no files were specified, print usage
23die "usage: $0 file.c | file.h\n" if scalar(@ARGV) == 0;
24
25# prepare the "convert me" file
26open(CM, ">$convertme") or die "convertme.pl $!";
27print CM "#!/usr/bin/perl -p -i\n\n";
28
29# process each file passed on the cmd line
30while (<>) {
31
32 # if the line says "getopt" in it anywhere, we don't want to muck with it
33 # because option lists tend to include strings like "cxtzvOf:" which get
34 # matched by the "check for mixed case" regexps below
35 next if /getopt/;
36
37 # tokenize the string into just the variables
38 while (/([a-zA-Z_][a-zA-Z0-9_]*)/g) {
39 $var = $1;
40
41 # ignore the word "BusyBox"
42 next if ($var =~ /BusyBox/);
43
44 # this checks for javaStyle or szHungarianNotation
45 $convert++ if ($var =~ /^[a-z]+[A-Z][a-z]+/);
46
47 # this checks for PascalStyle
48 $convert++ if ($var =~ /^[A-Z][a-z]+[A-Z][a-z]+/);
49
50 # if we want to add more checks, we can add 'em here, but the above
51 # checks catch "just enough" and not too much, so prolly not.
52
53 if ($convert) {
54 $convert = 0;
55
56 # skip ahead if we've already dealt with this one
57 next if ($converted{$var});
58
59 # record that we've dealt with this var
60 $converted{$var} = 1;
61
62 print CM "s/\\b$var\\b/"; # more to come in just a minute
63
64 # change the first letter to lower-case
65 $var = lcfirst($var);
66
67 # put underscores before all remaining upper-case letters
68 $var =~ s/([A-Z])/_$1/g;
69
70 # now change the remaining characters to lower-case
71 $var = lc($var);
72
73 print CM "$var/g;\n";
74 }
75 }
76}
77
78# tidy up and make the $convertme script executable
79close(CM);
80chmod 0755, $convertme;
81
82# print a helpful help message
83print "Done. Scheduled name changes are in $convertme.\n";
84print "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 @@
1#!/bin/sh
2# Sample udhcpc renew script
3
4RESOLV_CONF="/etc/udhcpc/resolv.conf"
5
6[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
7[ -n "$subnet" ] && NETMASK="netmask $subnet"
8
9/sbin/ifconfig $interface $ip $BROADCAST $NETMASK
10
11if [ -n "$router" ]
12then
13 echo "deleting routers"
14 while /sbin/route del default gw 0.0.0.0 dev $interface
15 do :
16 done
17
18 metric=0
19 for i in $router
20 do
21 /sbin/route add default gw $i dev $interface metric $((metric++))
22 done
23fi
24
25echo -n > $RESOLV_CONF
26[ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF
27for i in $dns
28do
29 echo adding dns $i
30 echo nameserver $i >> $RESOLV_CONF
31done \ 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 @@
1#!/bin/sh
2# Sample udhcpc deconfig script
3
4/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 @@
1#!/bin/sh
2# Sample udhcpc nak script
3
4echo 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 @@
1#!/bin/sh
2# Sample udhcpc bound script
3
4RESOLV_CONF="/etc/udhcpc/resolv.conf"
5
6[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
7[ -n "$subnet" ] && NETMASK="netmask $subnet"
8
9/sbin/ifconfig $interface $ip $BROADCAST $NETMASK
10
11if [ -n "$router" ]
12then
13 echo "deleting routers"
14 while /sbin/route del default gw 0.0.0.0 dev $interface
15 do :
16 done
17
18 metric=0
19 for i in $router
20 do
21 /sbin/route add default gw $i dev $interface metric $((metric++))
22 done
23fi
24
25echo -n > $RESOLV_CONF
26[ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF
27for i in $dns
28do
29 echo adding dns $i
30 echo nameserver $i >> $RESOLV_CONF
31done \ 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 @@
1#!/bin/sh
2# Currently, we only dispatch according to command. However, a more
3# elaborate system might dispatch by command and interface or do some
4# common initialization first, especially if more dhcp event notifications
5# are added.
6
7exec /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 @@
1#!/bin/sh
2
3# udhcpc script edited by Tim Riker <Tim@Rikers.org>
4
5[ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1
6
7RESOLV_CONF="/etc/resolv.conf"
8[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
9[ -n "$subnet" ] && NETMASK="netmask $subnet"
10
11case "$1" in
12 deconfig)
13 /sbin/ifconfig $interface 0.0.0.0
14 ;;
15
16 renew|bound)
17 /sbin/ifconfig $interface $ip $BROADCAST $NETMASK
18
19 if [ -n "$router" ] ; then
20 echo "deleting routers"
21 while route del default gw 0.0.0.0 dev $interface ; do
22 :
23 done
24
25 metric=0
26 for i in $router ; do
27 route add default gw $i dev $interface metric $((metric++))
28 done
29 fi
30
31 echo -n > $RESOLV_CONF
32 [ -n "$domain" ] && echo search $domain >> $RESOLV_CONF
33 for i in $dns ; do
34 echo adding dns $i
35 echo nameserver $i >> $RESOLV_CONF
36 done
37 ;;
38esac
39
40exit 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 @@
1# Sample udhcpd configuration file (/etc/udhcpd.conf)
2
3# The start and end of the IP lease block
4
5start 192.168.0.20 #default: 192.168.0.20
6end 192.168.0.254 #default: 192.168.0.254
7
8
9# The interface that udhcpd will use
10
11interface eth0 #default: eth0
12
13
14# The maximim number of leases (includes addressesd reserved
15# by OFFER's, DECLINE's, and ARP conficts
16
17#max_leases 254 #default: 254
18
19
20# If remaining is true (default), udhcpd will store the time
21# remaining for each lease in the udhcpd leases file. This is
22# for embedded systems that cannot keep time between reboots.
23# If you set remaining to no, the absolute time that the lease
24# expires at will be stored in the dhcpd.leases file.
25
26#remaining yes #default: yes
27
28
29# The time period at which udhcpd will write out a dhcpd.leases
30# file. If this is 0, udhcpd will never automatically write a
31# lease file. (specified in seconds)
32
33#auto_time 7200 #default: 7200 (2 hours)
34
35
36# The amount of time that an IP will be reserved (leased) for if a
37# DHCP decline message is received (seconds).
38
39#decline_time 3600 #default: 3600 (1 hour)
40
41
42# The amount of time that an IP will be reserved (leased) for if an
43# ARP conflct occurs. (seconds
44
45#conflict_time 3600 #default: 3600 (1 hour)
46
47
48# How long an offered address is reserved (leased) in seconds
49
50#offer_time 60 #default: 60 (1 minute)
51
52# If a lease to be given is below this value, the full lease time is
53# instead used (seconds).
54
55#min_lease 60 #defult: 60
56
57
58# The location of the leases file
59
60#lease_file /var/lib/misc/udhcpd.leases #defualt: /var/lib/misc/udhcpd.leases
61
62# The location of the pid file
63#pidfile /var/run/udhcpd.pid #default: /var/run/udhcpd.pid
64
65# Everytime udhcpd writes a leases file, the below script will be called.
66# Useful for writing the lease file to flash every few hours.
67
68#notify_file #default: (no script)
69
70#notify_file dumpleases # <--- usefull for debugging
71
72# The following are bootp specific options, setable by udhcpd.
73
74#siaddr 192.168.0.22 #default: 0.0.0.0
75
76#sname zorak #default: (none)
77
78#boot_file /var/nfs_root #default: (none)
79
80# The remainer of options are DHCP options and can be specifed with the
81# keyword 'opt' or 'option'. If an option can take multiple items, such
82# as the dns option, they can be listed on the same line, or multiple
83# lines. The only option with a default is 'lease'.
84
85#Examles
86opt dns 192.168.10.2 192.168.10.10
87option subnet 255.255.255.0
88opt router 192.168.10.2
89opt wins 192.168.10.10
90option dns 129.219.13.81 # appened to above DNS servers for a total of 3
91option domain local
92option lease 864000 # 10 days of seconds
93
94
95# Currently supported options, for more info, see options.c
96#opt subnet
97#opt timezone
98#opt router
99#opt timesvr
100#opt namesvr
101#opt dns
102#opt logsvr
103#opt cookiesvr
104#opt lprsvr
105#opt bootsize
106#opt domain
107#opt swapsvr
108#opt rootpath
109#opt ipttl
110#opt mtu
111#opt broadcast
112#opt wins
113#opt lease
114#opt ntpsrv
115#opt tftp
116#opt bootfile
117
118
119# Static leases map
120#static_lease 00:60:08:11:CE:4E 192.168.0.54
121#static_lease 00:60:08:11:CE:3E 192.168.0.44
122
123
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 @@
1#!/bin/sh
2#
3# This should work with the GNU version of tar and gzip!
4# This should work with the bash or ash shell!
5# Requires the programs (ar, tar, gzip, and the pager more or less).
6#
7usage() {
8echo "Usage: undeb -c package.deb <Print control file info>"
9echo " undeb -l package.deb <List contents of deb package>"
10echo " undeb -x package.deb /foo/boo <Extract deb package to this directory,"
11echo " put . for current directory>"
12exit
13}
14
15deb=$2
16
17exist() {
18if [ "$deb" = "" ]; then
19usage
20elif [ ! -s "$deb" ]; then
21echo "Can't find $deb!"
22exit
23fi
24}
25
26if [ "$1" = "" ]; then
27usage
28elif [ "$1" = "-l" ]; then
29exist
30type more >/dev/null 2>&1 && pager=more
31type less >/dev/null 2>&1 && pager=less
32[ "$pager" = "" ] && echo "No pager found!" && exit
33(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
34exit
35elif [ "$1" = "-c" ]; then
36exist
37ar -p $deb control.tar.gz | tar -xzO *control
38exit
39elif [ "$1" = "-x" ]; then
40exist
41if [ "$3" = "" ]; then
42usage
43elif [ ! -d "$3" ]; then
44echo "No such directory $3!"
45exit
46fi
47ar -p $deb data.tar.gz | tar -xzvpf - -C $3 || exit
48echo
49echo "Extracted $deb to $3!"
50exit
51else
52usage
53fi
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 @@
1#!/bin/sh
2#
3# This should work with the GNU version of cpio and gzip!
4# This should work with the bash or ash shell!
5# Requires the programs (cpio, gzip, and the pager more or less).
6#
7usage() {
8echo "Usage: unrpm -l package.rpm <List contents of rpm package>"
9echo " unrpm -x package.rpm /foo/boo <Extract rpm package to this directory,"
10echo " put . for current directory>"
11exit
12}
13
14rpm=$2
15
16exist() {
17if [ "$rpm" = "" ]; then
18usage
19elif [ ! -s "$rpm" ]; then
20echo "Can't find $rpm!"
21exit
22fi
23}
24
25if [ "$1" = "" ]; then
26usage
27elif [ "$1" = "-l" ]; then
28exist
29type more >/dev/null 2>&1 && pager=more
30type less >/dev/null 2>&1 && pager=less
31[ "$pager" = "" ] && echo "No pager found!" && exit
32(echo -e "\nPress enter to scroll, q to Quit!\n" ; rpm2cpio $rpm | cpio -tv --quiet) | $pager
33exit
34elif [ "$1" = "-x" ]; then
35exist
36if [ "$3" = "" ]; then
37usage
38elif [ ! -d "$3" ]; then
39echo "No such directory $3!"
40exit
41fi
42rpm2cpio $rpm | (umask 0 ; cd $3 ; cpio -idmuv) || exit
43echo
44echo "Extracted $rpm to $3!"
45exit
46else
47usage
48fi
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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Finding Utilities"
7
8config CONFIG_FIND
9 bool "find"
10 default n
11 help
12 find is used to search your system to find specified files.
13
14config CONFIG_FEATURE_FIND_MTIME
15 bool " Enable modified time matching (-mtime) option"
16 default y
17 depends on CONFIG_FIND
18 help
19 Allow searching based on the modification time of
20 files.
21
22config CONFIG_FEATURE_FIND_PERM
23 bool " Enable permissions matching (-perm) option"
24 default y
25 depends on CONFIG_FIND
26 help
27 Enable searching based on file permissions.
28
29config CONFIG_FEATURE_FIND_TYPE
30 bool " Enable filetype matching (-type) option"
31 default y
32 depends on CONFIG_FIND
33 help
34 Enable searching based on file type (file,
35 directory, socket, device, etc.).
36
37config CONFIG_FEATURE_FIND_XDEV
38 bool " Enable stay in filesystem (-xdev) option"
39 default y
40 depends on CONFIG_FIND
41 help
42 This option will allow find to restrict searches to a single
43 filesystem.
44
45config CONFIG_FEATURE_FIND_NEWER
46 bool " Enable -newer option for comparing file mtimes"
47 default y
48 depends on CONFIG_FIND
49 help
50 Support the 'find -newer' option for finding any files which have
51 a modified time that is more recent than the specified FILE.
52
53config CONFIG_FEATURE_FIND_INUM
54 bool " Enable inode number matching (-inum) option"
55 default y
56 depends on CONFIG_FIND
57 help
58 Support the 'find -inum' option for searching by inode number.
59
60config CONFIG_GREP
61 bool "grep"
62 default n
63 help
64 grep is used to search files for a specified pattern.
65
66config CONFIG_FEATURE_GREP_EGREP_ALIAS
67 bool " Support extended regular expressions (egrep & grep -E)"
68 default y
69 depends on CONFIG_GREP
70 help
71 Enabled support for extended regular expressions. Extended
72 regular expressions allow for alternation (foo|bar), grouping,
73 and various repetition operators.
74
75config CONFIG_FEATURE_GREP_FGREP_ALIAS
76 bool " Alias fgrep to grep -f"
77 default y
78 depends on CONFIG_GREP
79 help
80 fgrep sees the search pattern as a normal string rather than
81 regular expressions.
82 grep -f is always builtin, this just creates the fgrep alias.
83
84config CONFIG_FEATURE_GREP_CONTEXT
85 bool " Enable before and after context flags (-A, -B and -C)"
86 default y
87 depends on CONFIG_GREP
88 help
89 Print the specified number of leading (-B) and/or trailing (-A)
90 context surrounding our matching lines.
91 Print the specified number of context lines (-C).
92
93config CONFIG_XARGS
94 bool "xargs"
95 default n
96 help
97 xargs is used to execute a specified command on
98 every item from standard input.
99
100config CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
101 bool " Enable prompt and confirmation option -p"
102 default n
103 depends on CONFIG_XARGS
104 help
105 Support prompt the user about whether to run each command
106 line and read a line from the terminal.
107
108config CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
109 bool " Enable support single and double quotes and backslash"
110 default n
111 depends on CONFIG_XARGS
112 help
113 Default xargs unsupport single and double quotes
114 and backslash for can use aruments with spaces.
115
116config CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
117 bool " Enable support options -x"
118 default n
119 depends on CONFIG_XARGS
120 help
121 Enable support exit if the size (see the -s or -n option)
122 is exceeded.
123
124config CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
125 bool " Enable options -0"
126 default n
127 depends on CONFIG_XARGS
128 help
129 Enable input filenames are terminated by a null character
130 instead of by whitespace, and the quotes and backslash
131 are not special.
132
133endmenu
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/findutils
23FINDUTILS_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include $(srcdir)/Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20FINDUTILS_AR:=findutils.a
21ifndef $(FINDUTILS_DIR)
22FINDUTILS_DIR:=$(top_builddir)/findutils/
23endif
24srcdir=$(top_srcdir)/findutils
25
26FINDUTILS-y:=
27FINDUTILS-$(CONFIG_FIND) += find.o
28FINDUTILS-$(CONFIG_GREP) += grep.o
29FINDUTILS-$(CONFIG_XARGS) += xargs.o
30
31libraries-y+=$(FINDUTILS_DIR)$(FINDUTILS_AR)
32
33$(FINDUTILS_DIR)$(FINDUTILS_AR): $(patsubst %,$(FINDUTILS_DIR)%, $(FINDUTILS-y))
34 $(AR) -ro $@ $(patsubst %,$(FINDUTILS_DIR)%, $(FINDUTILS-y))
35
36$(FINDUTILS_DIR)%.o: $(srcdir)/%.c
37 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
38
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini find implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Reworked by David Douthitt <n9ubh@callsign.net> and
8 * Matt Kraai <kraai@alumni.carnegiemellon.edu>.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25
26#include <stdio.h>
27#include <unistd.h>
28#include <dirent.h>
29#include <string.h>
30#include <stdlib.h>
31#include <fnmatch.h>
32#include <time.h>
33#include <ctype.h>
34#include "busybox.h"
35
36//XXX just found out about libbb/messages.c . maybe move stuff there ? - ghoz
37const char msg_req_arg[] = "option `%s' requires an argument";
38const char msg_invalid_arg[] = "invalid argument `%s' to `%s'";
39
40static char *pattern;
41
42#ifdef CONFIG_FEATURE_FIND_TYPE
43static int type_mask = 0;
44#endif
45
46#ifdef CONFIG_FEATURE_FIND_PERM
47static char perm_char = 0;
48static int perm_mask = 0;
49#endif
50
51#ifdef CONFIG_FEATURE_FIND_MTIME
52static char mtime_char;
53static int mtime_days;
54#endif
55
56#ifdef CONFIG_FEATURE_FIND_XDEV
57static dev_t *xdev_dev;
58static int xdev_count = 0;
59#endif
60
61#ifdef CONFIG_FEATURE_FIND_NEWER
62time_t newer_mtime;
63#endif
64
65#ifdef CONFIG_FEATURE_FIND_INUM
66static ino_t inode_num;
67#endif
68
69static int fileAction(const char *fileName, struct stat *statbuf, void* junk)
70{
71 if (pattern != NULL) {
72 const char *tmp = strrchr(fileName, '/');
73
74 if (tmp == NULL)
75 tmp = fileName;
76 else
77 tmp++;
78 if (!(fnmatch(pattern, tmp, FNM_PERIOD) == 0))
79 goto no_match;
80 }
81#ifdef CONFIG_FEATURE_FIND_TYPE
82 if (type_mask != 0) {
83 if (!((statbuf->st_mode & S_IFMT) == type_mask))
84 goto no_match;
85 }
86#endif
87#ifdef CONFIG_FEATURE_FIND_PERM
88 if (perm_mask != 0) {
89 if (!((isdigit(perm_char) && (statbuf->st_mode & 07777) == perm_mask) ||
90 (perm_char == '-' && (statbuf->st_mode & perm_mask) == perm_mask) ||
91 (perm_char == '+' && (statbuf->st_mode & perm_mask) != 0)))
92 goto no_match;
93 }
94#endif
95#ifdef CONFIG_FEATURE_FIND_MTIME
96 if (mtime_char != 0) {
97 time_t file_age = time(NULL) - statbuf->st_mtime;
98 time_t mtime_secs = mtime_days * 24 * 60 * 60;
99 if (!((isdigit(mtime_char) && file_age >= mtime_secs &&
100 file_age < mtime_secs + 24 * 60 * 60) ||
101 (mtime_char == '+' && file_age >= mtime_secs + 24 * 60 * 60) ||
102 (mtime_char == '-' && file_age < mtime_secs)))
103 goto no_match;
104 }
105#endif
106#ifdef CONFIG_FEATURE_FIND_XDEV
107 if (xdev_count) {
108 int i;
109 for (i=0; i<xdev_count; i++) {
110 if (xdev_dev[i] == statbuf-> st_dev)
111 break;
112 }
113 if (i == xdev_count) {
114 if(S_ISDIR(statbuf->st_mode))
115 return SKIP;
116 else
117 goto no_match;
118 }
119 }
120#endif
121#ifdef CONFIG_FEATURE_FIND_NEWER
122 if (newer_mtime != 0) {
123 time_t file_age = newer_mtime - statbuf->st_mtime;
124 if (file_age >= 0)
125 goto no_match;
126 }
127#endif
128#ifdef CONFIG_FEATURE_FIND_INUM
129 if (inode_num != 0) {
130 if (!(statbuf->st_ino == inode_num))
131 goto no_match;
132 }
133#endif
134 puts(fileName);
135no_match:
136 return (TRUE);
137}
138
139#ifdef CONFIG_FEATURE_FIND_TYPE
140static int find_type(char *type)
141{
142 int mask = 0;
143
144 switch (type[0]) {
145 case 'b':
146 mask = S_IFBLK;
147 break;
148 case 'c':
149 mask = S_IFCHR;
150 break;
151 case 'd':
152 mask = S_IFDIR;
153 break;
154 case 'p':
155 mask = S_IFIFO;
156 break;
157 case 'f':
158 mask = S_IFREG;
159 break;
160 case 'l':
161 mask = S_IFLNK;
162 break;
163 case 's':
164 mask = S_IFSOCK;
165 break;
166 }
167
168 if (mask == 0 || type[1] != '\0')
169 bb_error_msg_and_die(msg_invalid_arg, type, "-type");
170
171 return mask;
172}
173#endif
174
175int find_main(int argc, char **argv)
176{
177 int dereference = FALSE;
178 int i, firstopt, status = EXIT_SUCCESS;
179
180 for (firstopt = 1; firstopt < argc; firstopt++) {
181 if (argv[firstopt][0] == '-')
182 break;
183 }
184
185 /* Parse any options */
186 for (i = firstopt; i < argc; i++) {
187 if (strcmp(argv[i], "-follow") == 0)
188 dereference = TRUE;
189 else if (strcmp(argv[i], "-print") == 0) {
190 ;
191 }
192 else if (strcmp(argv[i], "-name") == 0) {
193 if (++i == argc)
194 bb_error_msg_and_die(msg_req_arg, "-name");
195 pattern = argv[i];
196#ifdef CONFIG_FEATURE_FIND_TYPE
197 } else if (strcmp(argv[i], "-type") == 0) {
198 if (++i == argc)
199 bb_error_msg_and_die(msg_req_arg, "-type");
200 type_mask = find_type(argv[i]);
201#endif
202#ifdef CONFIG_FEATURE_FIND_PERM
203 } else if (strcmp(argv[i], "-perm") == 0) {
204 char *end;
205 if (++i == argc)
206 bb_error_msg_and_die(msg_req_arg, "-perm");
207 perm_mask = strtol(argv[i], &end, 8);
208 if ((end[0] != '\0') || (perm_mask > 07777))
209 bb_error_msg_and_die(msg_invalid_arg, argv[i], "-perm");
210 if ((perm_char = argv[i][0]) == '-')
211 perm_mask = -perm_mask;
212#endif
213#ifdef CONFIG_FEATURE_FIND_MTIME
214 } else if (strcmp(argv[i], "-mtime") == 0) {
215 char *end;
216 if (++i == argc)
217 bb_error_msg_and_die(msg_req_arg, "-mtime");
218 mtime_days = strtol(argv[i], &end, 10);
219 if (end[0] != '\0')
220 bb_error_msg_and_die(msg_invalid_arg, argv[i], "-mtime");
221 if ((mtime_char = argv[i][0]) == '-')
222 mtime_days = -mtime_days;
223#endif
224#ifdef CONFIG_FEATURE_FIND_XDEV
225 } else if (strcmp(argv[i], "-xdev") == 0) {
226 struct stat stbuf;
227
228 xdev_count = ( firstopt - 1 ) ? ( firstopt - 1 ) : 1;
229 xdev_dev = xmalloc ( xdev_count * sizeof( dev_t ));
230
231 if ( firstopt == 1 ) {
232 if ( stat ( ".", &stbuf ) < 0 )
233 bb_error_msg_and_die("could not stat '.'" );
234 xdev_dev [0] = stbuf. st_dev;
235 }
236 else {
237
238 for (i = 1; i < firstopt; i++) {
239 if ( stat ( argv [i], &stbuf ) < 0 )
240 bb_error_msg_and_die("could not stat '%s'", argv [i] );
241 xdev_dev [i-1] = stbuf. st_dev;
242 }
243 }
244#endif
245#ifdef CONFIG_FEATURE_FIND_NEWER
246 } else if (strcmp(argv[i], "-newer") == 0) {
247 struct stat stat_newer;
248 if (++i == argc)
249 bb_error_msg_and_die(msg_req_arg, "-newer");
250 if (stat (argv[i], &stat_newer) != 0)
251 bb_error_msg_and_die("file %s not found", argv[i]);
252 newer_mtime = stat_newer.st_mtime;
253#endif
254#ifdef CONFIG_FEATURE_FIND_INUM
255 } else if (strcmp(argv[i], "-inum") == 0) {
256 char *end;
257 if (++i == argc)
258 bb_error_msg_and_die(msg_req_arg, "-inum");
259 inode_num = strtol(argv[i], &end, 10);
260 if (end[0] != '\0')
261 bb_error_msg_and_die(msg_invalid_arg, argv[i], "-inum");
262#endif
263 } else
264 bb_show_usage();
265 }
266
267 if (firstopt == 1) {
268 if (! recursive_action(".", TRUE, dereference, FALSE, fileAction,
269 fileAction, NULL))
270 status = EXIT_FAILURE;
271 } else {
272 for (i = 1; i < firstopt; i++) {
273 if (! recursive_action(argv[i], TRUE, dereference, FALSE, fileAction,
274 fileAction, NULL))
275 status = EXIT_FAILURE;
276 }
277 }
278
279 return status;
280}
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 @@
1/*
2 * Mini grep implementation for busybox using libc regex.
3 *
4 * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley
5 * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22/*
23 * Apr 2004 by Vladimir Oleynik <dzo@simtreas.ru> -
24 * correction "-e pattern1 -e pattern2" logic and more optimizations.
25*/
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <getopt.h>
30#include <regex.h>
31#include <string.h>
32#include <errno.h>
33#include "busybox.h"
34
35
36/* options */
37#define GREP_OPTS "lnqvscFiHhe:f:L"
38#define GREP_OPT_l (1<<0)
39static char print_files_with_matches;
40#define GREP_OPT_n (1<<1)
41static char print_line_num;
42#define GREP_OPT_q (1<<2)
43static char be_quiet;
44#define GREP_OPT_v (1<<3)
45typedef char invert_search_t;
46static invert_search_t invert_search;
47#define GREP_OPT_s (1<<4)
48static char suppress_err_msgs;
49#define GREP_OPT_c (1<<5)
50static char print_match_counts;
51#define GREP_OPT_F (1<<6)
52static char fgrep_flag;
53#define GREP_OPT_i (1<<7)
54#define GREP_OPT_H (1<<8)
55#define GREP_OPT_h (1<<9)
56#define GREP_OPT_e (1<<10)
57#define GREP_OPT_f (1<<11)
58#define GREP_OPT_L (1<<12)
59static char print_files_without_matches;
60#ifdef CONFIG_FEATURE_GREP_CONTEXT
61#define GREP_OPT_CONTEXT "A:B:C"
62#define GREP_OPT_A (1<<13)
63#define GREP_OPT_B (1<<14)
64#define GREP_OPT_C (1<<15)
65#define GREP_OPT_E (1<<16)
66#else
67#define GREP_OPT_CONTEXT ""
68#define GREP_OPT_E (1<<13)
69#endif
70#ifdef CONFIG_FEATURE_GREP_EGREP_ALIAS
71# define OPT_EGREP "E"
72#else
73# define OPT_EGREP ""
74#endif
75
76static int reflags;
77static int print_filename;
78
79#ifdef CONFIG_FEATURE_GREP_CONTEXT
80static int lines_before;
81static int lines_after;
82static char **before_buf;
83static int last_line_printed;
84#endif /* CONFIG_FEATURE_GREP_CONTEXT */
85
86/* globals used internally */
87static llist_t *pattern_head; /* growable list of patterns to match */
88static char *cur_file; /* the current file we are reading */
89
90
91static void print_line(const char *line, int linenum, char decoration)
92{
93#ifdef CONFIG_FEATURE_GREP_CONTEXT
94 /* possibly print the little '--' separator */
95 if ((lines_before || lines_after) && last_line_printed &&
96 last_line_printed < linenum - 1) {
97 puts("--");
98 }
99 last_line_printed = linenum;
100#endif
101 if (print_filename)
102 printf("%s%c", cur_file, decoration);
103 if (print_line_num)
104 printf("%i%c", linenum, decoration);
105 puts(line);
106}
107
108extern void xregcomp(regex_t *preg, const char *regex, int cflags);
109
110
111static int grep_file(FILE *file)
112{
113 char *line;
114 invert_search_t ret;
115 int linenum = 0;
116 int nmatches = 0;
117#ifdef CONFIG_FEATURE_GREP_CONTEXT
118 int print_n_lines_after = 0;
119 int curpos = 0; /* track where we are in the circular 'before' buffer */
120 int idx = 0; /* used for iteration through the circular buffer */
121#endif /* CONFIG_FEATURE_GREP_CONTEXT */
122
123 while ((line = bb_get_chomped_line_from_file(file)) != NULL) {
124 llist_t *pattern_ptr = pattern_head;
125
126 linenum++;
127 ret = 0;
128 while (pattern_ptr) {
129 if (fgrep_flag) {
130 ret = strstr(line, pattern_ptr->data) != NULL;
131 } else {
132 /*
133 * test for a postitive-assertion match (regexec returns success (0)
134 * and the user did not specify invert search), or a negative-assertion
135 * match (regexec returns failure (REG_NOMATCH) and the user specified
136 * invert search)
137 */
138 regex_t regex;
139 xregcomp(&regex, pattern_ptr->data, reflags);
140 ret |= regexec(&regex, line, 0, NULL, 0) == 0;
141 regfree(&regex);
142 }
143 pattern_ptr = pattern_ptr->link;
144 } /* while (pattern_ptr) */
145
146 if ((ret ^ invert_search)) {
147
148 if (print_files_with_matches || be_quiet)
149 free(line);
150
151 /* if we found a match but were told to be quiet, stop here */
152 if (be_quiet || print_files_without_matches)
153 return -1;
154
155 /* keep track of matches */
156 nmatches++;
157
158 /* if we're just printing filenames, we stop after the first match */
159 if (print_files_with_matches)
160 break;
161
162 /* print the matched line */
163 if (print_match_counts == 0) {
164#ifdef CONFIG_FEATURE_GREP_CONTEXT
165 int prevpos = (curpos == 0) ? lines_before - 1 : curpos - 1;
166
167 /* if we were told to print 'before' lines and there is at least
168 * one line in the circular buffer, print them */
169 if (lines_before && before_buf[prevpos] != NULL) {
170 int first_buf_entry_line_num = linenum - lines_before;
171
172 /* advance to the first entry in the circular buffer, and
173 * figure out the line number is of the first line in the
174 * buffer */
175 idx = curpos;
176 while (before_buf[idx] == NULL) {
177 idx = (idx + 1) % lines_before;
178 first_buf_entry_line_num++;
179 }
180
181 /* now print each line in the buffer, clearing them as we go */
182 while (before_buf[idx] != NULL) {
183 print_line(before_buf[idx], first_buf_entry_line_num, '-');
184 free(before_buf[idx]);
185 before_buf[idx] = NULL;
186 idx = (idx + 1) % lines_before;
187 first_buf_entry_line_num++;
188 }
189 }
190
191 /* make a note that we need to print 'after' lines */
192 print_n_lines_after = lines_after;
193#endif /* CONFIG_FEATURE_GREP_CONTEXT */
194 print_line(line, linenum, ':');
195 }
196 }
197#ifdef CONFIG_FEATURE_GREP_CONTEXT
198 else { /* no match */
199 /* Add the line to the circular 'before' buffer */
200 if(lines_before) {
201 free(before_buf[curpos]);
202 before_buf[curpos] = bb_xstrdup(line);
203 curpos = (curpos + 1) % lines_before;
204 }
205 }
206
207 /* if we need to print some context lines after the last match, do so */
208 if (print_n_lines_after && (last_line_printed != linenum)) {
209 print_line(line, linenum, '-');
210 print_n_lines_after--;
211 }
212#endif /* CONFIG_FEATURE_GREP_CONTEXT */
213 free(line);
214 }
215
216
217 /* special-case file post-processing for options where we don't print line
218 * matches, just filenames and possibly match counts */
219
220 /* grep -c: print [filename:]count, even if count is zero */
221 if (print_match_counts) {
222 if (print_filename)
223 printf("%s:", cur_file);
224 printf("%d\n", nmatches);
225 }
226
227 /* grep -l: print just the filename, but only if we grepped the line in the file */
228 if (print_files_with_matches && nmatches > 0) {
229 puts(cur_file);
230 }
231
232 /* grep -L: print just the filename, but only if we didn't grep the line in the file */
233 if (print_files_without_matches && nmatches == 0) {
234 puts(cur_file);
235 }
236
237 return nmatches;
238}
239
240static void load_regexes_from_file(llist_t *fopt)
241{
242 char *line;
243 FILE *f;
244
245 while(fopt) {
246 llist_t *cur = fopt;
247 char *ffile = cur->data;
248
249 fopt = cur->link;
250 free(cur);
251 f = bb_xfopen(ffile, "r");
252 while ((line = bb_get_chomped_line_from_file(f)) != NULL) {
253 pattern_head = llist_add_to(pattern_head, line);
254 }
255 }
256}
257
258
259extern int grep_main(int argc, char **argv)
260{
261 FILE *file;
262 int matched;
263 unsigned long opt;
264 llist_t *fopt = NULL;
265
266 /* do normal option parsing */
267#ifdef CONFIG_FEATURE_GREP_CONTEXT
268 {
269 char *junk;
270 char *slines_after;
271 char *slines_before;
272 char *Copt;
273
274 bb_opt_complementaly = "H-h:e*:f*:C-AB";
275 opt = bb_getopt_ulflags(argc, argv,
276 GREP_OPTS GREP_OPT_CONTEXT OPT_EGREP,
277 &pattern_head, &fopt,
278 &slines_after, &slines_before, &Copt);
279
280 if(opt & GREP_OPT_C) {
281 /* C option unseted A and B options, but next -A or -B
282 may be ovewrite own option */
283 if(!(opt & GREP_OPT_A)) /* not overwtited */
284 slines_after = Copt;
285 if(!(opt & GREP_OPT_B)) /* not overwtited */
286 slines_before = Copt;
287 opt |= GREP_OPT_A|GREP_OPT_B; /* set for parse now */
288 }
289 if(opt & GREP_OPT_A) {
290 lines_after = strtoul(slines_after, &junk, 10);
291 if(*junk != '\0')
292 bb_error_msg_and_die("invalid context length argument");
293 }
294 if(opt & GREP_OPT_B) {
295 lines_before = strtoul(slines_before, &junk, 10);
296 if(*junk != '\0')
297 bb_error_msg_and_die("invalid context length argument");
298 }
299 /* sanity checks after parse may be invalid numbers ;-) */
300 if ((opt & (GREP_OPT_c|GREP_OPT_q|GREP_OPT_l|GREP_OPT_L))) {
301 opt &= ~GREP_OPT_n;
302 lines_before = 0;
303 lines_after = 0;
304 } else if(lines_before > 0)
305 before_buf = (char **)xcalloc(lines_before, sizeof(char *));
306 }
307#else
308 /* with auto sanity checks */
309 bb_opt_complementaly = "H-h:e*:f*:c-n:q-n:l-n";
310 opt = bb_getopt_ulflags(argc, argv, GREP_OPTS OPT_EGREP,
311 &pattern_head, &fopt);
312
313#endif
314 print_files_with_matches = opt & GREP_OPT_l;
315 print_files_without_matches = (opt & GREP_OPT_L) != 0;
316 print_line_num = opt & GREP_OPT_n;
317 be_quiet = opt & GREP_OPT_q;
318 invert_search = (opt & GREP_OPT_v) != 0; /* 0 | 1 */
319 suppress_err_msgs = opt & GREP_OPT_s;
320 print_match_counts = opt & GREP_OPT_c;
321 fgrep_flag = opt & GREP_OPT_F;
322 if(opt & GREP_OPT_H)
323 print_filename++;
324 if(opt & GREP_OPT_h)
325 print_filename--;
326 if(opt & GREP_OPT_f)
327 load_regexes_from_file(fopt);
328
329#ifdef CONFIG_FEATURE_GREP_EGREP_ALIAS
330 if(bb_applet_name[0] == 'e' || (opt & GREP_OPT_E))
331 reflags = REG_EXTENDED | REG_NOSUB;
332 else
333#endif
334 reflags = REG_NOSUB;
335
336 if(opt & GREP_OPT_i)
337 reflags |= REG_ICASE;
338
339 argv += optind;
340 argc -= optind;
341
342 /* if we didn't get a pattern from a -e and no command file was specified,
343 * argv[optind] should be the pattern. no pattern, no worky */
344 if (pattern_head == NULL) {
345 if (*argv == NULL)
346 bb_show_usage();
347 else {
348 pattern_head = llist_add_to(pattern_head, *argv++);
349 argc--;
350 }
351 }
352
353 /* argv[(optind)..(argc-1)] should be names of file to grep through. If
354 * there is more than one file to grep, we will print the filenames */
355 if (argc > 1) {
356 print_filename++;
357
358 /* If no files were specified, or '-' was specified, take input from
359 * stdin. Otherwise, we grep through all the files specified. */
360 } else if (argc == 0) {
361 argc++;
362 }
363 matched = 0;
364 while (argc--) {
365 cur_file = *argv++;
366 if(!cur_file || (*cur_file == '-' && !cur_file[1])) {
367 cur_file = "-";
368 file = stdin;
369 } else {
370 file = fopen(cur_file, "r");
371 }
372 if (file == NULL) {
373 if (!suppress_err_msgs)
374 bb_perror_msg("%s", cur_file);
375 } else {
376 matched += grep_file(file);
377 if(matched < 0) {
378 /* we found a match but were told to be quiet, stop here and
379 * return success */
380 break;
381 }
382 fclose(file);
383 }
384 }
385
386#ifdef CONFIG_FEATURE_CLEAN_UP
387 /* destroy all the elments in the pattern list */
388 while (pattern_head) {
389 llist_t *pattern_head_ptr = pattern_head;
390
391 pattern_head = pattern_head->link;
392 free(pattern_head_ptr);
393 }
394#endif
395
396 return !matched; /* invert return value 0 = success, 1 = failed */
397}
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 @@
1/*
2 * Mini xargs implementation for busybox
3 * Options are supported: "-prtx -n max_arg -s max_chars -e[ouf_str]"
4 *
5 * (C) 2002,2003 by Vladimir Oleynik <dzo@simtreas.ru>
6 *
7 * Special thanks
8 * - Mark Whitley and Glenn McGrath for stimulus to rewrite :)
9 * - Mike Rendell <michael@cs.mun.ca>
10 * and David MacKenzie <djm@gnu.ai.mit.edu>.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 *
26 * xargs is described in the Single Unix Specification v3 at
27 * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html
28 *
29 */
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <unistd.h>
35#include <getopt.h>
36#include <errno.h>
37#include <fcntl.h>
38#include <sys/types.h>
39#include <sys/wait.h>
40#include "busybox.h"
41
42/* COMPAT: SYSV version defaults size (and has a max value of) to 470.
43 We try to make it as large as possible. */
44#if !defined(ARG_MAX) && defined(_SC_ARG_MAX)
45#define ARG_MAX sysconf (_SC_ARG_MAX)
46#endif
47#ifndef ARG_MAX
48#define ARG_MAX 470
49#endif
50
51
52#ifdef TEST
53# ifndef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
54# define CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
55# endif
56# ifndef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
57# define CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
58# endif
59# ifndef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
60# define CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
61# endif
62# ifndef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
63# define CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
64# endif
65#endif
66
67/*
68 This function have special algorithm.
69 Don`t use fork and include to main!
70*/
71static int xargs_exec(char *const *args)
72{
73 pid_t p;
74 volatile int exec_errno = 0; /* shared vfork stack */
75
76 if ((p = vfork()) >= 0) {
77 if (p == 0) {
78 /* vfork -- child */
79 execvp(args[0], args);
80 exec_errno = errno; /* set error to shared stack */
81 _exit(1);
82 } else {
83 /* vfork -- parent */
84 int status;
85
86 while (wait(&status) == (pid_t) - 1)
87 if (errno != EINTR)
88 break;
89 if (exec_errno) {
90 errno = exec_errno;
91 bb_perror_msg("%s", args[0]);
92 return exec_errno == ENOENT ? 127 : 126;
93 } else {
94 if (WEXITSTATUS(status) == 255) {
95 bb_error_msg("%s: exited with status 255; aborting", args[0]);
96 return 124;
97 }
98 if (WIFSTOPPED(status)) {
99 bb_error_msg("%s: stopped by signal %d",
100 args[0], WSTOPSIG(status));
101 return 125;
102 }
103 if (WIFSIGNALED(status)) {
104 bb_error_msg("%s: terminated by signal %d",
105 args[0], WTERMSIG(status));
106 return 125;
107 }
108 if (WEXITSTATUS(status) != 0)
109 return 123;
110 return 0;
111 }
112 }
113 } else {
114 bb_perror_msg_and_die("vfork");
115 }
116}
117
118
119typedef struct xlist_s {
120 char *data;
121 size_t lenght;
122 struct xlist_s *link;
123} xlist_t;
124
125static int eof_stdin_detected;
126
127#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
128#define ISSPACE(c) (ISBLANK (c) || (c) == '\n' || (c) == '\r' \
129 || (c) == '\f' || (c) == '\v')
130
131#ifdef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
132static xlist_t *process_stdin(xlist_t * list_arg,
133 const char *eof_str, size_t mc, char *buf)
134{
135#define NORM 0
136#define QUOTE 1
137#define BACKSLASH 2
138#define SPACE 4
139
140 char *s = NULL; /* start word */
141 char *p = NULL; /* pointer to end word */
142 char q = 0; /* quote char */
143 char state = NORM;
144 char eof_str_detected = 0;
145 size_t line_l = 0; /* size loaded args line */
146 int c; /* current char */
147 xlist_t *cur;
148 xlist_t *prev;
149
150 for (prev = cur = list_arg; cur; cur = cur->link) {
151 line_l += cur->lenght; /* previous allocated */
152 if (prev != cur)
153 prev = prev->link;
154 }
155
156 while (!eof_stdin_detected) {
157 c = getchar();
158 if (c == EOF) {
159 eof_stdin_detected++;
160 if (s)
161 goto unexpected_eof;
162 break;
163 }
164 if (eof_str_detected)
165 continue;
166 if (state == BACKSLASH) {
167 state = NORM;
168 goto set;
169 } else if (state == QUOTE) {
170 if (c == q) {
171 q = 0;
172 state = NORM;
173 } else {
174 goto set;
175 }
176 } else { /* if(state == NORM) */
177
178 if (ISSPACE(c)) {
179 if (s) {
180unexpected_eof:
181 state = SPACE;
182 c = 0;
183 goto set;
184 }
185 } else {
186 if (s == NULL)
187 s = p = buf;
188 if (c == '\\') {
189 state = BACKSLASH;
190 } else if (c == '\'' || c == '"') {
191 q = c;
192 state = QUOTE;
193 } else {
194set:
195 if ((p - buf) >= mc)
196 bb_error_msg_and_die("argument line too long");
197 *p++ = c;
198 }
199 }
200 }
201 if (state == SPACE) { /* word's delimiter or EOF detected */
202 if (q) {
203 bb_error_msg_and_die("unmatched %s quote",
204 q == '\'' ? "single" : "double");
205 }
206 /* word loaded */
207 if (eof_str) {
208 eof_str_detected = strcmp(s, eof_str) == 0;
209 }
210 if (!eof_str_detected) {
211 size_t lenght = (p - buf);
212
213 cur = xmalloc(sizeof(xlist_t) + lenght);
214 cur->data = memcpy(cur + 1, s, lenght);
215 cur->lenght = lenght;
216 cur->link = NULL;
217 if (prev == NULL) {
218 list_arg = cur;
219 } else {
220 prev->link = cur;
221 }
222 prev = cur;
223 line_l += lenght;
224 if (line_l > mc) {
225 /* stop memory usage :-) */
226 break;
227 }
228 }
229 s = NULL;
230 state = NORM;
231 }
232 }
233 return list_arg;
234}
235#else
236/* The variant does not support single quotes, double quotes or backslash */
237static xlist_t *process_stdin(xlist_t * list_arg,
238 const char *eof_str, size_t mc, char *buf)
239{
240
241 int c; /* current char */
242 int eof_str_detected = 0;
243 char *s = NULL; /* start word */
244 char *p = NULL; /* pointer to end word */
245 size_t line_l = 0; /* size loaded args line */
246 xlist_t *cur;
247 xlist_t *prev;
248
249 for (prev = cur = list_arg; cur; cur = cur->link) {
250 line_l += cur->lenght; /* previous allocated */
251 if (prev != cur)
252 prev = prev->link;
253 }
254
255 while (!eof_stdin_detected) {
256 c = getchar();
257 if (c == EOF) {
258 eof_stdin_detected++;
259 }
260 if (eof_str_detected)
261 continue;
262 if (c == EOF || ISSPACE(c)) {
263 if (s == NULL)
264 continue;
265 c = EOF;
266 }
267 if (s == NULL)
268 s = p = buf;
269 if ((p - buf) >= mc)
270 bb_error_msg_and_die("argument line too long");
271 *p++ = c == EOF ? 0 : c;
272 if (c == EOF) { /* word's delimiter or EOF detected */
273 /* word loaded */
274 if (eof_str) {
275 eof_str_detected = strcmp(s, eof_str) == 0;
276 }
277 if (!eof_str_detected) {
278 size_t lenght = (p - buf);
279
280 cur = xmalloc(sizeof(xlist_t) + lenght);
281 cur->data = memcpy(cur + 1, s, lenght);
282 cur->lenght = lenght;
283 cur->link = NULL;
284 if (prev == NULL) {
285 list_arg = cur;
286 } else {
287 prev->link = cur;
288 }
289 prev = cur;
290 line_l += lenght;
291 if (line_l > mc) {
292 /* stop memory usage :-) */
293 break;
294 }
295 s = NULL;
296 }
297 }
298 }
299 return list_arg;
300}
301#endif /* CONFIG_FEATURE_XARGS_SUPPORT_QUOTES */
302
303
304#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
305/* Prompt the user for a response, and
306 if the user responds affirmatively, return true;
307 otherwise, return false. Used "/dev/tty", not stdin. */
308static int xargs_ask_confirmation(void)
309{
310 static FILE *tty_stream;
311 int c, savec;
312
313 if (!tty_stream) {
314 tty_stream = fopen("/dev/tty", "r");
315 if (!tty_stream)
316 bb_perror_msg_and_die("/dev/tty");
317 /* pranoidal security by vodz */
318 fcntl(fileno(tty_stream), F_SETFD, FD_CLOEXEC);
319 }
320 fputs(" ?...", stderr);
321 fflush(stderr);
322 c = savec = getc(tty_stream);
323 while (c != EOF && c != '\n')
324 c = getc(tty_stream);
325 if (savec == 'y' || savec == 'Y')
326 return 1;
327 return 0;
328}
329
330# define OPT_INC_P 1
331#else
332# define OPT_INC_P 0
333# define xargs_ask_confirmation() 1
334#endif /* CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION */
335
336#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
337# define OPT_INC_X 1
338#else
339# define OPT_INC_X 0
340#endif
341
342#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
343static xlist_t *process0_stdin(xlist_t * list_arg, const char *eof_str,
344 size_t mc, char *buf)
345{
346 int c; /* current char */
347 char *s = NULL; /* start word */
348 char *p = NULL; /* pointer to end word */
349 size_t line_l = 0; /* size loaded args line */
350 xlist_t *cur;
351 xlist_t *prev;
352
353 for (prev = cur = list_arg; cur; cur = cur->link) {
354 line_l += cur->lenght; /* previous allocated */
355 if (prev != cur)
356 prev = prev->link;
357 }
358
359 while (!eof_stdin_detected) {
360 c = getchar();
361 if (c == EOF) {
362 eof_stdin_detected++;
363 if (s == NULL)
364 break;
365 c = 0;
366 }
367 if (s == NULL)
368 s = p = buf;
369 if ((p - buf) >= mc)
370 bb_error_msg_and_die("argument line too long");
371 *p++ = c;
372 if (c == 0) { /* word's delimiter or EOF detected */
373 /* word loaded */
374 size_t lenght = (p - buf);
375
376 cur = xmalloc(sizeof(xlist_t) + lenght);
377 cur->data = memcpy(cur + 1, s, lenght);
378 cur->lenght = lenght;
379 cur->link = NULL;
380 if (prev == NULL) {
381 list_arg = cur;
382 } else {
383 prev->link = cur;
384 }
385 prev = cur;
386 line_l += lenght;
387 if (line_l > mc) {
388 /* stop memory usage :-) */
389 break;
390 }
391 s = NULL;
392 }
393 }
394 return list_arg;
395}
396
397# define READ_ARGS(l, e, nmc, mc) (*read_args)(l, e, nmc, mc)
398# define OPT_INC_0 1 /* future use */
399#else
400# define OPT_INC_0 0 /* future use */
401# define READ_ARGS(l, e, nmc, mc) process_stdin(l, e, nmc, mc)
402#endif /* CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM */
403
404
405#define OPT_VERBOSE (1<<0)
406#define OPT_NO_EMPTY (1<<1)
407#define OPT_UPTO_NUMBER (1<<2)
408#define OPT_UPTO_SIZE (1<<3)
409#define OPT_EOF_STRING (1<<4)
410#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
411#define OPT_INTERACTIVE (1<<5)
412#else
413#define OPT_INTERACTIVE (0) /* require for algorithm &| */
414#endif
415#define OPT_TERMINATE (1<<(5+OPT_INC_P))
416#define OPT_ZEROTERM (1<<(5+OPT_INC_P+OPT_INC_X))
417/* next future
418#define OPT_NEXT_OTHER (1<<(5+OPT_INC_P+OPT_INC_X+OPT_INC_0))
419*/
420
421int xargs_main(int argc, char **argv)
422{
423 char **args;
424 int i, a, n;
425 xlist_t *list = NULL;
426 xlist_t *cur;
427 int child_error = 0;
428 char *max_args, *max_chars;
429 int n_max_arg;
430 size_t n_chars = 0;
431 long orig_arg_max;
432 const char *eof_str = "_";
433 unsigned long opt;
434 size_t n_max_chars;
435
436#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
437 xlist_t *(*read_args) (xlist_t *, const char *, size_t, char *) = process_stdin;
438#endif
439
440#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
441 bb_opt_complementaly = "pt";
442#endif
443
444 opt = bb_getopt_ulflags(argc, argv, "+trn:s:e::"
445#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
446 "p"
447#endif
448#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
449 "x"
450#endif
451#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
452 "0"
453#endif
454 ,&max_args, &max_chars, &eof_str);
455
456 a = argc - optind;
457 argv += optind;
458 if (a == 0) {
459 /* default behavior is to echo all the filenames */
460 *argv = "echo";
461 a++;
462 }
463
464 orig_arg_max = ARG_MAX;
465 if (orig_arg_max == -1)
466 orig_arg_max = LONG_MAX;
467 orig_arg_max -= 2048; /* POSIX.2 requires subtracting 2048. */
468 if ((opt & OPT_UPTO_SIZE)) {
469 n_max_chars = bb_xgetularg10_bnd(max_chars, 1, orig_arg_max);
470 for (i = 0; i < a; i++) {
471 n_chars += strlen(*argv) + 1;
472 }
473 if (n_max_chars < n_chars) {
474 bb_error_msg_and_die("can not fit single argument within argument list size limit");
475 }
476 n_max_chars -= n_chars;
477 } else {
478 /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which
479 have it at 1 meg). Things will work fine with a large ARG_MAX but it
480 will probably hurt the system more than it needs to; an array of this
481 size is allocated. */
482 if (orig_arg_max > 20 * 1024)
483 orig_arg_max = 20 * 1024;
484 n_max_chars = orig_arg_max;
485 }
486 max_chars = xmalloc(n_max_chars);
487
488 if ((opt & OPT_UPTO_NUMBER)) {
489 n_max_arg = bb_xgetularg10_bnd(max_args, 1, INT_MAX);
490 } else {
491 n_max_arg = n_max_chars;
492 }
493
494#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
495 if (opt & OPT_ZEROTERM)
496 read_args = process0_stdin;
497#endif
498
499 while ((list = READ_ARGS(list, eof_str, n_max_chars, max_chars)) != NULL ||
500 (opt & OPT_NO_EMPTY) == 0)
501 {
502 opt |= OPT_NO_EMPTY;
503 n = 0;
504 n_chars = 0;
505#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
506 for (cur = list; cur;) {
507 n_chars += cur->lenght;
508 n++;
509 cur = cur->link;
510 if (n_chars > n_max_chars || (n == n_max_arg && cur)) {
511 if (opt & OPT_TERMINATE)
512 bb_error_msg_and_die("argument list too long");
513 break;
514 }
515 }
516#else
517 for (cur = list; cur; cur = cur->link) {
518 n_chars += cur->lenght;
519 n++;
520 if (n_chars > n_max_chars || n == n_max_arg) {
521 break;
522 }
523 }
524#endif /* CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT */
525
526 /* allocating pointers for execvp:
527 a*arg, n*arg from stdin, NULL */
528 args = xcalloc(n + a + 1, sizeof(char *));
529
530 /* Store the command to be executed
531 (taken from the command line) */
532 for (i = 0; i < a; i++)
533 args[i] = argv[i];
534 /* (taken from stdin) */
535 for (cur = list; n; cur = cur->link) {
536 args[i++] = cur->data;
537 n--;
538 }
539
540 if ((opt & (OPT_INTERACTIVE | OPT_VERBOSE))) {
541 for (i = 0; args[i]; i++) {
542 if (i)
543 fputc(' ', stderr);
544 fputs(args[i], stderr);
545 }
546 if ((opt & OPT_INTERACTIVE) == 0)
547 fputc('\n', stderr);
548 }
549 if ((opt & OPT_INTERACTIVE) == 0 || xargs_ask_confirmation() != 0) {
550 child_error = xargs_exec(args);
551 }
552
553 /* clean up */
554 for (i = a; args[i]; i++) {
555 cur = list;
556 list = list->link;
557 free(cur);
558 }
559 free(args);
560 if (child_error > 0 && child_error != 123) {
561 break;
562 }
563 }
564#ifdef CONFIG_FEATURE_CLEAN_UP
565 free(max_chars);
566#endif
567 return child_error;
568}
569
570
571#ifdef TEST
572
573const char *bb_applet_name = "debug stuff usage";
574
575void bb_show_usage(void)
576{
577 fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n",
578 bb_applet_name);
579 exit(1);
580}
581
582int main(int argc, char **argv)
583{
584 return xargs_main(argc, argv);
585}
586#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 @@
1config
2config.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 @@
1/*
2 * applets.h - a listing of all busybox applets.
3 *
4 * If you write a new applet, you need to add an entry to this list to make
5 * busybox aware of it.
6 *
7 * It is CRUCIAL that this listing be kept in ascii order, otherwise the binary
8 * search lookup contributed by Gaute B Strokkenes stops working. If you value
9 * your kneecaps, you'll be sure to *make sure* that any changes made to this
10 * file result in the listing remaining in ascii order. You have been warned.
11 */
12
13#undef APPLET
14#undef APPLET_ODDNAME
15#undef APPLET_NOUSAGE
16
17
18#if defined(PROTOTYPES)
19 #define APPLET(a,b,c,d) extern int b(int argc, char **argv);
20 #define APPLET_NOUSAGE(a,b,c,d) extern int b(int argc, char **argv);
21 #define APPLET_ODDNAME(a,b,c,d,e) extern int b(int argc, char **argv);
22 extern const char usage_messages[];
23#elif defined(MAKE_USAGE)
24 #ifdef CONFIG_FEATURE_VERBOSE_USAGE
25 #define APPLET(a,b,c,d) a##_trivial_usage "\n\n" a##_full_usage "\0"
26 #define APPLET_NOUSAGE(a,b,c,d) "\b\0"
27 #define APPLET_ODDNAME(a,b,c,d,e) e##_trivial_usage "\n\n" e##_full_usage "\0"
28 #else
29 #define APPLET(a,b,c,d) a##_trivial_usage "\0"
30 #define APPLET_NOUSAGE(a,b,c,d) "\b\0"
31 #define APPLET_ODDNAME(a,b,c,d,e) e##_trivial_usage "\0"
32 #endif
33#elif defined(MAKE_LINKS)
34# define APPLET(a,b,c,d) LINK c a
35# define APPLET_NOUSAGE(a,b,c,d) LINK c a
36# define APPLET_ODDNAME(a,b,c,d,e) LINK c a
37#else
38 const struct BB_applet applets[] = {
39 #define APPLET(a,b,c,d) {#a,b,c,d},
40 #define APPLET_NOUSAGE(a,b,c,d) {a,b,c,d},
41 #define APPLET_ODDNAME(a,b,c,d,e) {a,b,c,d},
42#endif
43
44#ifdef CONFIG_INSTALL_NO_USR
45#define _BB_DIR_USR_BIN _BB_DIR_BIN
46#define _BB_DIR_USR_SBIN _BB_DIR_SBIN
47#endif
48
49
50
51#ifdef CONFIG_TEST
52 APPLET_NOUSAGE("[", test_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
53#endif
54#ifdef CONFIG_ADDGROUP
55 APPLET(addgroup, addgroup_main, _BB_DIR_BIN, _BB_SUID_NEVER)
56#endif
57#ifdef CONFIG_ADDUSER
58 APPLET(adduser, adduser_main, _BB_DIR_BIN, _BB_SUID_NEVER)
59#endif
60#ifdef CONFIG_ADJTIMEX
61 APPLET(adjtimex, adjtimex_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
62#endif
63#ifdef CONFIG_AR
64 APPLET(ar, ar_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
65#endif
66#ifdef CONFIG_ARPING
67 APPLET(arping, arping_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
68#endif
69#ifdef CONFIG_ASH
70 APPLET_NOUSAGE("ash", ash_main, _BB_DIR_BIN, _BB_SUID_NEVER)
71#endif
72#ifdef CONFIG_AWK
73 APPLET(awk, awk_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
74#endif
75#ifdef CONFIG_BASENAME
76 APPLET(basename, basename_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
77#endif
78#ifdef CONFIG_BUNZIP2
79 APPLET(bunzip2, bunzip2_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
80#endif
81 APPLET_NOUSAGE("busybox", busybox_main, _BB_DIR_BIN, _BB_SUID_MAYBE)
82#ifdef CONFIG_BUNZIP2
83 APPLET(bzcat, bunzip2_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
84#endif
85#ifdef CONFIG_CAL
86 APPLET(cal, cal_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
87#endif
88#ifdef CONFIG_CAT
89 APPLET(cat, cat_main, _BB_DIR_BIN, _BB_SUID_NEVER)
90#endif
91#ifdef CONFIG_CHGRP
92 APPLET(chgrp, chgrp_main, _BB_DIR_BIN, _BB_SUID_NEVER)
93#endif
94#ifdef CONFIG_CHMOD
95 APPLET(chmod, chmod_main, _BB_DIR_BIN, _BB_SUID_NEVER)
96#endif
97#ifdef CONFIG_CHOWN
98 APPLET(chown, chown_main, _BB_DIR_BIN, _BB_SUID_NEVER)
99#endif
100#ifdef CONFIG_CHROOT
101 APPLET(chroot, chroot_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
102#endif
103#ifdef CONFIG_CHVT
104 APPLET(chvt, chvt_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
105#endif
106#ifdef CONFIG_CLEAR
107 APPLET(clear, clear_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
108#endif
109#ifdef CONFIG_CMP
110 APPLET(cmp, cmp_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
111#endif
112#ifdef CONFIG_CP
113 APPLET(cp, cp_main, _BB_DIR_BIN, _BB_SUID_NEVER)
114#endif
115#ifdef CONFIG_CPIO
116 APPLET(cpio, cpio_main, _BB_DIR_BIN, _BB_SUID_NEVER)
117#endif
118#ifdef CONFIG_CROND
119 APPLET(crond, crond_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
120#endif
121#ifdef CONFIG_CRONTAB
122 APPLET(crontab, crontab_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS)
123#endif
124#ifdef CONFIG_CUT
125 APPLET(cut, cut_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
126#endif
127#ifdef CONFIG_DATE
128 APPLET(date, date_main, _BB_DIR_BIN, _BB_SUID_NEVER)
129#endif
130#ifdef CONFIG_DC
131 APPLET(dc, dc_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
132#endif
133#ifdef CONFIG_DD
134 APPLET(dd, dd_main, _BB_DIR_BIN, _BB_SUID_NEVER)
135#endif
136#ifdef CONFIG_DEALLOCVT
137 APPLET(deallocvt, deallocvt_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
138#endif
139#ifdef CONFIG_DELGROUP
140 APPLET(delgroup, delgroup_main, _BB_DIR_BIN, _BB_SUID_NEVER)
141#endif
142#ifdef CONFIG_DELUSER
143 APPLET(deluser, deluser_main, _BB_DIR_BIN, _BB_SUID_NEVER)
144#endif
145#ifdef CONFIG_DEVFSD
146 APPLET(devfsd, devfsd_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
147#endif
148#ifdef CONFIG_DF
149 APPLET(df, df_main, _BB_DIR_BIN, _BB_SUID_NEVER)
150#endif
151#ifdef CONFIG_DIRNAME
152 APPLET(dirname, dirname_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
153#endif
154#ifdef CONFIG_DMESG
155 APPLET(dmesg, dmesg_main, _BB_DIR_BIN, _BB_SUID_NEVER)
156#endif
157#ifdef CONFIG_DOS2UNIX
158 APPLET(dos2unix, dos2unix_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
159#endif
160#ifdef CONFIG_DPKG
161 APPLET(dpkg, dpkg_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
162#endif
163#ifdef CONFIG_DPKG_DEB
164 APPLET_ODDNAME("dpkg-deb", dpkg_deb_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER, dpkg_deb)
165#endif
166#ifdef CONFIG_DU
167 APPLET(du, du_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
168#endif
169#ifdef CONFIG_DUMPKMAP
170 APPLET(dumpkmap, dumpkmap_main, _BB_DIR_BIN, _BB_SUID_NEVER)
171#endif
172#ifdef CONFIG_DUMPLEASES
173 APPLET(dumpleases, dumpleases_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
174#endif
175#ifdef CONFIG_ECHO
176 APPLET(echo, echo_main, _BB_DIR_BIN, _BB_SUID_NEVER)
177#endif
178#if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS)
179 APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
180#endif
181#ifdef CONFIG_ENV
182 APPLET(env, env_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
183#endif
184#ifdef CONFIG_EXPR
185 APPLET(expr, expr_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
186#endif
187#ifdef CONFIG_FALSE
188 APPLET(false, false_main, _BB_DIR_BIN, _BB_SUID_NEVER)
189#endif
190#ifdef CONFIG_FBSET
191 APPLET(fbset, fbset_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
192#endif
193#ifdef CONFIG_FDFLUSH
194 APPLET(fdflush, fdflush_main, _BB_DIR_BIN, _BB_SUID_NEVER)
195#endif
196#ifdef CONFIG_FDFORMAT
197 APPLET(fdformat, fdformat_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
198#endif
199#ifdef CONFIG_FDISK
200 APPLET(fdisk, fdisk_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
201#endif
202#if defined(CONFIG_FEATURE_GREP_FGREP_ALIAS)
203 APPLET_NOUSAGE("fgrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
204#endif
205#ifdef CONFIG_FIND
206 APPLET(find, find_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
207#endif
208#ifdef CONFIG_FOLD
209 APPLET(fold, fold_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
210#endif
211#ifdef CONFIG_FREE
212 APPLET(free, free_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
213#endif
214#ifdef CONFIG_FREERAMDISK
215 APPLET(freeramdisk, freeramdisk_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
216#endif
217#ifdef CONFIG_FSCK_MINIX
218 APPLET_ODDNAME("fsck.minix", fsck_minix_main, _BB_DIR_SBIN, _BB_SUID_NEVER, fsck_minix)
219#endif
220#ifdef CONFIG_FTPGET
221 APPLET(ftpget, ftpgetput_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
222#endif
223#ifdef CONFIG_FTPPUT
224 APPLET(ftpput, ftpgetput_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
225#endif
226#ifdef CONFIG_GETOPT
227 APPLET(getopt, getopt_main, _BB_DIR_BIN, _BB_SUID_NEVER)
228#endif
229#ifdef CONFIG_GETTY
230 APPLET(getty, getty_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
231#endif
232#ifdef CONFIG_GREP
233 APPLET(grep, grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
234#endif
235#ifdef CONFIG_GUNZIP
236 APPLET(gunzip, gunzip_main, _BB_DIR_BIN, _BB_SUID_NEVER)
237#endif
238#ifdef CONFIG_GZIP
239 APPLET(gzip, gzip_main, _BB_DIR_BIN, _BB_SUID_NEVER)
240#endif
241#ifdef CONFIG_HALT
242 APPLET(halt, halt_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
243#endif
244#ifdef CONFIG_HDPARM
245 APPLET(hdparm, hdparm_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
246#endif
247#ifdef CONFIG_HEAD
248 APPLET(head, head_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
249#endif
250#ifdef CONFIG_HEXDUMP
251 APPLET(hexdump, hexdump_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
252#endif
253#ifdef CONFIG_HOSTID
254 APPLET(hostid, hostid_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
255#endif
256#ifdef CONFIG_HOSTNAME
257 APPLET(hostname, hostname_main, _BB_DIR_BIN, _BB_SUID_NEVER)
258#endif
259#ifdef CONFIG_HTTPD
260 APPLET(httpd, httpd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
261#endif
262#ifdef CONFIG_HUSH
263 APPLET_NOUSAGE("hush", hush_main, _BB_DIR_BIN, _BB_SUID_NEVER)
264#endif
265#ifdef CONFIG_HWCLOCK
266 APPLET(hwclock, hwclock_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
267#endif
268#ifdef CONFIG_ID
269 APPLET(id, id_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
270#endif
271#ifdef CONFIG_IFCONFIG
272 APPLET(ifconfig, ifconfig_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
273#endif
274#ifdef CONFIG_IFUPDOWN
275 APPLET(ifdown, ifupdown_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
276#endif
277#ifdef CONFIG_IFUPDOWN
278 APPLET(ifup, ifupdown_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
279#endif
280#ifdef CONFIG_INETD
281 APPLET(inetd, inetd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
282#endif
283#ifdef CONFIG_INIT
284 APPLET(init, init_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
285#endif
286#ifdef CONFIG_INSMOD
287 APPLET(insmod, insmod_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
288#endif
289#ifdef CONFIG_INSTALL
290 APPLET(install, install_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
291#endif
292#ifdef CONFIG_IP
293 APPLET(ip, ip_main, _BB_DIR_BIN, _BB_SUID_NEVER)
294#endif
295#ifdef CONFIG_IPADDR
296 APPLET(ipaddr, ipaddr_main, _BB_DIR_BIN, _BB_SUID_NEVER)
297#endif
298#ifdef CONFIG_IPCALC
299 APPLET(ipcalc, ipcalc_main, _BB_DIR_BIN, _BB_SUID_NEVER)
300#endif
301#ifdef CONFIG_IPLINK
302 APPLET(iplink, iplink_main, _BB_DIR_BIN, _BB_SUID_NEVER)
303#endif
304#ifdef CONFIG_IPROUTE
305 APPLET(iproute, iproute_main, _BB_DIR_BIN, _BB_SUID_NEVER)
306#endif
307#ifdef CONFIG_IPTUNNEL
308 APPLET(iptunnel, iptunnel_main, _BB_DIR_BIN, _BB_SUID_NEVER)
309#endif
310#ifdef CONFIG_KILL
311 APPLET(kill, kill_main, _BB_DIR_BIN, _BB_SUID_NEVER)
312#endif
313#ifdef CONFIG_KILLALL
314 APPLET(killall, kill_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
315#endif
316#ifdef CONFIG_KLOGD
317 APPLET(klogd, klogd_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
318#endif
319#ifdef CONFIG_LASH
320 APPLET(lash, lash_main, _BB_DIR_BIN, _BB_SUID_NEVER)
321#endif
322#ifdef CONFIG_LAST
323 APPLET(last, last_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
324#endif
325#ifdef CONFIG_LENGTH
326 APPLET(length, length_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
327#endif
328#ifdef CONFIG_FEATURE_INITRD
329 APPLET_NOUSAGE("linuxrc", init_main, _BB_DIR_ROOT, _BB_SUID_NEVER)
330#endif
331#ifdef CONFIG_LN
332 APPLET(ln, ln_main, _BB_DIR_BIN, _BB_SUID_NEVER)
333#endif
334#ifdef CONFIG_LOADFONT
335 APPLET(loadfont, loadfont_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
336#endif
337#ifdef CONFIG_LOADKMAP
338 APPLET(loadkmap, loadkmap_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
339#endif
340#ifdef CONFIG_LOGGER
341 APPLET(logger, logger_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
342#endif
343#ifdef CONFIG_LOGIN
344 APPLET(login, login_main, _BB_DIR_BIN, _BB_SUID_ALWAYS)
345#endif
346#ifdef CONFIG_LOGNAME
347 APPLET(logname, logname_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
348#endif
349#ifdef CONFIG_LOGREAD
350 APPLET(logread, logread_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
351#endif
352#ifdef CONFIG_LOSETUP
353 APPLET(losetup, losetup_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
354#endif
355#ifdef CONFIG_LS
356 APPLET(ls, ls_main, _BB_DIR_BIN, _BB_SUID_NEVER)
357#endif
358#ifdef CONFIG_LSMOD
359 APPLET(lsmod, lsmod_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
360#endif
361#ifdef CONFIG_MAKEDEVS
362 APPLET(makedevs, makedevs_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
363#endif
364#ifdef CONFIG_MD5SUM
365 APPLET(md5sum, md5sum_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
366#endif
367#ifdef CONFIG_MESG
368 APPLET(mesg, mesg_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
369#endif
370#ifdef CONFIG_MKDIR
371 APPLET(mkdir, mkdir_main, _BB_DIR_BIN, _BB_SUID_NEVER)
372#endif
373#ifdef CONFIG_MKFIFO
374 APPLET(mkfifo, mkfifo_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
375#endif
376#ifdef CONFIG_MKFS_MINIX
377 APPLET_ODDNAME("mkfs.minix", mkfs_minix_main, _BB_DIR_SBIN, _BB_SUID_NEVER, mkfs_minix)
378#endif
379#ifdef CONFIG_MKNOD
380 APPLET(mknod, mknod_main, _BB_DIR_BIN, _BB_SUID_NEVER)
381#endif
382#ifdef CONFIG_MKSWAP
383 APPLET(mkswap, mkswap_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
384#endif
385#ifdef CONFIG_MKTEMP
386 APPLET(mktemp, mktemp_main, _BB_DIR_BIN, _BB_SUID_NEVER)
387#endif
388#ifdef CONFIG_MODPROBE
389 APPLET(modprobe, modprobe_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
390#endif
391#ifdef CONFIG_MORE
392 APPLET(more, more_main, _BB_DIR_BIN, _BB_SUID_NEVER)
393#endif
394#ifdef CONFIG_MOUNT
395 APPLET(mount, mount_main, _BB_DIR_BIN, _BB_SUID_NEVER)
396#endif
397#ifdef CONFIG_MSH
398 APPLET_NOUSAGE("msh", msh_main, _BB_DIR_BIN, _BB_SUID_NEVER)
399#endif
400#ifdef CONFIG_MT
401 APPLET(mt, mt_main, _BB_DIR_BIN, _BB_SUID_NEVER)
402#endif
403#ifdef CONFIG_MV
404 APPLET(mv, mv_main, _BB_DIR_BIN, _BB_SUID_NEVER)
405#endif
406#ifdef CONFIG_NAMEIF
407 APPLET(nameif, nameif_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
408#endif
409#ifdef CONFIG_NC
410 APPLET(nc, nc_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
411#endif
412#ifdef CONFIG_NETSTAT
413 APPLET(netstat, netstat_main, _BB_DIR_BIN, _BB_SUID_NEVER)
414#endif
415#ifdef CONFIG_NSLOOKUP
416 APPLET(nslookup, nslookup_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
417#endif
418#ifdef CONFIG_OD
419 APPLET(od, od_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
420#endif
421#ifdef CONFIG_OPENVT
422 APPLET(openvt, openvt_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
423#endif
424#ifdef CONFIG_PASSWD
425 APPLET(passwd, passwd_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS)
426#endif
427#ifdef CONFIG_PATCH
428 APPLET(patch, patch_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
429#endif
430#ifdef CONFIG_PIDOF
431 APPLET(pidof, pidof_main, _BB_DIR_BIN, _BB_SUID_NEVER)
432#endif
433#ifdef CONFIG_PING
434 APPLET(ping, ping_main, _BB_DIR_BIN, _BB_SUID_MAYBE)
435#endif
436#ifdef CONFIG_PING6
437 APPLET(ping6, ping6_main, _BB_DIR_BIN, _BB_SUID_NEVER)
438#endif
439#ifdef CONFIG_PIPE_PROGRESS
440 APPLET_NOUSAGE("pipe_progress", pipe_progress_main, _BB_DIR_BIN, _BB_SUID_NEVER)
441#endif
442#ifdef CONFIG_PIVOT_ROOT
443 APPLET(pivot_root, pivot_root_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
444#endif
445#ifdef CONFIG_POWEROFF
446 APPLET(poweroff, poweroff_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
447#endif
448#ifdef CONFIG_PRINTF
449 APPLET(printf, printf_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
450#endif
451#ifdef CONFIG_PS
452 APPLET(ps, ps_main, _BB_DIR_BIN, _BB_SUID_NEVER)
453#endif
454#ifdef CONFIG_PWD
455 APPLET(pwd, pwd_main, _BB_DIR_BIN, _BB_SUID_NEVER)
456#endif
457#ifdef CONFIG_RDATE
458 APPLET(rdate, rdate_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
459#endif
460#ifdef CONFIG_READLINK
461 APPLET(readlink, readlink_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
462#endif
463#ifdef CONFIG_REALPATH
464 APPLET(realpath, realpath_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
465#endif
466#ifdef CONFIG_REBOOT
467 APPLET(reboot, reboot_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
468#endif
469#ifdef CONFIG_RENICE
470 APPLET(renice, renice_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
471#endif
472#ifdef CONFIG_RESET
473 APPLET(reset, reset_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
474#endif
475#ifdef CONFIG_RM
476 APPLET(rm, rm_main, _BB_DIR_BIN, _BB_SUID_NEVER)
477#endif
478#ifdef CONFIG_RMDIR
479 APPLET(rmdir, rmdir_main, _BB_DIR_BIN, _BB_SUID_NEVER)
480#endif
481#ifdef CONFIG_RMMOD
482 APPLET(rmmod, rmmod_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
483#endif
484#ifdef CONFIG_ROUTE
485 APPLET(route, route_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
486#endif
487#ifdef CONFIG_RPM
488 APPLET(rpm, rpm_main, _BB_DIR_BIN, _BB_SUID_NEVER)
489#endif
490#ifdef CONFIG_RPM2CPIO
491 APPLET(rpm2cpio, rpm2cpio_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
492#endif
493#ifdef CONFIG_RUN_PARTS
494 APPLET_ODDNAME("run-parts", run_parts_main, _BB_DIR_BIN, _BB_SUID_NEVER, run_parts)
495#endif
496#ifdef CONFIG_RX
497 APPLET(rx, rx_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
498#endif
499#ifdef CONFIG_SED
500 APPLET(sed, sed_main, _BB_DIR_BIN, _BB_SUID_NEVER)
501#endif
502#ifdef CONFIG_SEQ
503 APPLET(seq, seq_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
504#endif
505#ifdef CONFIG_SETKEYCODES
506 APPLET(setkeycodes, setkeycodes_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
507#endif
508#if defined(CONFIG_FEATURE_SH_IS_ASH) && defined(CONFIG_ASH)
509 APPLET_NOUSAGE("sh", ash_main, _BB_DIR_BIN, _BB_SUID_NEVER)
510#elif defined(CONFIG_FEATURE_SH_IS_HUSH) && defined(CONFIG_HUSH)
511 APPLET_NOUSAGE("sh", hush_main, _BB_DIR_BIN, _BB_SUID_NEVER)
512#elif defined(CONFIG_FEATURE_SH_IS_LASH) && defined(CONFIG_LASH)
513 APPLET_NOUSAGE("sh", lash_main, _BB_DIR_BIN, _BB_SUID_NEVER)
514#elif defined(CONFIG_FEATURE_SH_IS_MSH) && defined(CONFIG_MSH)
515 APPLET_NOUSAGE("sh", msh_main, _BB_DIR_BIN, _BB_SUID_NEVER)
516#endif
517#ifdef CONFIG_SHA1SUM
518 APPLET(sha1sum, sha1sum_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
519#endif
520#ifdef CONFIG_SLEEP
521 APPLET(sleep, sleep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
522#endif
523#ifdef CONFIG_SORT
524 APPLET(sort, sort_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
525#endif
526#ifdef CONFIG_START_STOP_DAEMON
527 APPLET_ODDNAME("start-stop-daemon", start_stop_daemon_main, _BB_DIR_SBIN, _BB_SUID_NEVER, start_stop_daemon)
528#endif
529#ifdef CONFIG_STRINGS
530 APPLET(strings, strings_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
531#endif
532#ifdef CONFIG_STTY
533 APPLET(stty, stty_main, _BB_DIR_BIN, _BB_SUID_NEVER)
534#endif
535#ifdef CONFIG_SU
536 APPLET(su, su_main, _BB_DIR_BIN, _BB_SUID_ALWAYS)
537#endif
538#ifdef CONFIG_SULOGIN
539 APPLET(sulogin, sulogin_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
540#endif
541#ifdef CONFIG_SWAPONOFF
542 APPLET(swapoff, swap_on_off_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
543#endif
544#ifdef CONFIG_SWAPONOFF
545 APPLET(swapon, swap_on_off_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
546#endif
547#ifdef CONFIG_SYNC
548 APPLET(sync, sync_main, _BB_DIR_BIN, _BB_SUID_NEVER)
549#endif
550#ifdef CONFIG_SYSCTL
551 APPLET(sysctl, sysctl_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
552#endif
553#ifdef CONFIG_SYSLOGD
554 APPLET(syslogd, syslogd_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
555#endif
556#ifdef CONFIG_TAIL
557 APPLET(tail, tail_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
558#endif
559#ifdef CONFIG_TAR
560 APPLET(tar, tar_main, _BB_DIR_BIN, _BB_SUID_NEVER)
561#endif
562#ifdef CONFIG_TEE
563 APPLET(tee, tee_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
564#endif
565#ifdef CONFIG_TELNET
566 APPLET(telnet, telnet_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
567#endif
568#ifdef CONFIG_TELNETD
569 APPLET(telnetd, telnetd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
570#endif
571#ifdef CONFIG_TEST
572 APPLET(test, test_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
573#endif
574#ifdef CONFIG_TFTP
575 APPLET(tftp, tftp_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
576#endif
577#ifdef CONFIG_TIME
578 APPLET(time, time_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
579#endif
580#ifdef CONFIG_TOP
581 APPLET(top, top_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
582#endif
583#ifdef CONFIG_TOUCH
584 APPLET(touch, touch_main, _BB_DIR_BIN, _BB_SUID_NEVER)
585#endif
586#ifdef CONFIG_TR
587 APPLET(tr, tr_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
588#endif
589#ifdef CONFIG_TRACEROUTE
590 APPLET(traceroute, traceroute_main, _BB_DIR_USR_BIN, _BB_SUID_MAYBE)
591#endif
592#ifdef CONFIG_TRUE
593 APPLET(true, true_main, _BB_DIR_BIN, _BB_SUID_NEVER)
594#endif
595#ifdef CONFIG_TTY
596 APPLET(tty, tty_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
597#endif
598#ifdef CONFIG_UDHCPC
599 APPLET(udhcpc, udhcpc_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
600#endif
601#ifdef CONFIG_UDHCPD
602 APPLET(udhcpd, udhcpd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)
603#endif
604#ifdef CONFIG_UMOUNT
605 APPLET(umount, umount_main, _BB_DIR_BIN, _BB_SUID_NEVER)
606#endif
607#ifdef CONFIG_UNAME
608 APPLET(uname, uname_main, _BB_DIR_BIN, _BB_SUID_NEVER)
609#endif
610#ifdef CONFIG_UNCOMPRESS
611 APPLET(uncompress, uncompress_main, _BB_DIR_BIN, _BB_SUID_NEVER)
612#endif
613#ifdef CONFIG_UNIQ
614 APPLET(uniq, uniq_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
615#endif
616#ifdef CONFIG_UNIX2DOS
617 APPLET(unix2dos, dos2unix_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
618#endif
619#ifdef CONFIG_UNZIP
620 APPLET(unzip, unzip_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
621#endif
622#ifdef CONFIG_UPTIME
623 APPLET(uptime, uptime_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
624#endif
625#ifdef CONFIG_USLEEP
626 APPLET(usleep, usleep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
627#endif
628#ifdef CONFIG_UUDECODE
629 APPLET(uudecode, uudecode_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
630#endif
631#ifdef CONFIG_UUENCODE
632 APPLET(uuencode, uuencode_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
633#endif
634#ifdef CONFIG_VCONFIG
635 APPLET(vconfig, vconfig_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
636#endif
637#ifdef CONFIG_VI
638 APPLET(vi, vi_main, _BB_DIR_BIN, _BB_SUID_NEVER)
639#endif
640#ifdef CONFIG_VLOCK
641 APPLET(vlock, vlock_main, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS)
642#endif
643#ifdef CONFIG_WATCH
644 APPLET(watch, watch_main, _BB_DIR_BIN, _BB_SUID_NEVER)
645#endif
646#ifdef CONFIG_WATCHDOG
647 APPLET(watchdog, watchdog_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
648#endif
649#ifdef CONFIG_WC
650 APPLET(wc, wc_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
651#endif
652#ifdef CONFIG_WGET
653 APPLET(wget, wget_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
654#endif
655#ifdef CONFIG_WHICH
656 APPLET(which, which_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
657#endif
658#ifdef CONFIG_WHO
659 APPLET(who, who_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
660#endif
661#ifdef CONFIG_WHOAMI
662 APPLET(whoami, whoami_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
663#endif
664#ifdef CONFIG_XARGS
665 APPLET(xargs, xargs_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
666#endif
667#ifdef CONFIG_YES
668 APPLET(yes, yes_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
669#endif
670#ifdef CONFIG_GUNZIP
671 APPLET(zcat, gunzip_main, _BB_DIR_BIN, _BB_SUID_NEVER)
672#endif
673
674#if !defined(PROTOTYPES) && !defined(MAKE_USAGE)
675 { 0,NULL,0 }
676};
677
678#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Busybox main internal header file
4 *
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
21 * Permission has been granted to redistribute this code under the GPL.
22 *
23 */
24#ifndef _BB_INTERNAL_H_
25#define _BB_INTERNAL_H_ 1
26
27#include "config.h"
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <stdarg.h>
32#include <sys/stat.h>
33#include <sys/types.h>
34
35#if __GNU_LIBRARY__ < 5
36#ifndef __dietlibc__
37#error "Sorry, libc5 is not supported"
38#endif
39#endif
40
41#ifndef BB_EXTRA_VERSION
42#define BB_BANNER "BusyBox v" BB_VER " (" BB_BT ")"
43#else
44#define BB_BANNER "BusyBox v" BB_VER " (" BB_EXTRA_VERSION ")"
45#endif
46
47#ifdef DMALLOC
48#include <dmalloc.h>
49#endif
50
51#include <features.h>
52
53/* Pull in the utility routines from libbb */
54#include "libbb.h"
55
56enum Location {
57 _BB_DIR_ROOT = 0,
58 _BB_DIR_BIN,
59 _BB_DIR_SBIN,
60 _BB_DIR_USR_BIN,
61 _BB_DIR_USR_SBIN
62};
63
64enum SUIDRoot {
65 _BB_SUID_NEVER = 0,
66 _BB_SUID_MAYBE,
67 _BB_SUID_ALWAYS
68};
69
70struct BB_applet {
71 const char *name;
72 int (*main) (int argc, char **argv);
73 enum Location location:4;
74 enum SUIDRoot need_suid:4;
75};
76
77/* From busybox.c */
78extern const struct BB_applet applets[];
79
80/* Automagically pull in all the applet function prototypes and
81 * applet usage strings. These are all of the form:
82 * extern int foo_main(int argc, char **argv);
83 * extern const char foo_usage[];
84 * These are all autogenerated from the set of currently defined applets.
85 */
86#define PROTOTYPES
87#include "applets.h"
88#undef PROTOTYPES
89
90#ifdef CONFIG_FEATURE_BUFFERS_GO_ON_STACK
91#define RESERVE_CONFIG_BUFFER(buffer,len) char buffer[len]
92#define RESERVE_CONFIG_UBUFFER(buffer,len) unsigned char buffer[len]
93#define RELEASE_CONFIG_BUFFER(buffer) ((void)0)
94#else
95#ifdef CONFIG_FEATURE_BUFFERS_GO_IN_BSS
96#define RESERVE_CONFIG_BUFFER(buffer,len) static char buffer[len]
97#define RESERVE_CONFIG_UBUFFER(buffer,len) static unsigned char buffer[len]
98#define RELEASE_CONFIG_BUFFER(buffer) ((void)0)
99#else
100#define RESERVE_CONFIG_BUFFER(buffer,len) char *buffer=xmalloc(len)
101#define RESERVE_CONFIG_UBUFFER(buffer,len) unsigned char *buffer=xmalloc(len)
102#define RELEASE_CONFIG_BUFFER(buffer) free (buffer)
103#endif
104#endif
105
106
107#ifndef RB_POWER_OFF
108/* Stop system and switch power off if possible. */
109#define RB_POWER_OFF 0x4321fedc
110#endif
111
112/* Try to pull in PATH_MAX */
113#include <limits.h>
114
115/* for PATH_MAX on systems that don't have it in limits.h */
116#include <sys/param.h>
117#ifndef PATH_MAX
118#define PATH_MAX 256
119#endif
120
121#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 @@
1#define F_IGNORE 0x01 /* %_A */
2#define F_SETREP 0x02 /* rep count set, not default */
3#define F_ADDRESS 0x001 /* print offset */
4#define F_BPAD 0x002 /* blank pad */
5#define F_C 0x004 /* %_c */
6#define F_CHAR 0x008 /* %c */
7#define F_DBL 0x010 /* %[EefGf] */
8#define F_INT 0x020 /* %[di] */
9#define F_P 0x040 /* %_p */
10#define F_STR 0x080 /* %s */
11#define F_U 0x100 /* %_u */
12#define F_UINT 0x200 /* %[ouXx] */
13#define F_TEXT 0x400 /* no conversions */
14
15enum _vflag { ALL, DUP, FIRST, WAIT }; /* -v values */
16
17typedef struct _pr {
18 struct _pr *nextpr; /* next print unit */
19 unsigned int flags; /* flag values */
20 int bcnt; /* byte count */
21 char *cchar; /* conversion character */
22 char *fmt; /* printf format */
23 char *nospace; /* no whitespace version */
24} PR;
25
26typedef struct _fu {
27 struct _fu *nextfu; /* next format unit */
28 struct _pr *nextpr; /* next print unit */
29 unsigned int flags; /* flag values */
30 int reps; /* repetition count */
31 int bcnt; /* byte count */
32 char *fmt; /* format string */
33} FU;
34
35typedef struct _fs { /* format strings */
36 struct _fs *nextfs; /* linked list of format strings */
37 struct _fu *nextfu; /* linked list of format units */
38 int bcnt;
39} FS;
40
41extern void bb_dump_add(const char *fmt);
42extern int bb_dump_dump (char **argv);
43extern int bb_dump_size(FS * fs);
44
45extern FS *bb_dump_fshead; /* head of format strings */
46extern int bb_dump_blocksize; /* data block size */
47extern int bb_dump_length; /* max bytes to read */
48extern enum _vflag bb_dump_vflag;
49extern 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 @@
1/* Copyright (C) 1991,92,95,96,97,98,99,2000,01 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, write to the Free
16 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17 02111-1307 USA. */
18
19/*
20 * POSIX Standard: 9.2.1 Group Database Access <grp.h>
21 */
22
23
24#if !defined CONFIG_USE_BB_PWD_GRP
25#include <grp.h>
26
27#else
28
29#ifndef _GRP_H
30#define _GRP_H 1
31
32
33#include <sys/types.h>
34#include <features.h>
35#include <stdio.h>
36
37
38/* The group structure. */
39struct group
40{
41 char *gr_name; /* Group name. */
42 char *gr_passwd; /* Password. */
43 gid_t gr_gid; /* Group ID. */
44 char **gr_mem; /* Member list. */
45};
46
47
48/* Rewind the group-file stream. */
49extern void setgrent (void);
50
51/* Close the group-file stream. */
52extern void endgrent (void);
53
54/* Read an entry from the group-file stream, opening it if necessary. */
55extern struct group *getgrent (void);
56
57/* Read a group entry from STREAM. */
58extern struct group *fgetgrent (FILE *__stream);
59
60/* Write the given entry onto the given stream. */
61extern int putgrent (__const struct group *__restrict __p,
62 FILE *__restrict __f);
63
64/* Search for an entry with a matching group ID. */
65extern struct group *getgrgid (gid_t __gid);
66
67/* Search for an entry with a matching group name. */
68extern struct group *getgrnam (__const char *__name);
69
70/* Reentrant versions of some of the functions above.
71
72 PLEASE NOTE: the `getgrent_r' function is not (yet) standardized.
73 The interface may change in later versions of this library. But
74 the interface is designed following the principals used for the
75 other reentrant functions so the chances are good this is what the
76 POSIX people would choose. */
77
78extern int getgrent_r (struct group *__restrict __resultbuf,
79 char *__restrict __buffer, size_t __buflen,
80 struct group **__restrict __result);
81
82/* Search for an entry with a matching group ID. */
83extern int getgrgid_r (gid_t __gid, struct group *__restrict __resultbuf,
84 char *__restrict __buffer, size_t __buflen,
85 struct group **__restrict __result);
86
87/* Search for an entry with a matching group name. */
88extern int getgrnam_r (__const char *__restrict __name,
89 struct group *__restrict __resultbuf,
90 char *__restrict __buffer, size_t __buflen,
91 struct group **__restrict __result);
92
93/* Read a group entry from STREAM. This function is not standardized
94 an probably never will. */
95extern int fgetgrent_r (FILE *__restrict __stream,
96 struct group *__restrict __resultbuf,
97 char *__restrict __buffer, size_t __buflen,
98 struct group **__restrict __result);
99
100/* Set the group set for the current user to GROUPS (N of them). */
101extern int setgroups (size_t __n, __const gid_t *__groups);
102
103/* Store at most *NGROUPS members of the group set for USER into
104 *GROUPS. Also include GROUP. The actual number of groups found is
105 returned in *NGROUPS. Return -1 if the if *NGROUPS is too small. */
106extern int getgrouplist (__const char *__user, gid_t __group,
107 gid_t *__groups, int *__ngroups);
108
109/* Initialize the group set for the current user
110 by reading the group database and using all groups
111 of which USER is a member. Also include GROUP. */
112extern int initgroups (__const char *__user, gid_t __group);
113
114
115#endif /* grp.h */
116#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 @@
1/*
2 * stolen from net-tools-1.59 and stripped down for busybox by
3 * Erik Andersen <andersen@codepoet.org>
4 *
5 * Heavily modified by Manuel Novoa III Mar 12, 2001
6 *
7 * Version: $Id: inet_common.h,v 1.4 2004/03/10 07:42:37 mjn3 Exp $
8 *
9 */
10
11#include <features.h>
12#include <sys/types.h>
13#include <sys/socket.h>
14#include <arpa/inet.h>
15
16
17extern const char bb_INET_default[]; /* = "default" */
18
19/* hostfirst!=0 If we expect this to be a hostname,
20 try hostname database first
21 */
22extern int INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst);
23
24
25/* numeric: & 0x8000: default instead of *,
26 * & 0x4000: host instead of net,
27 * & 0x0fff: don't resolve
28 */
29extern int INET_rresolve(char *name, size_t len, struct sockaddr_in *s_in,
30 int numeric, unsigned int netmask);
31
32extern int INET6_resolve(const char *name, struct sockaddr_in6 *sin6);
33extern 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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Busybox main internal header file
4 *
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
21 * Permission has been granted to redistribute this code under the GPL.
22 *
23 */
24#ifndef __LIBCONFIG_H__
25#define __LIBCONFIG_H__ 1
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <stdarg.h>
30#include <sys/stat.h>
31#include <sys/types.h>
32#include <regex.h>
33#include <termios.h>
34#include <stdint.h>
35
36#include <netdb.h>
37
38#ifdef DMALLOC
39#include <dmalloc.h>
40#endif
41
42#include <features.h>
43
44#include "config.h"
45#ifdef CONFIG_SELINUX
46#include <proc_secure.h>
47#endif
48
49#include "pwd_.h"
50#include "grp_.h"
51#ifdef CONFIG_FEATURE_SHADOWPASSWDS
52#include "shadow_.h"
53#endif
54#ifdef CONFIG_FEATURE_SHA1_PASSWORDS
55# include "sha1.h"
56#endif
57
58/* Convenience macros to test the version of gcc. */
59#if defined __GNUC__ && defined __GNUC_MINOR__
60# define __GNUC_PREREQ(maj, min) \
61 ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
62#else
63# define __GNUC_PREREQ(maj, min) 0
64#endif
65
66/* __restrict is known in EGCS 1.2 and above. */
67#if !__GNUC_PREREQ (2,92)
68# define __restrict /* Ignore */
69#endif
70
71/* Some useful definitions */
72#define FALSE ((int) 0)
73#define TRUE ((int) 1)
74#define SKIP ((int) 2)
75
76/* for mtab.c */
77#define MTAB_GETMOUNTPT '1'
78#define MTAB_GETDEVICE '2'
79
80#define BUF_SIZE 8192
81#define EXPAND_ALLOC 1024
82
83/* Macros for min/max. */
84#ifndef MIN
85#define MIN(a,b) (((a)<(b))?(a):(b))
86#endif
87
88#ifndef MAX
89#define MAX(a,b) (((a)>(b))?(a):(b))
90#endif
91
92extern void bb_show_usage(void) __attribute__ ((noreturn));
93extern void bb_error_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
94extern void bb_error_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
95extern void bb_perror_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
96extern void bb_perror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
97extern void bb_vherror_msg(const char *s, va_list p);
98extern void bb_herror_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2)));
99extern void bb_herror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2)));
100
101extern void bb_perror_nomsg_and_die(void) __attribute__ ((noreturn));
102extern void bb_perror_nomsg(void);
103
104/* These two are used internally -- you shouldn't need to use them */
105extern void bb_verror_msg(const char *s, va_list p) __attribute__ ((format (printf, 1, 0)));
106extern void bb_vperror_msg(const char *s, va_list p) __attribute__ ((format (printf, 1, 0)));
107
108extern const char *bb_mode_string(int mode);
109extern int is_directory(const char *name, int followLinks, struct stat *statBuf);
110
111extern int remove_file(const char *path, int flags);
112extern int copy_file(const char *source, const char *dest, int flags);
113extern ssize_t safe_read(int fd, void *buf, size_t count);
114extern ssize_t bb_full_read(int fd, void *buf, size_t len);
115extern ssize_t safe_write(int fd, const void *buf, size_t count);
116extern ssize_t bb_full_write(int fd, const void *buf, size_t len);
117extern int recursive_action(const char *fileName, int recurse,
118 int followLinks, int depthFirst,
119 int (*fileAction) (const char *fileName, struct stat* statbuf, void* userData),
120 int (*dirAction) (const char *fileName, struct stat* statbuf, void* userData),
121 void* userData);
122
123extern int bb_parse_mode( const char* s, mode_t* theMode);
124extern long bb_xgetlarg(const char *arg, int base, long lower, long upper);
125
126extern unsigned long bb_baud_to_value(speed_t speed);
127extern speed_t bb_value_to_baud(unsigned long value);
128
129extern int get_kernel_revision(void);
130
131extern int get_console_fd(void);
132extern struct mntent *find_mount_point(const char *name, const char *table);
133extern void write_mtab(char* blockDevice, char* directory,
134 char* filesystemType, long flags, char* string_flags);
135extern void erase_mtab(const char * name);
136extern long *find_pid_by_name( const char* pidName);
137extern char *find_real_root_device_name(void);
138extern char *bb_get_line_from_file(FILE *file);
139extern char *bb_get_chomped_line_from_file(FILE *file);
140extern int bb_copyfd_size(int fd1, int fd2, const off_t size);
141extern int bb_copyfd_eof(int fd1, int fd2);
142extern void bb_xprint_and_close_file(FILE *file);
143extern int bb_xprint_file_by_name(const char *filename);
144extern char bb_process_escape_sequence(const char **ptr);
145extern char *bb_get_last_path_component(char *path);
146extern FILE *bb_wfopen(const char *path, const char *mode);
147extern FILE *bb_xfopen(const char *path, const char *mode);
148
149//#warning rename?
150extern int bb_fclose_nonstdin(FILE *f);
151extern void bb_fflush_stdout_and_exit(int retval) __attribute__ ((noreturn));
152
153extern const char *bb_opt_complementaly;
154extern const struct option *bb_applet_long_options;
155extern unsigned long bb_getopt_ulflags(int argc, char **argv, const char *applet_opts, ...);
156
157//#warning rename?
158extern FILE *bb_wfopen_input(const char *filename);
159
160extern int bb_vfprintf(FILE * __restrict stream, const char * __restrict format,
161 va_list arg) __attribute__ ((format (printf, 2, 0)));
162extern int bb_vprintf(const char * __restrict format, va_list arg)
163 __attribute__ ((format (printf, 1, 0)));
164extern int bb_fprintf(FILE * __restrict stream, const char * __restrict format, ...)
165 __attribute__ ((format (printf, 2, 3)));
166extern int bb_printf(const char * __restrict format, ...)
167 __attribute__ ((format (printf, 1, 2)));
168
169//#warning rename to xferror_filename?
170extern void bb_xferror(FILE *fp, const char *fn);
171extern void bb_xferror_stdout(void);
172extern void bb_xfflush_stdout(void);
173
174extern void bb_warn_ignoring_args(int n);
175
176extern void chomp(char *s);
177extern void trim(char *s);
178extern const char *bb_skip_whitespace(const char *);
179
180extern struct BB_applet *find_applet_by_name(const char *name);
181void run_applet_by_name(const char *name, int argc, char **argv);
182
183//#warning is this needed anymore?
184#ifndef DMALLOC
185extern void *xmalloc (size_t size);
186extern void *xrealloc(void *old, size_t size);
187extern void *xcalloc(size_t nmemb, size_t size);
188#endif
189extern char *bb_xstrdup (const char *s);
190extern char *bb_xstrndup (const char *s, int n);
191extern char *safe_strncpy(char *dst, const char *src, size_t size);
192extern int safe_strtoi(char *arg, int* value);
193extern int safe_strtod(char *arg, double* value);
194extern int safe_strtol(char *arg, long* value);
195extern int safe_strtoul(char *arg, unsigned long* value);
196
197struct suffix_mult {
198 const char *suffix;
199 unsigned int mult;
200};
201
202extern unsigned long bb_xgetularg_bnd_sfx(const char *arg, int base,
203 unsigned long lower,
204 unsigned long upper,
205 const struct suffix_mult *suffixes);
206extern unsigned long bb_xgetularg_bnd(const char *arg, int base,
207 unsigned long lower,
208 unsigned long upper);
209extern unsigned long bb_xgetularg10_bnd(const char *arg,
210 unsigned long lower,
211 unsigned long upper);
212extern unsigned long bb_xgetularg10(const char *arg);
213
214extern long bb_xgetlarg_bnd_sfx(const char *arg, int base,
215 long lower,
216 long upper,
217 const struct suffix_mult *suffixes);
218extern long bb_xgetlarg10_sfx(const char *arg, const struct suffix_mult *suffixes);
219
220
221//#warning pitchable now?
222extern unsigned long bb_xparse_number(const char *numstr,
223 const struct suffix_mult *suffixes);
224
225
226//#warning change names?
227
228/* These parse entries in /etc/passwd and /etc/group. This is desirable
229 * for BusyBox since we want to avoid using the glibc NSS stuff, which
230 * increases target size and is often not needed embedded systems. */
231extern long my_getpwnam(const char *name);
232extern long my_getgrnam(const char *name);
233extern char * my_getug(char *buffer, char *idname, long id, int bufsize, char prefix);
234extern char * my_getpwuid(char *name, long uid, int bufsize);
235extern char * my_getgrgid(char *group, long gid, int bufsize);
236extern char *bb_askpass(int timeout, const char * prompt);
237
238extern int device_open(const char *device, int mode);
239
240extern int del_loop(const char *device);
241extern int set_loop(const char *device, const char *file, int offset, int *loopro);
242extern char *find_unused_loop_device (void);
243
244
245#if (__GLIBC__ < 2)
246extern int vdprintf(int d, const char *format, va_list ap);
247#endif
248
249int nfsmount(const char *spec, const char *node, int *flags,
250 char **extra_opts, char **mount_opts, int running_bg);
251
252/* Include our own copy of struct sysinfo to avoid binary compatability
253 * problems with Linux 2.4, which changed things. Grumble, grumble. */
254struct sysinfo {
255 long uptime; /* Seconds since boot */
256 unsigned long loads[3]; /* 1, 5, and 15 minute load averages */
257 unsigned long totalram; /* Total usable main memory size */
258 unsigned long freeram; /* Available memory size */
259 unsigned long sharedram; /* Amount of shared memory */
260 unsigned long bufferram; /* Memory used by buffers */
261 unsigned long totalswap; /* Total swap space size */
262 unsigned long freeswap; /* swap space still available */
263 unsigned short procs; /* Number of current processes */
264 unsigned short pad; /* Padding needed for m68k */
265 unsigned long totalhigh; /* Total high memory size */
266 unsigned long freehigh; /* Available high memory size */
267 unsigned int mem_unit; /* Memory unit size in bytes */
268 char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding: libc5 uses this.. */
269};
270extern int sysinfo (struct sysinfo* info);
271
272enum {
273 KILOBYTE = 1024,
274 MEGABYTE = (KILOBYTE*1024),
275 GIGABYTE = (MEGABYTE*1024)
276};
277const char *make_human_readable_str(unsigned long long size,
278 unsigned long block_size, unsigned long display_unit);
279
280int bb_ask_confirmation(void);
281int klogctl(int type, char * b, int len);
282
283char *xgetcwd(char *cwd);
284char *xreadlink(const char *path);
285char *concat_path_file(const char *path, const char *filename);
286char *concat_subpath_file(const char *path, const char *filename);
287char *last_char_is(const char *s, int c);
288
289int read_package_field(const char *package_buffer, char **field_name, char **field_value);
290//#warning yuk!
291char *fgets_str(FILE *file, const char *terminating_string);
292
293extern int uncompress(int fd_in, int fd_out);
294extern int inflate(int in, int out);
295
296extern struct hostent *xgethostbyname(const char *name);
297extern struct hostent *xgethostbyname2(const char *name, int af);
298extern int create_icmp_socket(void);
299extern int create_icmp6_socket(void);
300extern int xconnect(struct sockaddr_in *s_addr);
301extern unsigned short bb_lookup_port(const char *port, const char *protocol, unsigned short default_port);
302extern void bb_lookup_host(struct sockaddr_in *s_in, const char *host);
303
304//#warning wrap this?
305char *dirname (char *path);
306
307int bb_make_directory (char *path, long mode, int flags);
308
309const char *u_signal_names(const char *str_sig, int *signo, int startnum);
310char *bb_simplify_path(const char *path);
311
312enum { /* DO NOT CHANGE THESE VALUES! cp.c depends on them. */
313 FILEUTILS_PRESERVE_STATUS = 1,
314 FILEUTILS_DEREFERENCE = 2,
315 FILEUTILS_RECUR = 4,
316 FILEUTILS_FORCE = 8,
317 FILEUTILS_INTERACTIVE = 16
318};
319
320extern const char *bb_applet_name;
321
322extern const char * const bb_msg_full_version;
323extern const char * const bb_msg_memory_exhausted;
324extern const char * const bb_msg_invalid_date;
325extern const char * const bb_msg_io_error;
326extern const char * const bb_msg_write_error;
327extern const char * const bb_msg_name_longer_than_foo;
328extern const char * const bb_msg_unknown;
329extern const char * const bb_msg_can_not_create_raw_socket;
330extern const char * const bb_msg_perm_denied_are_you_root;
331extern const char * const bb_msg_standard_input;
332extern const char * const bb_msg_standard_output;
333
334extern const char * const bb_path_nologin_file;
335extern const char * const bb_path_passwd_file;
336extern const char * const bb_path_shadow_file;
337extern const char * const bb_path_gshadow_file;
338extern const char * const bb_path_group_file;
339extern const char * const bb_path_securetty_file;
340extern const char * const bb_path_motd_file;
341
342/*
343 * You can change LIBBB_DEFAULT_LOGIN_SHELL, but don`t use,
344 * use bb_default_login_shell and next defines,
345 * if you LIBBB_DEFAULT_LOGIN_SHELL change,
346 * don`t lose change increment constant!
347 */
348#define LIBBB_DEFAULT_LOGIN_SHELL "-/bin/sh"
349
350extern const char * const bb_default_login_shell;
351/* "/bin/sh" */
352#define DEFAULT_SHELL (bb_default_login_shell+1)
353/* "sh" */
354#define DEFAULT_SHELL_SHORT_NAME (bb_default_login_shell+6)
355
356
357extern const char bb_path_mtab_file[];
358
359extern int bb_default_error_retval;
360
361#ifdef CONFIG_FEATURE_DEVFS
362# define CURRENT_VC "/dev/vc/0"
363# define VC_1 "/dev/vc/1"
364# define VC_2 "/dev/vc/2"
365# define VC_3 "/dev/vc/3"
366# define VC_4 "/dev/vc/4"
367# define VC_5 "/dev/vc/5"
368#if defined(__sh__) || defined(__H8300H__) || defined(__H8300S__)
369/* Yes, this sucks, but both SH (including sh64) and H8 have a SCI(F) for their
370 respective serial ports .. as such, we can't use the common device paths for
371 these. -- PFM */
372# define SC_0 "/dev/ttsc/0"
373# define SC_1 "/dev/ttsc/1"
374# define SC_FORMAT "/dev/ttsc/%d"
375#else
376# define SC_0 "/dev/tts/0"
377# define SC_1 "/dev/tts/1"
378# define SC_FORMAT "/dev/tts/%d"
379#endif
380# define VC_FORMAT "/dev/vc/%d"
381# define LOOP_FORMAT "/dev/loop/%d"
382# define FB_0 "/dev/fb/0"
383#else
384# define CURRENT_VC "/dev/tty0"
385# define VC_1 "/dev/tty1"
386# define VC_2 "/dev/tty2"
387# define VC_3 "/dev/tty3"
388# define VC_4 "/dev/tty4"
389# define VC_5 "/dev/tty5"
390#if defined(__sh__) || defined(__H8300H__) || defined(__H8300S__)
391# define SC_0 "/dev/ttySC0"
392# define SC_1 "/dev/ttySC1"
393# define SC_FORMAT "/dev/ttySC%d"
394#else
395# define SC_0 "/dev/ttyS0"
396# define SC_1 "/dev/ttyS1"
397# define SC_FORMAT "/dev/ttyS%d"
398#endif
399# define VC_FORMAT "/dev/tty%d"
400# define LOOP_FORMAT "/dev/loop%d"
401# define FB_0 "/dev/fb0"
402#endif
403
404//#warning put these in .o files
405
406/* The following devices are the same on devfs and non-devfs systems. */
407#define CURRENT_TTY "/dev/tty"
408#define CONSOLE_DEV "/dev/console"
409
410int is_in_ino_dev_hashtable(const struct stat *statbuf, char **name);
411void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name);
412void reset_ino_dev_hashtable(void);
413
414/* Stupid gcc always includes its own builtin strlen()... */
415extern size_t bb_strlen(const char *string);
416#define strlen(x) bb_strlen(x)
417
418void bb_xasprintf(char **string_ptr, const char *format, ...) __attribute__ ((format (printf, 2, 3)));
419
420
421#define FAIL_DELAY 3
422extern void change_identity ( const struct passwd *pw );
423extern const char *change_identity_e2str ( const struct passwd *pw );
424extern void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args
425#ifdef CONFIG_SELINUX
426 , security_id_t sid
427#endif
428);
429extern int run_parts(char **args, const unsigned char test_mode, char **env);
430extern int restricted_shell ( const char *shell );
431extern void setup_environment ( const char *shell, int loginshell, int changeenv, const struct passwd *pw );
432extern int correct_password ( const struct passwd *pw );
433extern char *pw_encrypt(const char *clear, const char *salt);
434extern struct spwd *pwd_to_spwd(const struct passwd *pw);
435extern int obscure(const char *old, const char *newval, const struct passwd *pwdp);
436
437extern int bb_xopen(const char *pathname, int flags);
438extern ssize_t bb_xread(int fd, void *buf, size_t count);
439extern void bb_xread_all(int fd, void *buf, size_t count);
440extern unsigned char bb_xread_char(int fd);
441
442typedef struct {
443 int pid;
444 char user[9];
445 char state[4];
446 unsigned long rss;
447 int ppid;
448#ifdef FEATURE_CPU_USAGE_PERCENTAGE
449 unsigned pcpu;
450 unsigned long stime, utime;
451#endif
452 char *cmd;
453
454 /* basename of executable file in call to exec(2),
455 size from kernel headers */
456 char short_cmd[16];
457} procps_status_t;
458
459extern procps_status_t * procps_scan(int save_user_arg0
460#ifdef CONFIG_SELINUX
461 , int use_selinux, security_id_t *sid
462#endif
463);
464extern unsigned short compare_string_array(const char *string_array[], const char *key);
465
466extern int my_query_module(const char *name, int which, void **buf, size_t *bufsize, size_t *ret);
467
468typedef struct llist_s {
469 char *data;
470 struct llist_s *link;
471} llist_t;
472extern llist_t *llist_add_to(llist_t *old_head, char *new_item);
473
474extern void print_login_issue(const char *issue_file, const char *tty);
475extern void print_login_prompt(void);
476
477extern void vfork_daemon_rexec(int nochdir, int noclose,
478 int argc, char **argv, char *foreground_opt);
479extern void get_terminal_width_height(int fd, int *width, int *height);
480extern unsigned long get_ug_id(const char *s, long (*my_getxxnam)(const char *));
481extern void xregcomp(regex_t *preg, const char *regex, int cflags);
482
483#define HASH_SHA1 1
484#define HASH_MD5 2
485extern int hash_fd(int fd, const size_t size, const uint8_t hash_algo, uint8_t *hashval);
486
487#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 @@
1/* Copyright (C) 1991,92,95,96,97,98,99,2001 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, write to the Free
16 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17 02111-1307 USA. */
18
19/*
20 * POSIX Standard: 9.2.2 User Database Access <pwd.h>
21 */
22
23#if !defined CONFIG_USE_BB_PWD_GRP
24#include <pwd.h>
25
26#else
27
28#ifndef _PWD_H
29#define _PWD_H 1
30
31#include <sys/types.h>
32#include <features.h>
33#include <stdio.h>
34
35/* The passwd structure. */
36struct passwd
37{
38 char *pw_name; /* Username. */
39 char *pw_passwd; /* Password. */
40 uid_t pw_uid; /* User ID. */
41 gid_t pw_gid; /* Group ID. */
42 char *pw_gecos; /* Real name. */
43 char *pw_dir; /* Home directory. */
44 char *pw_shell; /* Shell program. */
45};
46
47
48/* Rewind the password-file stream. */
49extern void setpwent (void);
50
51/* Close the password-file stream. */
52extern void endpwent (void);
53
54/* Read an entry from the password-file stream, opening it if necessary. */
55extern struct passwd *getpwent (void);
56
57/* Read an entry from STREAM. */
58extern struct passwd *fgetpwent (FILE *__stream);
59
60/* Write the given entry onto the given stream. */
61extern int putpwent (__const struct passwd *__restrict __p,
62 FILE *__restrict __f);
63
64/* Search for an entry with a matching user ID. */
65extern struct passwd *getpwuid (uid_t __uid);
66
67/* Search for an entry with a matching username. */
68extern struct passwd *getpwnam (__const char *__name);
69
70/* Reentrant versions of some of the functions above.
71
72 PLEASE NOTE: the `getpwent_r' function is not (yet) standardized.
73 The interface may change in later versions of this library. But
74 the interface is designed following the principals used for the
75 other reentrant functions so the chances are good this is what the
76 POSIX people would choose. */
77
78extern int getpwent_r (struct passwd *__restrict __resultbuf,
79 char *__restrict __buffer, size_t __buflen,
80 struct passwd **__restrict __result);
81
82extern int getpwuid_r (uid_t __uid,
83 struct passwd *__restrict __resultbuf,
84 char *__restrict __buffer, size_t __buflen,
85 struct passwd **__restrict __result);
86
87extern int getpwnam_r (__const char *__restrict __name,
88 struct passwd *__restrict __resultbuf,
89 char *__restrict __buffer, size_t __buflen,
90 struct passwd **__restrict __result);
91
92
93/* Read an entry from STREAM. This function is not standardized and
94 probably never will. */
95extern int fgetpwent_r (FILE *__restrict __stream,
96 struct passwd *__restrict __resultbuf,
97 char *__restrict __buffer, size_t __buflen,
98 struct passwd **__restrict __result);
99
100/* Re-construct the password-file line for the given uid
101 in the given buffer. This knows the format that the caller
102 will expect, but this need not be the format of the password file. */
103extern int getpw (uid_t __uid, char *__buffer);
104
105#endif /* pwd.h */
106#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 @@
1/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, write to the Free
16 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17 02111-1307 USA. */
18
19/* Declaration of types and functions for shadow password suite. */
20
21#if !defined CONFIG_USE_BB_SHADOW
22#include <shadow.h>
23#else
24
25#ifndef _SHADOW_H
26#define _SHADOW_H 1
27
28#include <stdio.h>
29
30/* Paths to the user database files. */
31#ifndef _PATH_SHADOW
32#define _PATH_SHADOW "/etc/shadow"
33#endif
34#define SHADOW _PATH_SHADOW
35
36
37/* Structure of the password file. */
38struct spwd
39{
40 char *sp_namp; /* Login name. */
41 char *sp_pwdp; /* Encrypted password. */
42 long int sp_lstchg; /* Date of last change. */
43 long int sp_min; /* Minimum number of days between changes. */
44 long int sp_max; /* Maximum number of days between changes. */
45 long int sp_warn; /* Number of days to warn user to change
46 the password. */
47 long int sp_inact; /* Number of days the account may be
48 inactive. */
49 long int sp_expire; /* Number of days since 1970-01-01 until
50 account expires. */
51 unsigned long int sp_flag; /* Reserved. */
52};
53
54
55/* Open database for reading. */
56extern void setspent (void);
57
58/* Close database. */
59extern void endspent (void);
60
61/* Get next entry from database, perhaps after opening the file. */
62extern struct spwd *getspent (void);
63
64/* Get shadow entry matching NAME. */
65extern struct spwd *getspnam (__const char *__name);
66
67/* Read shadow entry from STRING. */
68extern struct spwd *sgetspent (__const char *__string);
69
70/* Read next shadow entry from STREAM. */
71extern struct spwd *fgetspent (FILE *__stream);
72
73/* Write line containing shadow password entry to stream. */
74extern int putspent (__const struct spwd *__p, FILE *__stream);
75
76/* Reentrant versions of some of the functions above. */
77extern int getspent_r (struct spwd *__result_buf, char *__buffer,
78 size_t __buflen, struct spwd **__result);
79
80extern int getspnam_r (__const char *__name, struct spwd *__result_buf,
81 char *__buffer, size_t __buflen,
82 struct spwd **__result)__THROW;
83
84extern int sgetspent_r (__const char *__string, struct spwd *__result_buf,
85 char *__buffer, size_t __buflen,
86 struct spwd **__result);
87
88extern int fgetspent_r (FILE *__stream, struct spwd *__result_buf,
89 char *__buffer, size_t __buflen,
90 struct spwd **__result);
91/* Protect password file against multi writers. */
92extern int lckpwdf (void);
93
94/* Unlock password file. */
95extern int ulckpwdf (void);
96
97#endif /* shadow.h */
98#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 @@
1#ifndef __UNARCHIVE_H__
2#define __UNARCHIVE_H__
3
4#define ARCHIVE_PRESERVE_DATE 1
5#define ARCHIVE_CREATE_LEADING_DIRS 2
6#define ARCHIVE_EXTRACT_UNCONDITIONAL 4
7#define ARCHIVE_EXTRACT_QUIET 8
8#define ARCHIVE_EXTRACT_NEWER 16
9
10#include <sys/types.h>
11#include <stdio.h>
12#include "libbb.h"
13
14typedef struct file_headers_s {
15 char *name;
16 char *link_name;
17 off_t size;
18 uid_t uid;
19 gid_t gid;
20 mode_t mode;
21 time_t mtime;
22 dev_t device;
23} file_header_t;
24
25typedef struct archive_handle_s {
26 /* define if the header and data compenent should processed */
27 char (*filter)(struct archive_handle_s *);
28 llist_t *accept;
29 llist_t *reject;
30 llist_t *passed; /* List of files that have successfully been worked on */
31
32 /* Contains the processed header entry */
33 file_header_t *file_header;
34
35 /* process the header component, e.g. tar -t */
36 void (*action_header)(const file_header_t *);
37
38 /* process the data component, e.g. extract to filesystem */
39 void (*action_data)(struct archive_handle_s *);
40
41 /* How to process any sub archive, e.g. get_header_tar_gz */
42 char (*action_data_subarchive)(struct archive_handle_s *);
43
44 /* Contains the handle to a sub archive */
45 struct archive_handle_s *sub_archive;
46
47 /* The raw stream as read from disk or stdin */
48 int src_fd;
49
50 /* Count the number of bytes processed */
51 off_t offset;
52
53 /* Function that skips data: read_by_char or read_by_skip */
54 void (*seek)(const struct archive_handle_s *archive_handle, const unsigned int amount);
55
56 /* Temporary storage */
57 char *buffer;
58
59 /* Misc. stuff */
60 unsigned char flags;
61
62} archive_handle_t;
63
64extern archive_handle_t *init_handle(void);
65
66extern char filter_accept_all(archive_handle_t *archive_handle);
67extern char filter_accept_list(archive_handle_t *archive_handle);
68extern char filter_accept_list_reassign(archive_handle_t *archive_handle);
69extern char filter_accept_reject_list(archive_handle_t *archive_handle);
70
71extern void unpack_ar_archive(archive_handle_t *ar_archive);
72
73extern void data_skip(archive_handle_t *archive_handle);
74extern void data_extract_all(archive_handle_t *archive_handle);
75extern void data_extract_to_stdout(archive_handle_t *archive_handle);
76extern void data_extract_to_buffer(archive_handle_t *archive_handle);
77
78extern void header_skip(const file_header_t *file_header);
79extern void header_list(const file_header_t *file_header);
80extern void header_verbose_list(const file_header_t *file_header);
81
82extern void check_header_gzip(int src_fd);
83
84extern char get_header_ar(archive_handle_t *archive_handle);
85extern char get_header_cpio(archive_handle_t *archive_handle);
86extern char get_header_tar(archive_handle_t *archive_handle);
87extern char get_header_tar_bz2(archive_handle_t *archive_handle);
88extern char get_header_tar_gz(archive_handle_t *archive_handle);
89
90extern void seek_by_jump(const archive_handle_t *archive_handle, const unsigned int amount);
91extern void seek_by_char(const archive_handle_t *archive_handle, const unsigned int amount);
92
93extern void archive_xread_all(const archive_handle_t *archive_handle, void *buf, const size_t count);
94extern ssize_t archive_xread_all_eof(archive_handle_t *archive_handle, unsigned char *buf, size_t count);
95
96extern void data_align(archive_handle_t *archive_handle, const unsigned short boundary);
97extern const llist_t *find_list_entry(const llist_t *list, const char *filename);
98
99extern int uncompressStream(int src_fd, int dst_fd);
100extern void inflate_init(unsigned int bufsize);
101extern int inflate_unzip(int in, int out);
102extern int inflate_gunzip(int in, int out);
103
104extern int open_transformer(int src_fd, int (*transformer)(int src_fd, int dst_fd));
105
106
107#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 @@
1#ifndef __BB_USAGE_H__
2#define __BB_USAGE_H__
3
4#define addgroup_trivial_usage \
5 "[-g GID] group_name [user_name]"
6#define addgroup_full_usage \
7 "Adds a group to the system\n\n" \
8 "Options:\n" \
9 "\t-g GID\t\tspecify gid"
10
11#define adduser_trivial_usage \
12 "[OPTIONS] user_name"
13#define adduser_full_usage \
14 "Adds a user to the system\n\n" \
15 "Options:\n" \
16 "\t-h DIR\t\tAssign home directory DIR\n" \
17 "\t-g GECOS\tAssign gecos field GECOS\n" \
18 "\t-s SHELL\tAssign login shell SHELL\n" \
19 "\t-G\t\tAdd the user to existing group GROUP\n" \
20 "\t-S\t\tcreate a system user (ignored)\n" \
21 "\t-D\t\tDo not assign a password (logins still possible via ssh)\n" \
22 "\t-H\t\tDo not create the home directory"
23
24#define adjtimex_trivial_usage \
25 "[-q] [-o offset] [-f frequency] [-p timeconstant] [-t tick]"
26#define adjtimex_full_usage \
27 "Reads and optionally sets system timebase parameters.\n" \
28 "See adjtimex(2).\n\n" \
29 "Options:\n" \
30 "\t-q\t\tquiet mode - do not print\n" \
31 "\t-o offset\ttime offset, microseconds\n" \
32 "\t-f frequency\tfrequency adjust, integer kernel units (65536 is 1ppm)\n" \
33 "\t\t\t(positive values make the system clock run fast)\n" \
34 "\t-t tick\t\tmicroseconds per tick, usually 10000\n" \
35 "\t-p timeconstant"
36
37#define ar_trivial_usage \
38 "[-o] [-v] [-p] [-t] [-x] ARCHIVE FILES"
39#define ar_full_usage \
40 "Extract or list FILES from an ar archive.\n\n" \
41 "Options:\n" \
42 "\t-o\t\tpreserve original dates\n" \
43 "\t-p\t\textract to stdout\n" \
44 "\t-t\t\tlist\n" \
45 "\t-x\t\textract\n" \
46 "\t-v\t\tverbosely list files processed"
47
48#define arping_trivial_usage \
49 "[-fqbDUA] [-c count] [-w timeout] [-I device] [-s sender] target\n"
50#define arping_full_usage \
51 "Ping hosts by ARP requests/replies.\n\n" \
52 "Options:\n" \
53 "\t-f\t\tQuit on first ARP reply\n" \
54 "\t-q\t\tBe quiet\n" \
55 "\t-b\t\tKeep broadcasting, don't go unicast\n" \
56 "\t-D\t\tDuplicated address detection mode\n" \
57 "\t-U\t\tUnsolicited ARP mode, update your neighbours\n" \
58 "\t-A\t\tARP answer mode, update your neighbours\n" \
59 "\t-c count\tStop after sending count ARP request packets\n" \
60 "\t-w timeout\tTime to wait for ARP reply, in seconds\n" \
61 "\t-I device\tOutgoing interface name, default is eth0\n" \
62 "\t-s sender\tSet specific sender IP address\n" \
63 "\ttarget\t\tTarget IP address of ARP request"
64
65#define ash_trivial_usage \
66 "[FILE]...\n" \
67 "or: ash -c command [args]...\n"
68#define ash_full_usage \
69 "The ash shell (command interpreter)"
70
71#define awk_trivial_usage \
72 "[OPTION]... [program-text] [FILE ...]"
73#define awk_full_usage \
74 "Options:\n" \
75 "\t-v var=val\t\tassign value 'val' to variable 'var'\n" \
76 "\t-F sep\t\tuse 'sep' as field separator\n" \
77 "\t-f progname\t\tread program source from file 'progname'"
78
79#define basename_trivial_usage \
80 "FILE [SUFFIX]"
81#define basename_full_usage \
82 "Strips directory path and suffixes from FILE.\n" \
83 "If specified, also removes any trailing SUFFIX."
84#define basename_example_usage \
85 "$ basename /usr/local/bin/foo\n" \
86 "foo\n" \
87 "$ basename /usr/local/bin/\n" \
88 "bin\n" \
89 "$ basename /foo/bar.txt .txt\n" \
90 "bar"
91
92#define bunzip2_trivial_usage \
93 "[OPTION]... [FILE]"
94#define bunzip2_full_usage \
95 "Uncompress FILE (or standard input if FILE is '-' or omitted).\n\n" \
96 "Options:\n" \
97 "\t-c\tWrite output to standard output\n" \
98 "\t-f\tForce"
99
100#define bzcat_trivial_usage \
101 "FILE"
102#define bzcat_full_usage \
103 "Uncompress to stdout."
104
105#define cal_trivial_usage \
106 "[-jy] [[month] year]"
107#define cal_full_usage \
108 "Display a calendar.\n" \
109 "\nOptions:\n" \
110 "\t-j\tUse julian dates.\n" \
111 "\t-y\tDisplay the entire year."
112
113#define cat_trivial_usage \
114 "[-u] [FILE]..."
115#define cat_full_usage \
116 "Concatenates FILE(s) and prints them to stdout.\n\n" \
117 "Options:\n" \
118 "\t-u\tignored since unbuffered i/o is always used"
119#define cat_example_usage \
120 "$ cat /proc/uptime\n" \
121 "110716.72 17.67"
122
123#define chgrp_trivial_usage \
124 "[OPTION]... GROUP FILE..."
125#define chgrp_full_usage \
126 "Change the group membership of each FILE to GROUP.\n" \
127 "\nOptions:\n" \
128 "\t-R\tChanges files and directories recursively."
129#define chgrp_example_usage \
130 "$ ls -l /tmp/foo\n" \
131 "-r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo\n" \
132 "$ chgrp root /tmp/foo\n" \
133 "$ ls -l /tmp/foo\n" \
134 "-r--r--r-- 1 andersen root 0 Apr 12 18:25 /tmp/foo\n"
135
136#define chmod_trivial_usage \
137 "[-R] MODE[,MODE]... FILE..."
138#define chmod_full_usage \
139 "Each MODE is one or more of the letters ugoa, one of the\n" \
140 "symbols +-= and one or more of the letters rwxst.\n\n" \
141 "Options:\n" \
142 "\t-R\tChanges files and directories recursively."
143#define chmod_example_usage \
144 "$ ls -l /tmp/foo\n" \
145 "-rw-rw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n" \
146 "$ chmod u+x /tmp/foo\n" \
147 "$ ls -l /tmp/foo\n" \
148 "-rwxrw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo*\n" \
149 "$ chmod 444 /tmp/foo\n" \
150 "$ ls -l /tmp/foo\n" \
151 "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n"
152
153#define chown_trivial_usage \
154 "[ -Rh ]... OWNER[<.|:>[GROUP]] FILE..."
155#define chown_full_usage \
156 "Change the owner and/or group of each FILE to OWNER and/or GROUP.\n" \
157 "\nOptions:\n" \
158 "\t-R\tChanges files and directories recursively.\n" \
159 "\t-h\tDo not dereference symbolic links."
160#define chown_example_usage \
161 "$ ls -l /tmp/foo\n" \
162 "-r--r--r-- 1 andersen andersen 0 Apr 12 18:25 /tmp/foo\n" \
163 "$ chown root /tmp/foo\n" \
164 "$ ls -l /tmp/foo\n" \
165 "-r--r--r-- 1 root andersen 0 Apr 12 18:25 /tmp/foo\n" \
166 "$ chown root.root /tmp/foo\n" \
167 "ls -l /tmp/foo\n" \
168 "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n"
169
170#define chroot_trivial_usage \
171 "NEWROOT [COMMAND...]"
172#define chroot_full_usage \
173 "Run COMMAND with root directory set to NEWROOT."
174#define chroot_example_usage \
175 "$ ls -l /bin/ls\n" \
176 "lrwxrwxrwx 1 root root 12 Apr 13 00:46 /bin/ls -> /BusyBox\n" \
177 "# mount /dev/hdc1 /mnt -t minix\n" \
178 "# chroot /mnt\n" \
179 "# ls -l /bin/ls\n" \
180 "-rwxr-xr-x 1 root root 40816 Feb 5 07:45 /bin/ls*\n"
181
182#define chvt_trivial_usage \
183 "N"
184#define chvt_full_usage \
185 "Changes the foreground virtual terminal to /dev/ttyN"
186
187#define clear_trivial_usage \
188 ""
189#define clear_full_usage \
190 "Clear screen."
191
192#define cmp_trivial_usage \
193 "[-l] [-s] FILE1 [FILE2]"
194#define cmp_full_usage \
195 "Compare files. Compares FILE1 vs stdin if FILE2 is not specified.\n\n" \
196 "Options:\n" \
197 "\t-l\tWrite the byte numbers (decimal) and values (octal)\n" \
198 "\t\t for all differing bytes.\n" \
199 "\t-s\tquiet mode - do not print"
200
201#define cp_trivial_usage \
202 "[OPTION]... SOURCE DEST"
203#define cp_full_usage \
204 "Copies SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n" \
205 "\n" \
206 "\t-a\tSame as -dpR\n" \
207 "\t-d\tPreserves links\n" \
208 "\t-p\tPreserves file attributes if possible\n" \
209 "\t-f\tforce (implied; ignored) - always set\n" \
210 "\t-i\tinteractive, prompt before overwrite\n" \
211 "\t-R,-r\tCopies directories recursively"
212
213#define cpio_trivial_usage \
214 "-[dimtuv][F cpiofile]"
215#define cpio_full_usage \
216 "Extract or list files from a cpio archive\n" \
217 "Main operation mode:\n" \
218 "\td\t\tmake leading directories\n" \
219 "\ti\t\textract\n" \
220 "\tm\t\tpreserve mtime\n" \
221 "\tt\t\tlist\n" \
222 "\tv\t\tverbose\n" \
223 "\tu\t\tunconditional overwrite\n" \
224 "\tF\t\tinput from file"
225
226#define crond_trivial_usage \
227 "-d[#] -c <crondir> -f -b"
228#define crond_full_usage \
229 "\t-d [#] -l [#] -S -L logfile -f -b -c dir\n" \
230 "\t-d num\tdebug level\n" \
231 "\t-l num\tlog level (8 - default)\n" \
232 "\t-S\tlog to syslogd (default)\n" \
233 "\t-L file\tlog to file\n" \
234 "\t-f\trun in fordeground\n" \
235 "\t-b\trun in background (default)\n" \
236 "\t-c dir\tworking dir"
237
238#define crontab_trivial_usage \
239 "[-c dir] {file|-}|[-u|-l|-e|-d user]"
240#define crontab_full_usage \
241 "\tfile <opts> replace crontab from file\n" \
242 "\t- <opts> replace crontab from stdin\n" \
243 "\t-u user specify user\n" \
244 "\t-l [user] list crontab for user\n" \
245 "\t-e [user] edit crontab for user\n" \
246 "\t-d [user] delete crontab for user\n" \
247 "\t-c dir specify crontab directory"
248
249
250#define cut_trivial_usage \
251 "[OPTION]... [FILE]..."
252#define cut_full_usage \
253 "Prints selected fields from each input FILE to standard output.\n\n" \
254 "Options:\n" \
255 "\t-b LIST\t\tOutput only bytes from LIST\n" \
256 "\t-c LIST\t\tOutput only characters from LIST\n" \
257 "\t-d CHAR\t\tUse CHAR instead of tab as the field delimiter\n" \
258 "\t-s\t\tOutput only the lines containing delimiter\n" \
259 "\t-f N\t\tPrint only these fields\n" \
260 "\t-n\t\tIgnored"
261#define cut_example_usage \
262 "$ echo "Hello world" | cut -f 1 -d ' '\n" \
263 "Hello\n" \
264 "$ echo "Hello world" | cut -f 2 -d ' '\n" \
265 "world\n"
266
267#ifdef CONFIG_FEATURE_DATE_ISOFMT
268#define USAGE_DATE_ISOFMT(a) a
269#else
270#define USAGE_DATE_ISOFMT(a)
271#endif
272
273#define date_trivial_usage \
274 "[OPTION]... [MMDDhhmm[[CC]YY][.ss]] [+FORMAT]"
275#define date_full_usage \
276 "Displays the current time in the given FORMAT, or sets the system date.\n" \
277 "\nOptions:\n" \
278 "\t-R\t\tOutputs RFC-822 compliant date string\n" \
279 "\t-d STRING\tDisplays time described by STRING, not `now'\n" \
280 USAGE_DATE_ISOFMT("\t-I[TIMESPEC]\tOutputs an ISO-8601 compliant date/time string.\n" \
281 "\t\t\tTIMESPEC=`date' (or missing) for date only,\n" \
282 "\t\t\t`hours', `minutes', or `seconds' for date and,\n" \
283 "\t\t\ttime to the indicated precision.\n") \
284 "\t-s\t\tSets time described by STRING\n" \
285 "\t-r FILE\t\tDisplays the last modification time of FILE\n" \
286 "\t-u\t\tPrints or sets Coordinated Universal Time"
287#define date_example_usage \
288 "$ date\n" \
289 "Wed Apr 12 18:52:41 MDT 2000\n"
290
291#define dc_trivial_usage \
292 "expression ..."
293#define dc_full_usage \
294 "This is a Tiny RPN calculator that understands the\n" \
295 "following operations: +, add, -, sub, *, mul, /, div, %, mod, "\
296 "**, exp, and, or, not, eor.\n" \
297 "For example: 'dc 2 2 add' -> 4, and 'dc 8 8 \\* 2 2 + /' -> 16.\n" \
298 "\nOptions:\n" \
299 "p - Prints the value on the top of the stack, without altering the stack.\n" \
300 "f - Prints the entire contents of the stack without altering anything.\n" \
301 "o - Pops the value off the top of the stack and uses it to set the output radix.\n" \
302 " Only 10 and 16 are supported."
303#define dc_example_usage \
304 "$ dc 2 2 + p\n" \
305 "4\n" \
306 "$ dc 8 8 \\* 2 2 + / p\n" \
307 "16\n" \
308 "$ dc 0 1 and p\n" \
309 "0\n" \
310 "$ dc 0 1 or p\n" \
311 "1\n" \
312 "$ echo 72 9 div 8 mul p | dc\n" \
313 "64\n"
314
315#define dd_trivial_usage \
316 "[if=FILE] [of=FILE] [bs=N] [count=N] [skip=N]\n" \
317 "\t [seek=N] [conv=notrunc|noerror|sync]"
318#define dd_full_usage \
319 "Copy a file, converting and formatting according to options\n\n" \
320 "\tif=FILE\t\tread from FILE instead of stdin\n" \
321 "\tof=FILE\t\twrite to FILE instead of stdout\n" \
322 "\tbs=N\t\tread and write N bytes at a time\n" \
323 "\tcount=N\t\tcopy only N input blocks\n" \
324 "\tskip=N\t\tskip N input blocks\n" \
325 "\tseek=N\t\tskip N output blocks\n" \
326 "\tconv=notrunc\tdon't truncate output file\n" \
327 "\tconv=noerror\tcontinue after read errors\n" \
328 "\tconv=sync\tpad blocks with zeros\n" \
329 "\n" \
330 "Numbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024),\n" \
331 "MD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)."
332#define dd_example_usage \
333 "$ dd if=/dev/zero of=/dev/ram1 bs=1M count=4\n" \
334 "4+0 records in\n" \
335 "4+0 records out\n"
336
337#define deallocvt_trivial_usage \
338 "[N]"
339#define deallocvt_full_usage \
340 "Deallocate unused virtual terminal /dev/ttyN"
341
342#define delgroup_trivial_usage \
343 "GROUP"
344#define delgroup_full_usage \
345 "Deletes group GROUP from the system"
346
347#define deluser_trivial_usage \
348 "USER"
349#define deluser_full_usage \
350 "Deletes user USER from the system"
351
352#ifdef CONFIG_DEVFSD_FG_NP
353 #define USAGE_DEVFSD_FG_NP(a) a
354#else
355 #define USAGE_DEVFSD_FG_NP(a)
356#endif
357
358#define devfsd_trivial_usage \
359 "mntpnt [-v]"\
360 USAGE_DEVFSD_FG_NP("[-fg][-np]" )
361#define devfsd_full_usage \
362 "Optional daemon for managing devfs permissions and old device name symlinks.\n" \
363 "\nOptions:\n" \
364 "\tmntpnt\tThe mount point where devfs is mounted.\n\n" \
365 "\t-v\tPrint the protocol version numbers for devfsd\n" \
366 "\t\tand the kernel-side protocol version and exits." \
367 USAGE_DEVFSD_FG_NP( "\n\n\t-fg\tRun the daemon in the foreground.\n\n" \
368 "\t-np\tExit after parsing the configuration file\n" \
369 "\t\tand processing synthetic REGISTER events.\n" \
370 "\t\tDo not poll for events.")
371
372#ifdef CONFIG_FEATURE_HUMAN_READABLE
373 #define USAGE_HUMAN_READABLE(a) a
374 #define USAGE_NOT_HUMAN_READABLE(a)
375#else
376 #define USAGE_HUMAN_READABLE(a)
377 #define USAGE_NOT_HUMAN_READABLE(a) a
378#endif
379#define df_trivial_usage \
380 "[-" USAGE_HUMAN_READABLE("hm") USAGE_NOT_HUMAN_READABLE("") "k] [FILESYSTEM ...]"
381#define df_full_usage \
382 "Print the filesystem space used and space available.\n\n" \
383 "Options:\n" \
384 USAGE_HUMAN_READABLE( \
385 "\n\t-h\tprint sizes in human readable format (e.g., 1K 243M 2G )\n" \
386 "\t-m\tprint sizes in megabytes\n" \
387 "\t-k\tprint sizes in kilobytes(default)") USAGE_NOT_HUMAN_READABLE( \
388 "\n\t-k\tprint sizes in kilobytes(compatibility)")
389#define df_example_usage \
390 "$ df\n" \
391 "Filesystem 1k-blocks Used Available Use% Mounted on\n" \
392 "/dev/sda3 8690864 8553540 137324 98% /\n" \
393 "/dev/sda1 64216 36364 27852 57% /boot\n" \
394 "$ df /dev/sda3\n" \
395 "Filesystem 1k-blocks Used Available Use% Mounted on\n" \
396 "/dev/sda3 8690864 8553540 137324 98% /\n"
397
398#define dirname_trivial_usage \
399 "FILENAME"
400#define dirname_full_usage \
401 "Strips non-directory suffix from FILENAME"
402#define dirname_example_usage \
403 "$ dirname /tmp/foo\n" \
404 "/tmp\n" \
405 "$ dirname /tmp/foo/\n" \
406 "/tmp\n"
407
408#define dmesg_trivial_usage \
409 "[-c] [-n LEVEL] [-s SIZE]"
410#define dmesg_full_usage \
411 "Prints or controls the kernel ring buffer\n\n" \
412 "Options:\n" \
413 "\t-c\t\tClears the ring buffer's contents after printing\n" \
414 "\t-n LEVEL\tSets console logging level\n" \
415 "\t-s SIZE\t\tUse a buffer of size SIZE"
416
417#define dos2unix_trivial_usage \
418 "[option] [FILE]"
419#define dos2unix_full_usage \
420 "Converts FILE from dos format to unix format. When no option\n" \
421 "is given, the input is converted to the opposite output format.\n" \
422 "When no file is given, uses stdin for input and stdout for output.\n\n" \
423 "Options:\n" \
424 "\t-u\toutput will be in UNIX format\n" \
425 "\t-d\toutput will be in DOS format"
426
427#define dpkg_trivial_usage \
428 "[-ilCPru] [-F option] package_name"
429#define dpkg_full_usage \
430 "dpkg is a utility to install, remove and manage Debian packages.\n\n" \
431 "Options:\n" \
432 "\t-i\t\tInstall the package\n" \
433 "\t-l\t\tList of installed packages\n" \
434 "\t-C\t\tConfigure an unpackaged package\n" \
435 "\t-F depends\tIgnore depency problems\n" \
436 "\t-P\t\tPurge all files of a package\n" \
437 "\t-r\t\tRemove all but the configuration files for a package\n" \
438 "\t-u\t\tUnpack a package, but don't configure it"
439
440#define dpkg_deb_trivial_usage \
441 "[-cefxX] FILE [argument]"
442#define dpkg_deb_full_usage \
443 "Perform actions on Debian packages (.debs)\n\n" \
444 "Options:\n" \
445 "\t-c\tList contents of filesystem tree\n" \
446 "\t-e\tExtract control files to [argument] directory\n" \
447 "\t-f\tDisplay control field name starting with [argument]\n" \
448 "\t-x\tExtract packages filesystem tree to directory\n" \
449 "\t-X\tVerbose extract"
450#define dpkg_deb_example_usage \
451 "$ dpkg-deb -X ./busybox_0.48-1_i386.deb /tmp\n"
452
453#ifdef CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K
454#define USAGE_DU_DEFALT_BLOCKSIZE_1k(a) a
455#define USAGE_NOT_DU_DEFALT_BLOCKSIZE_1k(a)
456#else
457#define USAGE_DU_DEFALT_BLOCKSIZE_1k(a)
458#define USAGE_NOT_DU_DEFALT_BLOCKSIZE_1k(a) a
459#endif
460
461#define du_trivial_usage \
462 "[-aHLdclsx" USAGE_HUMAN_READABLE("hm") "k] [FILE]..."
463#define du_full_usage \
464 "Summarizes disk space used for each FILE and/or directory.\n" \
465 "Disk space is printed in units of " \
466 USAGE_DU_DEFALT_BLOCKSIZE_1k("1024") USAGE_NOT_DU_DEFALT_BLOCKSIZE_1k("512") \
467 " bytes.\n\n" \
468 "Options:\n" \
469 "\t-a\tshow sizes of files in addition to directories\n" \
470 "\t-H\tfollow symbolic links that are FILE command line args\n" \
471 "\t-L\tfollow all symbolic links encountered\n" \
472 "\t-d N\tlimit output to directories (and files with -a) of depth < N\n" \
473 "\t-c\toutput a grand total\n" \
474 "\t-l\tcount sizes many times if hard linked\n" \
475 "\t-s\tdisplay only a total for each argument\n" \
476 "\t-x\tskip directories on different filesystems\n" \
477 USAGE_HUMAN_READABLE( \
478 "\t-h\tprint sizes in human readable format (e.g., 1K 243M 2G )\n" \
479 "\t-m\tprint sizes in megabytes\n" ) \
480 "\t-k\tprint sizes in kilobytes" USAGE_DU_DEFALT_BLOCKSIZE_1k("(default)")
481#define du_example_usage \
482 "$ du\n" \
483 "16 ./CVS\n" \
484 "12 ./kernel-patches/CVS\n" \
485 "80 ./kernel-patches\n" \
486 "12 ./tests/CVS\n" \
487 "36 ./tests\n" \
488 "12 ./scripts/CVS\n" \
489 "16 ./scripts\n" \
490 "12 ./docs/CVS\n" \
491 "104 ./docs\n" \
492 "2417 .\n"
493
494#define dumpkmap_trivial_usage \
495 "> keymap"
496#define dumpkmap_full_usage \
497 "Prints out a binary keyboard translation table to standard output."
498#define dumpkmap_example_usage \
499 "$ dumpkmap > keymap\n"
500
501#define dumpleases_trivial_usage \
502 "[-r|-a] [-f LEASEFILE]"
503#define dumpleases_full_usage \
504 "Displays the DHCP leases granted by udhcpd.\n\n" \
505 "Options:\n" \
506 "\t-f,\t--file=FILENAME\tLeases file to load\n" \
507 "\t-r,\t--remaining\tInterpret lease times as time remaing\n" \
508 "\t-a,\t--absolute\tInterpret lease times as expire time"
509
510#ifdef CONFIG_FEATURE_FANCY_ECHO
511 #define USAGE_FANCY_ECHO(a) a
512#else
513 #define USAGE_FANCY_ECHO(a)
514#endif
515
516#define echo_trivial_usage \
517 USAGE_FANCY_ECHO("[-neE] ") "[ARG ...]"
518#define echo_full_usage \
519 "Prints the specified ARGs to stdout\n\n" \
520 USAGE_FANCY_ECHO("Options:\n" \
521 "\t-n\tsuppress trailing newline\n" \
522 "\t-e\tinterpret backslash-escaped characters (i.e., \\t=tab)\n" \
523 "\t-E\tdisable interpretation of backslash-escaped characters")
524#define echo_example_usage \
525 "$ echo "Erik is cool"\n" \
526 "Erik is cool\n" \
527 USAGE_FANCY_ECHO("$ echo -e "Erik\\nis\\ncool"\n" \
528 "Erik\n" \
529 "is\n" \
530 "cool\n" \
531 "$ echo "Erik\\nis\\ncool"\n" \
532 "Erik\\nis\\ncool\n")
533
534#define env_trivial_usage \
535 "[-iu] [-] [name=value]... [command]"
536#define env_full_usage \
537 "Prints the current environment or runs a program after setting\n" \
538 "up the specified environment.\n\n" \
539 "Options:\n" \
540 "\t-, -i\tstart with an empty environment\n" \
541 "\t-u\tremove variable from the environment"
542
543#define expr_trivial_usage \
544 "EXPRESSION"
545#define expr_full_usage \
546 "Prints the value of EXPRESSION to standard output.\n\n" \
547 "EXPRESSION may be:\n" \
548 "\tARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n" \
549 "\tARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n" \
550 "\tARG1 < ARG2 ARG1 is less than ARG2\n" \
551 "\tARG1 <= ARG2 ARG1 is less than or equal to ARG2\n" \
552 "\tARG1 = ARG2 ARG1 is equal to ARG2\n" \
553 "\tARG1 != ARG2 ARG1 is unequal to ARG2\n" \
554 "\tARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n" \
555 "\tARG1 > ARG2 ARG1 is greater than ARG2\n" \
556 "\tARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n" \
557 "\tARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n" \
558 "\tARG1 * ARG2 arithmetic product of ARG1 and ARG2\n" \
559 "\tARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n" \
560 "\tARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2\n" \
561 "\tSTRING : REGEXP anchored pattern match of REGEXP in STRING\n" \
562 "\tmatch STRING REGEXP same as STRING : REGEXP\n" \
563 "\tsubstr STRING POS LENGTH substring of STRING, POS counted from 1\n" \
564 "\tindex STRING CHARS index in STRING where any CHARS is found,\n" \
565 "\t or 0\n" \
566 "\tlength STRING length of STRING\n" \
567 "\tquote TOKEN interpret TOKEN as a string, even if\n" \
568 "\t it is a keyword like `match' or an\n" \
569 "\t operator like `/'\n" \
570 "\t( EXPRESSION ) value of EXPRESSION\n\n" \
571 "Beware that many operators need to be escaped or quoted for shells.\n" \
572 "Comparisons are arithmetic if both ARGs are numbers, else\n" \
573 "lexicographical. Pattern matches return the string matched between \n" \
574 "\\( and \\) or null; if \\( and \\) are not used, they return the number \n" \
575 "of characters matched or 0."
576
577#define false_trivial_usage \
578 ""
579#define false_full_usage \
580 "Return an exit code of FALSE (1)."
581#define false_example_usage \
582 "$ false\n" \
583 "$ echo $?\n" \
584 "1\n"
585
586#define fbset_trivial_usage \
587 "[options] [mode]"
588#define fbset_full_usage \
589 "Show and modify frame buffer settings"
590#define fbset_example_usage \
591 "$ fbset\n" \
592 "mode "1024x768-76"\n" \
593 "\t# D: 78.653 MHz, H: 59.949 kHz, V: 75.694 Hz\n" \
594 "\tgeometry 1024 768 1024 768 16\n" \
595 "\ttimings 12714 128 32 16 4 128 4\n" \
596 "\taccel false\n" \
597 "\trgba 5/11,6/5,5/0,0/0\n" \
598 "endmode\n"
599
600#define fdflush_trivial_usage \
601 "DEVICE"
602#define fdflush_full_usage \
603 "Forces floppy disk drive to detect disk change"
604
605#define fdformat_trivial_usage \
606 "[-n] DEVICE"
607#define fdformat_full_usage \
608 "Low-level formats a floppy disk\n\n" \
609 "Options:\n" \
610 "\t-n\tDon't verify after format"
611
612#define fdisk_trivial_usage \
613 "[-luv] [-C CYLINDERS] [-H HEADS] [-S SECTORS] [-b SSZ] DISK"
614#define fdisk_full_usage \
615 "Change partition table\n" \
616 "Options:\n" \
617 "\t-l List partition table(s)\n" \
618 "\t-u Give Start and End in sector (instead of cylinder) units\n" \
619 "\t-s PARTITION Give partition size(s) in blocks\n" \
620 "\t-b 2048: (for certain MO disks) use 2048-byte sectors\n" \
621 "\t-C CYLINDERS Set the number of cylinders\n" \
622 "\t-H HEADS Set the number of heads\n" \
623 "\t-S SECTORS Set the number of sectors\n" \
624 "\t-v Give fdisk version"
625
626#ifdef CONFIG_FEATURE_FIND_TYPE
627 #define USAGE_FIND_TYPE(a) a
628#else
629 #define USAGE_FIND_TYPE(a)
630#endif
631#ifdef CONFIG_FEATURE_FIND_PERM
632 #define USAGE_FIND_PERM(a) a
633#else
634 #define USAGE_FIND_PERM(a)
635#endif
636#ifdef CONFIG_FEATURE_FIND_MTIME
637 #define USAGE_FIND_MTIME(a) a
638#else
639 #define USAGE_FIND_MTIME(a)
640#endif
641#ifdef CONFIG_FEATURE_FIND_NEWER
642 #define USAGE_FIND_NEWER(a) a
643#else
644 #define USAGE_FIND_NEWER(a)
645#endif
646#ifdef CONFIG_FEATURE_FIND_INUM
647 #define USAGE_FIND_INUM(a) a
648#else
649 #define USAGE_FIND_INUM(a)
650#endif
651
652#define find_trivial_usage \
653 "[PATH...] [EXPRESSION]"
654#define find_full_usage \
655 "Search for files in a directory hierarchy. The default PATH is\n" \
656 "the current directory; default EXPRESSION is '-print'\n" \
657 "\nEXPRESSION may consist of:\n" \
658 "\t-follow\t\tDereference symbolic links.\n" \
659 "\t-name PATTERN\tFile name (leading directories removed) matches PATTERN.\n" \
660 "\t-print\t\tPrint (default and assumed).\n" \
661 USAGE_FIND_TYPE( \
662 "\n\t-type X\t\tFiletype matches X (where X is one of: f,d,l,b,c,...)" \
663) USAGE_FIND_PERM( \
664 "\n\t-perm PERMS\tPermissions match any of (+NNN); all of (-NNN);\n\t\t\tor exactly (NNN)" \
665) USAGE_FIND_MTIME( \
666 "\n\t-mtime TIME\tModified time is greater than (+N); less than (-N);\n\t\t\tor exactly (N) days" \
667) USAGE_FIND_NEWER( \
668 "\n\t-newer FILE\tModified time is more recent than FILE's" \
669) USAGE_FIND_INUM( \
670 "\n\t-inum N\t\tFile has inode number N")
671#define find_example_usage \
672 "$ find / -name passwd\n" \
673 "/etc/passwd\n"
674
675#define fold_trivial_usage \
676 "[-bsw] [FILE]"
677#define fold_full_usage \
678 "Wrap input lines in each FILE (standard input by default), writing to\n" \
679 "standard output.\n\n" \
680 "Options:\n" \
681 "\t-b\tcount bytes rather than columns\n" \
682 "\t-s\tbreak at spaces\n" \
683 "\t-w\tuse WIDTH columns instead of 80"
684
685#define free_trivial_usage \
686 ""
687#define free_full_usage \
688 "Displays the amount of free and used system memory"
689#define free_example_usage \
690 "$ free\n" \
691 " total used free shared buffers\n" \
692 " Mem: 257628 248724 8904 59644 93124\n" \
693 " Swap: 128516 8404 120112\n" \
694 "Total: 386144 257128 129016\n" \
695
696#define freeramdisk_trivial_usage \
697 "DEVICE"
698#define freeramdisk_full_usage \
699 "Frees all memory used by the specified ramdisk."
700#define freeramdisk_example_usage \
701 "$ freeramdisk /dev/ram2\n"
702
703#define fsck_minix_trivial_usage \
704 "[-larvsmf] /dev/name"
705#define fsck_minix_full_usage \
706 "Performs a consistency check for MINIX filesystems.\n\n" \
707 "Options:\n" \
708 "\t-l\tLists all filenames\n" \
709 "\t-r\tPerform interactive repairs\n" \
710 "\t-a\tPerform automatic repairs\n" \
711 "\t-v\tverbose\n" \
712 "\t-s\tOutputs super-block information\n" \
713 "\t-m\tActivates MINIX-like \"mode not cleared\" warnings\n" \
714 "\t-f\tForce file system check."
715
716#define ftpget_trivial_usage \
717 "[options] remote-host local-file remote-file"
718#define ftpget_full_usage \
719 "Retrieve a remote file via FTP.\n\n" \
720 "Options:\n" \
721 "\t-c, --continue Continue a previous transfer\n" \
722 "\t-v, --verbose Verbose\n" \
723 "\t-u, --username Username to be used\n" \
724 "\t-p, --password Password to be used\n" \
725 "\t-P, --port Port number to be used"
726
727#define ftpput_trivial_usage \
728 "[options] remote-host remote-file local-file"
729#define ftpput_full_usage \
730 "Store a local file on a remote machine via FTP.\n\n" \
731 "Options:\n" \
732 "\t-v, --verbose Verbose\n" \
733 "\t-u, --username Username to be used\n" \
734 "\t-p, --password Password to be used\n" \
735 "\t-P, --port Port number to be used"
736
737#define getopt_trivial_usage \
738 "[OPTIONS]..."
739#define getopt_full_usage \
740 "Parse command options\n" \
741 "\t-a, --alternative Allow long options starting with single -\n" \
742 "\t-l, --longoptions=longopts Long options to be recognized\n" \
743 "\t-n, --name=progname The name under which errors are reported\n" \
744 "\t-o, --options=optstring Short options to be recognized\n" \
745 "\t-q, --quiet Disable error reporting by getopt(3)\n" \
746 "\t-Q, --quiet-output No normal output\n" \
747 "\t-s, --shell=shell Set shell quoting conventions\n" \
748 "\t-T, --test Test for getopt(1) version\n" \
749 "\t-u, --unquoted Do not quote the output"
750#define getopt_example_usage \
751 "$ cat getopt.test\n" \
752 "#!/bin/sh\n" \
753 "GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \\\n" \
754 " -n 'example.busybox' -- "$@"`\n" \
755 "if [ $? != 0 ] ; then exit 1 ; fi\n" \
756 "eval set -- "$GETOPT"\n" \
757 "while true ; do\n" \
758 " case $1 in\n" \
759 " -a|--a-long) echo \"Option a\" ; shift ;;\n" \
760 " -b|--b-long) echo \"Option b, argument `$2'\" ; shift 2 ;;\n" \
761 " -c|--c-long)\n" \
762 " case "$2" in\n" \
763 " \"\") echo \"Option c, no argument\"; shift 2 ;;\n" \
764 " *) echo \"Option c, argument `$2'\" ; shift 2 ;;\n" \
765 " esac ;;\n" \
766 " --) shift ; break ;;\n" \
767 " *) echo \"Internal error!\" ; exit 1 ;;\n" \
768 " esac\n" \
769 "done\n"
770
771#define getty_trivial_usage \
772 "[OPTIONS]... baud_rate,... line [termtype]"
773#define getty_full_usage \
774 "Opens a tty, prompts for a login name, then invokes /bin/login\n\n" \
775 "Options:\n" \
776 "\t-h\t\tEnable hardware (RTS/CTS) flow control.\n" \
777 "\t-i\t\tDo not display /etc/issue before running login.\n" \
778 "\t-L\t\tLocal line, so do not do carrier detect.\n" \
779 "\t-m\t\tGet baud rate from modem's CONNECT status message.\n" \
780 "\t-w\t\tWait for a CR or LF before sending /etc/issue.\n" \
781 "\t-n\t\tDo not prompt the user for a login name.\n" \
782 "\t-f issue_file\tDisplay issue_file instead of /etc/issue.\n" \
783 "\t-l login_app\tInvoke login_app instead of /bin/login.\n" \
784 "\t-t timeout\tTerminate after timeout if no username is read.\n" \
785 "\t-I initstring\tSets the init string to send before anything else.\n" \
786 "\t-H login_host\tLog login_host into the utmp file as the hostname."
787
788
789#define grep_trivial_usage \
790 "[-ihHnqvs] PATTERN [FILEs...]"
791#define grep_full_usage \
792 "Search for PATTERN in each FILE or standard input.\n\n" \
793 "Options:\n" \
794 "\t-H\tprefix output lines with filename where match was found\n" \
795 "\t-h\tsuppress the prefixing filename on output\n" \
796 "\t-i\tignore case distinctions\n" \
797 "\t-l\tlist names of files that match\n" \
798 "\t-n\tprint line number with output lines\n" \
799 "\t-q\tbe quiet. Returns 0 if result was found, 1 otherwise\n" \
800 "\t-v\tselect non-matching lines\n" \
801 "\t-s\tsuppress file open/read error messages"
802#define grep_example_usage \
803 "$ grep root /etc/passwd\n" \
804 "root:x:0:0:root:/root:/bin/bash\n" \
805 "$ grep ^[rR]oo. /etc/passwd\n" \
806 "root:x:0:0:root:/root:/bin/bash\n"
807
808#define gunzip_trivial_usage \
809 "[OPTION]... FILE"
810#define gunzip_full_usage \
811 "Uncompress FILE (or standard input if FILE is '-').\n\n" \
812 "Options:\n" \
813 "\t-c\tWrite output to standard output\n" \
814 "\t-f\tForce read when source is a terminal\n" \
815 "\t-t\tTest compressed file integrity"
816#define gunzip_example_usage \
817 "$ ls -la /tmp/BusyBox*\n" \
818 "-rw-rw-r-- 1 andersen andersen 557009 Apr 11 10:55 /tmp/BusyBox-0.43.tar.gz\n" \
819 "$ gunzip /tmp/BusyBox-0.43.tar.gz\n" \
820 "$ ls -la /tmp/BusyBox*\n" \
821 "-rw-rw-r-- 1 andersen andersen 1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar\n"
822
823#define gzip_trivial_usage \
824 "[OPTION]... [FILE]..."
825#define gzip_full_usage \
826 "Compress FILE(s) with maximum compression.\n" \
827 "When FILE is '-' or unspecified, reads standard input. Implies -c.\n\n" \
828 "Options:\n" \
829 "\t-c\tWrite output to standard output instead of FILE.gz\n" \
830 "\t-d\tDecompress\n" \
831 "\t-f\tForce write when destination is a terminal"
832#define gzip_example_usage \
833 "$ ls -la /tmp/busybox*\n" \
834 "-rw-rw-r-- 1 andersen andersen 1761280 Apr 14 17:47 /tmp/busybox.tar\n" \
835 "$ gzip /tmp/busybox.tar\n" \
836 "$ ls -la /tmp/busybox*\n" \
837 "-rw-rw-r-- 1 andersen andersen 554058 Apr 14 17:49 /tmp/busybox.tar.gz\n"
838
839#define halt_trivial_usage \
840 "[-d<delay>]"
841#define halt_full_usage \
842 "Halt the system.\n" \
843 "Options:\n" \
844 "\t-d\t\tdelay interval for halting."
845
846#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
847#define USAGE_HDPARM_IDENT(a) a
848#else
849#define USAGE_HDPARM_IDENT(a)
850#endif
851
852#ifdef CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF
853#define USAGE_SCAN_HWIF(a) a
854#else
855#define USAGE_SCAN_HWIF(a)
856#endif
857
858#ifdef CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
859#define USAGE_UNREGISTER_HWIF(a) a
860#else
861#define USAGE_UNREGISTER_HWIF(a)
862#endif
863
864#ifdef CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET
865#define USAGE_DRIVE_RESET(a) a
866#else
867#define USAGE_DRIVE_RESET(a)
868#endif
869
870#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
871#define USAGE_TRISTATE_HWIF(a) a
872#else
873#define USAGE_TRISTATE_HWIF(a)
874#endif
875
876#ifdef CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA
877#define USAGE_GETSET_DMA(a) a
878#else
879#define USAGE_GETSET_DMA(a)
880#endif
881
882#define hdparm_trivial_usage \
883 "[options] [device] .."
884#define hdparm_full_usage \
885 "Options:" \
886 "\t-a get/set fs readahead\n" \
887 "\t-A set drive read-lookahead flag (0/1)\n" \
888 "\t-b get/set bus state (0 == off, 1 == on, 2 == tristate)\n" \
889 "\t-B set Advanced Power Management setting (1-255)\n" \
890 "\t-c get/set IDE 32-bit IO setting\n" \
891 "\t-C check IDE power mode status\n" \
892 USAGE_GETSET_DMA("\t-d get/set using_dma flag\n") \
893 "\t-D enable/disable drive defect-mgmt\n" \
894 "\t-f flush buffer cache for device on exit\n" \
895 "\t-g display drive geometry\n" \
896 "\t-h display terse usage information\n" \
897 "\t-i display drive identification\n" \
898 USAGE_HDPARM_IDENT("\t-I detailed/current information directly from drive\n") \
899 USAGE_HDPARM_IDENT("\t-Istdin similar to -I, but wants /proc/ide/" "*" "/hd?/identify as input\n") \
900 "\t-k get/set keep_settings_over_reset flag (0/1)\n" \
901 "\t-K set drive keep_features_over_reset flag (0/1)\n" \
902 "\t-L set drive doorlock (0/1) (removable harddisks only)\n" \
903 "\t-m get/set multiple sector count\n" \
904 "\t-n get/set ignore-write-errors flag (0/1)\n" \
905 "\t-p set PIO mode on IDE interface chipset (0,1,2,3,4,...)\n" \
906 "\t-P set drive prefetch count\n" \
907 "\t-q change next setting quietly\n" \
908 "\t-Q get/set DMA tagged-queuing depth (if supported)\n" \
909 "\t-r get/set readonly flag (DANGEROUS to set)\n" \
910 USAGE_SCAN_HWIF("\t-R register an IDE interface (DANGEROUS)\n") \
911 "\t-S set standby (spindown) timeout\n" \
912 "\t-t perform device read timings\n" \
913 "\t-T perform cache read timings\n" \
914 "\t-u get/set unmaskirq flag (0/1)\n" \
915 USAGE_UNREGISTER_HWIF("\t-U un-register an IDE interface (DANGEROUS)\n") \
916 "\t-v defaults; same as -mcudkrag for IDE drives\n" \
917 "\t-V display program version and exit immediately\n" \
918 USAGE_DRIVE_RESET("\t-w perform device reset (DANGEROUS)\n") \
919 "\t-W set drive write-caching flag (0/1) (DANGEROUS)\n" \
920 USAGE_TRISTATE_HWIF("\t-x tristate device for hotswap (0/1) (DANGEROUS)\n") \
921 "\t-X set IDE xfer mode (DANGEROUS)\n" \
922 "\t-y put IDE drive in standby mode\n" \
923 "\t-Y put IDE drive to sleep\n" \
924 "\t-Z disable Seagate auto-powersaving mode\n" \
925 "\t-z re-read partition table"
926
927#ifdef CONFIG_FEATURE_FANCY_HEAD
928#define USAGE_FANCY_HEAD(a) a
929#else
930#define USAGE_FANCY_HEAD(a)
931#endif
932
933#define head_trivial_usage \
934 "[OPTION]... [FILE]..."
935#define head_full_usage \
936 "Print first 10 lines of each FILE to standard output.\n" \
937 "With more than one FILE, precede each with a header giving the\n" \
938 "file name. With no FILE, or when FILE is -, read standard input.\n\n" \
939 "Options:\n" \
940 "\t-n NUM\t\tPrint first NUM lines instead of first 10" \
941 USAGE_FANCY_HEAD( \
942 "\n\t-c NUM\t\toutput the first NUM bytes\n" \
943 "\t-q\t\tnever output headers giving file names\n" \
944 "\t-v\t\talways output headers giving file names" )
945#define head_example_usage \
946 "$ head -n 2 /etc/passwd\n" \
947 "root:x:0:0:root:/root:/bin/bash\n" \
948 "daemon:x:1:1:daemon:/usr/sbin:/bin/sh\n"
949
950#define hexdump_trivial_usage \
951 "[-[bcdefnosvx]] [OPTION] FILE"
952#define hexdump_full_usage \
953 "The hexdump utility is a filter which displays the specified files,\n" \
954 "or the standard input, if no files are specified, in a user specified\n"\
955 "format\n" \
956 "\t-b\t\tOne-byte octal display\n" \
957 "\t-c\t\tOne-byte character display\n" \
958 "\t-d\t\tTwo-byte decimal display\n" \
959 "\t-e FORMAT STRING\n" \
960 "\t-f FORMAT FILE\n" \
961 "\t-n LENGTH\tInterpret only length bytes of input\n" \
962 "\t-o\t\tTwo-byte octal display\n" \
963 "\t-s OFFSET\tSkip offset byte\n" \
964 "\t-v\t\tdisplay all input data\n" \
965 "\t-x\t\tTwo-byte hexadecimal display"
966
967#define hostid_trivial_usage \
968 ""
969#define hostid_full_usage \
970 "Print out a unique 32-bit identifier for the machine."
971
972#define hostname_trivial_usage \
973 "[OPTION] {hostname | -F FILE}"
974#define hostname_full_usage \
975 "Get or set the hostname or DNS domain name. If a hostname is given\n" \
976 "(or FILE with the -F parameter), the host name will be set.\n\n" \
977 "Options:\n" \
978 "\t-s\tShort\n" \
979 "\t-i\tAddresses for the hostname\n" \
980 "\t-d\tDNS domain name\n" \
981 "\t-f\tFully qualified domain name\n" \
982 "\t-F FILE\tUse the contents of FILE to specify the hostname"
983#define hostname_example_usage \
984 "$ hostname\n" \
985 "sage\n"
986
987#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
988 #define USAGE_HTTPD_BASIC_AUTH(a) a
989 #ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
990 #define USAGE_HTTPD_AUTH_MD5(a) a
991 #else
992 #define USAGE_HTTPD_AUTH_MD5(a)
993 #endif
994#else
995 #define USAGE_HTTPD_BASIC_AUTH(a)
996 #define USAGE_HTTPD_AUTH_MD5(a)
997#endif
998#ifdef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
999 #define USAGE_HTTPD_STANDALONE(a)
1000 #define USAGE_HTTPD_SETUID(a)
1001#else
1002 #define USAGE_HTTPD_STANDALONE(a) a
1003 #ifdef CONFIG_FEATURE_HTTPD_SETUID
1004 #define USAGE_HTTPD_SETUID(a) a
1005 #else
1006 #define USAGE_HTTPD_SETUID(a)
1007 #endif
1008#endif
1009#define httpd_trivial_usage \
1010 "[-c <conf file>]" \
1011 USAGE_HTTPD_STANDALONE(" [-p <port>]") \
1012 USAGE_HTTPD_SETUID(" [-u user]") \
1013 USAGE_HTTPD_BASIC_AUTH(" [-r <realm>]") \
1014 USAGE_HTTPD_AUTH_MD5(" [-m pass]") \
1015 " [-h home]" \
1016 " [-d/-e <string>]"
1017#define httpd_full_usage \
1018 "Listens for incoming http server requests.\n\n"\
1019 "Options:\n" \
1020 "\t-c FILE\t\tSpecifies configuration file. (default httpd.conf)\n" \
1021 USAGE_HTTPD_STANDALONE("\t-p PORT\tServer port (default 80)\n") \
1022 USAGE_HTTPD_SETUID("\t-u USER\tSet uid to USER after listening privileges port\n") \
1023 USAGE_HTTPD_BASIC_AUTH("\t-r REALM\tAuthentication Realm for Basic Authentication\n") \
1024 USAGE_HTTPD_AUTH_MD5("\t-m PASS\t\tCrypt PASS with md5 algorithm\n") \
1025 "\t-h HOME \tSpecifies http HOME directory (default ./)\n" \
1026 "\t-e STRING\tHtml encode STRING\n" \
1027 "\t-d STRING\tURL decode STRING"
1028
1029#define hwclock_trivial_usage \
1030 "[-r|--show] [-s|--hctosys] [-w|--systohc] [-l|--localtime] [-u|--utc]"
1031#define hwclock_full_usage \
1032 "Query and set the hardware clock (RTC)\n\n" \
1033 "Options:\n" \
1034 "\t-r\tread hardware clock and print result\n" \
1035 "\t-s\tset the system time from the hardware clock\n" \
1036 "\t-w\tset the hardware clock to the current system time\n" \
1037 "\t-u\tthe hardware clock is kept in coordinated universal time\n" \
1038 "\t-l\tthe hardware clock is kept in local time"
1039
1040#ifdef CONFIG_SELINUX
1041 #define USAGE_SELINUX(a) a
1042#else
1043 #define USAGE_SELINUX(a)
1044#endif
1045
1046#define id_trivial_usage \
1047 "[OPTIONS]... [USERNAME]"
1048#define id_full_usage \
1049 "Print information for USERNAME or the current user\n\n" \
1050 "Options:\n" \
1051 USAGE_SELINUX("\t-c\tprints only the security context\n") \
1052 "\t-g\tprints only the group ID\n" \
1053 "\t-u\tprints only the user ID\n" \
1054 "\t-n\tprint a name instead of a number\n" \
1055 "\t-r\tprints the real user ID instead of the effective ID"
1056#define id_example_usage \
1057 "$ id\n" \
1058 "uid=1000(andersen) gid=1000(andersen)\n"
1059
1060#ifdef CONFIG_FEATURE_IFCONFIG_SLIP
1061 #define USAGE_SIOCSKEEPALIVE(a) a
1062#else
1063 #define USAGE_SIOCSKEEPALIVE(a)
1064#endif
1065#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
1066 #define USAGE_IFCONFIG_MII(a) a
1067#else
1068 #define USAGE_IFCONFIG_MII(a)
1069#endif
1070#ifdef CONFIG_FEATURE_IFCONFIG_HW
1071 #define USAGE_IFCONFIG_HW(a) a
1072#else
1073 #define USAGE_IFCONFIG_HW(a)
1074#endif
1075#ifdef CONFIG_FEATURE_IFCONFIG_STATUS
1076 #define USAGE_IFCONFIG_OPT_A(a) a
1077#else
1078 #define USAGE_IFCONFIG_OPT_A(a)
1079#endif
1080#ifdef CONFIG_FEATURE_IPV6
1081 #define USAGE_IPV6(a) a
1082#else
1083 #define USAGE_IPV6(a)
1084#endif
1085
1086#define ifconfig_trivial_usage \
1087 USAGE_IFCONFIG_OPT_A("[-a]") " <interface> [<address>]"
1088#define ifconfig_full_usage \
1089 "configure a network interface\n\n" \
1090 "Options:\n" \
1091 USAGE_IPV6("[add <address>[/<prefixlen>]]\n") \
1092 USAGE_IPV6("[del <address>[/<prefixlen>]]\n") \
1093 "\t[[-]broadcast [<address>]] [[-]pointopoint [<address>]]\n" \
1094 "\t[netmask <address>] [dstaddr <address>]\n" \
1095 USAGE_SIOCSKEEPALIVE("\t[outfill <NN>] [keepalive <NN>]\n") \
1096 "\t" USAGE_IFCONFIG_HW("[hw ether <address>] ") \
1097 "[metric <NN>] [mtu <NN>]\n" \
1098 "\t[[-]trailers] [[-]arp] [[-]allmulti]\n" \
1099 "\t[multicast] [[-]promisc] [txqueuelen <NN>] [[-]dynamic]\n" \
1100 USAGE_IFCONFIG_MII("\t[mem_start <NN>] [io_addr <NN>] [irq <NN>]\n") \
1101 "\t[up|down] ..."
1102
1103#define ifup_trivial_usage \
1104 "<-ahinv> <ifaces...>"
1105#define ifup_full_usage \
1106 "ifup <options> <ifaces...>\n\n" \
1107 "Options:\n" \
1108 "\t-h\tthis help\n" \
1109 "\t-a\tde/configure all interfaces automatically\n" \
1110 "\t-i FILE\tuse FILE for interface definitions\n" \
1111 "\t-n\tprint out what would happen, but don't do it\n" \
1112 "\t\t\t(note that this option doesn't disable mappings)\n" \
1113 "\t-v\tprint out what would happen before doing it\n" \
1114 "\t-m\tdon't run any mappings\n" \
1115 "\t-f\tforce de/configuration"
1116
1117#define ifdown_trivial_usage \
1118 "<-ahinv> <ifaces...>"
1119#define ifdown_full_usage \
1120 "ifdown <options> <ifaces...>\n\n" \
1121 "Options:\n" \
1122 "\t-h\tthis help\n" \
1123 "\t-a\tde/configure all interfaces automatically\n" \
1124 "\t-i FILE\tuse FILE for interface definitions\n" \
1125 "\t-n\tprint out what would happen, but don't do it\n" \
1126 "\t\t(note that this option doesn't disable mappings)\n" \
1127 "\t-v\tprint out what would happen before doing it\n" \
1128 "\t-m\tdon't run any mappings\n" \
1129 "\t-f\tforce de/configuration"
1130
1131#define inetd_trivial_usage \
1132 "[-q len] [conf]"
1133#define inetd_full_usage \
1134 "Listens for network connections and launches programs\n\n" \
1135 "Option:\n" \
1136 "\t-q\tSets the size of the socket listen queue to\n" \
1137 "\t\tthe specified value. Default is 128."
1138
1139#define init_trivial_usage \
1140 ""
1141#define init_full_usage \
1142 "Init is the parent of all processes."
1143#define init_notes_usage \
1144"This version of init is designed to be run only by the kernel.\n" \
1145"\n" \
1146"BusyBox init doesn't support multiple runlevels. The runlevels field of\n" \
1147"the /etc/inittab file is completely ignored by BusyBox init. If you want \n" \
1148"runlevels, use sysvinit.\n" \
1149"\n" \
1150"BusyBox init works just fine without an inittab. If no inittab is found, \n" \
1151"it has the following default behavior:\n" \
1152"\n" \
1153" ::sysinit:/etc/init.d/rcS\n" \
1154" ::askfirst:/bin/sh\n" \
1155" ::ctrlaltdel:/sbin/reboot\n" \
1156" ::shutdown:/sbin/swapoff -a\n" \
1157" ::shutdown:/bin/umount -a -r\n" \
1158" ::restart:/sbin/init\n" \
1159"\n" \
1160"if it detects that /dev/console is _not_ a serial console, it will also run:\n" \
1161"\n" \
1162" tty2::askfirst:/bin/sh\n" \
1163" tty3::askfirst:/bin/sh\n" \
1164" tty4::askfirst:/bin/sh\n" \
1165"\n" \
1166"If you choose to use an /etc/inittab file, the inittab entry format is as follows:\n" \
1167"\n" \
1168" <id>:<runlevels>:<action>:<process>\n" \
1169"\n" \
1170" <id>:\n" \
1171"\n" \
1172" WARNING: This field has a non-traditional meaning for BusyBox init!\n" \
1173" The id field is used by BusyBox init to specify the controlling tty for\n" \
1174" the specified process to run on. The contents of this field are\n" \
1175" appended to "/dev/" and used as-is. There is no need for this field to\n" \
1176" be unique, although if it isn't you may have strange results. If this\n" \
1177" field is left blank, the controlling tty is set to the console. Also\n" \
1178" note that if BusyBox detects that a serial console is in use, then only\n" \
1179" entries whose controlling tty is either the serial console or /dev/null\n" \
1180" will be run. BusyBox init does nothing with utmp. We don't need no\n" \
1181" stinkin' utmp.\n" \
1182"\n" \
1183" <runlevels>:\n" \
1184"\n" \
1185" The runlevels field is completely ignored.\n" \
1186"\n" \
1187" <action>:\n" \
1188"\n" \
1189" Valid actions include: sysinit, respawn, askfirst, wait,\n" \
1190" once, restart, ctrlaltdel, and shutdown.\n" \
1191"\n" \
1192" The available actions can be classified into two groups: actions\n" \
1193" that are run only once, and actions that are re-run when the specified\n" \
1194" process exits.\n" \
1195"\n" \
1196" Run only-once actions:\n" \
1197"\n" \
1198" 'sysinit' is the first item run on boot. init waits until all\n" \
1199" sysinit actions are completed before continuing. Following the\n" \
1200" completion of all sysinit actions, all 'wait' actions are run.\n" \
1201" 'wait' actions, like 'sysinit' actions, cause init to wait until\n" \
1202" the specified task completes. 'once' actions are asynchronous,\n" \
1203" therefore, init does not wait for them to complete. 'restart' is\n" \
1204" the action taken to restart the init process. By default this should\n" \
1205" simply run /sbin/init, but can be a script which runs pivot_root or it\n" \
1206" can do all sorts of other interesting things. The 'ctrlaltdel' init\n" \
1207" actions are run when the system detects that someone on the system\n" \
1208" console has pressed the CTRL-ALT-DEL key combination. Typically one\n" \
1209" wants to run 'reboot' at this point to cause the system to reboot.\n" \
1210" Finally the 'shutdown' action specifies the actions to taken when\n" \
1211" init is told to reboot. Unmounting filesystems and disabling swap\n" \
1212" is a very good here\n" \
1213"\n" \
1214" Run repeatedly actions:\n" \
1215"\n" \
1216" 'respawn' actions are run after the 'once' actions. When a process\n" \
1217" started with a 'respawn' action exits, init automatically restarts\n" \
1218" it. Unlike sysvinit, BusyBox init does not stop processes from\n" \
1219" respawning out of control. The 'askfirst' actions acts just like\n" \
1220" respawn, except that before running the specified process it\n" \
1221" displays the line "Please press Enter to activate this console."\n" \
1222" and then waits for the user to press enter before starting the\n" \
1223" specified process.\n" \
1224"\n" \
1225" Unrecognized actions (like initdefault) will cause init to emit an\n" \
1226" error message, and then go along with its business. All actions are\n" \
1227" run in the order they appear in /etc/inittab.\n" \
1228"\n" \
1229" <process>:\n" \
1230"\n" \
1231" Specifies the process to be executed and its command line.\n" \
1232"\n" \
1233"Example /etc/inittab file:\n" \
1234"\n" \
1235" # This is run first except when booting in single-user mode.\n" \
1236" #\n" \
1237" ::sysinit:/etc/init.d/rcS\n" \
1238" \n" \
1239" # /bin/sh invocations on selected ttys\n" \
1240" #\n" \
1241" # Start an "askfirst" shell on the console (whatever that may be)\n" \
1242" ::askfirst:-/bin/sh\n" \
1243" # Start an "askfirst" shell on /dev/tty2-4\n" \
1244" tty2::askfirst:-/bin/sh\n" \
1245" tty3::askfirst:-/bin/sh\n" \
1246" tty4::askfirst:-/bin/sh\n" \
1247" \n" \
1248" # /sbin/getty invocations for selected ttys\n" \
1249" #\n" \
1250" tty4::respawn:/sbin/getty 38400 tty4\n" \
1251" tty5::respawn:/sbin/getty 38400 tty5\n" \
1252" \n" \
1253" \n" \
1254" # Example of how to put a getty on a serial line (for a terminal)\n" \
1255" #\n" \
1256" #::respawn:/sbin/getty -L ttyS0 9600 vt100\n" \
1257" #::respawn:/sbin/getty -L ttyS1 9600 vt100\n" \
1258" #\n" \
1259" # Example how to put a getty on a modem line.\n" \
1260" #::respawn:/sbin/getty 57600 ttyS2\n" \
1261" \n" \
1262" # Stuff to do when restarting the init process\n" \
1263" ::restart:/sbin/init\n" \
1264" \n" \
1265" # Stuff to do before rebooting\n" \
1266" ::ctrlaltdel:/sbin/reboot\n" \
1267" ::shutdown:/bin/umount -a -r\n" \
1268" ::shutdown:/sbin/swapoff -a\n"
1269
1270#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
1271 #define USAGE_INSMOD_MAP(a) a
1272#else
1273 #define USAGE_INSMOD_MAP(a)
1274#endif
1275#define insmod_trivial_usage \
1276 "[OPTION]... MODULE [symbol=value]..."
1277#define insmod_full_usage \
1278 "Loads the specified kernel modules into the kernel.\n\n" \
1279 "Options:\n" \
1280 "\t-f\tForce module to load into the wrong kernel version.\n" \
1281 "\t-k\tMake module autoclean-able.\n" \
1282 "\t-v\tverbose output\n" \
1283 "\t-q\tquiet output\n" \
1284 "\t-L\tLock to prevent simultaneous loads of a module\n" \
1285 USAGE_INSMOD_MAP("\t-m\tOutput load map to stdout\n") \
1286 "\t-o NAME\tSet internal module name to NAME\n" \
1287 "\t-x\tdo not export externs"
1288
1289#define install_trivial_usage \
1290 "[-cgmops] [sources] <dest|directory>"
1291#define install_full_usage \
1292 "Copies files and set attributes\n\n" \
1293 "Options:\n" \
1294 "\t-c\tcopy the file, default\n" \
1295 "\t-d\tcreate directories\n" \
1296 "\t-g\tset group ownership\n" \
1297 "\t-m\tset permission modes\n" \
1298 "\t-o\tset ownership\n" \
1299 "\t-p\tpreserve date\n" \
1300 "\t-s\tstrip symbol tables"
1301
1302#define ip_trivial_usage \
1303 "[ OPTIONS ] { address | link | route | tunnel } { COMMAND | help }"
1304#define ip_full_usage \
1305 "ip [ OPTIONS ] OBJECT { COMMAND | help }\n" \
1306 "where OBJECT := { link | addr | route | tunnel }\n" \
1307 "OPTIONS := { -f[amily] { inet | inet6 | link } | -o[neline] }"
1308
1309#define ipaddr_trivial_usage \
1310 "{ {add|del} IFADDR dev STRING | {show|flush}\n" \
1311 "\t\t[ dev STRING ] [ to PREFIX ] }"
1312#define ipaddr_full_usage \
1313 "ipaddr {add|del} IFADDR dev STRING\n" \
1314 "ipaddr {show|flush} [ dev STRING ] [ scope SCOPE-ID ]\n" \
1315 "\t\t\t[ to PREFIX ] [ label PATTERN ]\n" \
1316 "\t\t\tIFADDR := PREFIX | ADDR peer PREFIX\n" \
1317 "\t\t\t[ broadcast ADDR ] [ anycast ADDR ]\n" \
1318 "\t\t\t[ label STRING ] [ scope SCOPE-ID ]\n" \
1319 "\t\t\tSCOPE-ID := [ host | link | global | NUMBER ]"
1320
1321#ifdef CONFIG_FEATURE_IPCALC_FANCY
1322 #define XUSAGE_IPCALC_FANCY(a) a
1323#else
1324 #define XUSAGE_IPCALC_FANCY(a)
1325#endif
1326#define ipcalc_trivial_usage \
1327 "[OPTION]... <ADDRESS>[[/]<NETMASK>] [NETMASK]"
1328#define ipcalc_full_usage \
1329 "Calculate IP network settings from a IP address\n\n" \
1330 "Options:\n" \
1331 "\t-b\t--broadcast\tDisplay calculated broadcast address.\n" \
1332 "\t-n\t--network\tDisplay calculated network address.\n" \
1333 "\t-m\t--netmask\tDisplay default netmask for IP." \
1334 XUSAGE_IPCALC_FANCY(\
1335 "\n\t-p\t--prefix\tDisplay the prefix for IP/NETMASK." \
1336 "\t-h\t--hostname\tDisplay first resolved host name.\n" \
1337 "\t-s\t--silent\tDon't ever display error messages.")
1338
1339#define iplink_trivial_usage \
1340 "{ set DEVICE { up | down | arp { on | off } | show [ DEVICE ] }"
1341#define iplink_full_usage \
1342 "iplink set DEVICE { up | down | arp { on | off } |\n" \
1343 "\t\t\tdynamic { on | off } |\n" \
1344 "\t\t\tmtu MTU }\n" \
1345 "\tiplink show [ DEVICE ]"
1346
1347#define iproute_trivial_usage \
1348 "{ list | flush | { add | del | change | append |\n" \
1349 "\t\treplace | monitor } ROUTE }"
1350#define iproute_full_usage \
1351 "iproute { list | flush } SELECTOR\n" \
1352 "iproute get ADDRESS [ from ADDRESS iif STRING ]\n" \
1353 "\t\t\t[ oif STRING ] [ tos TOS ]\n" \
1354 "\tiproute { add | del | change | append | replace | monitor } ROUTE\n" \
1355 "\t\t\tSELECTOR := [ root PREFIX ] [ match PREFIX ] [ proto RTPROTO ]\n" \
1356 "\t\t\tROUTE := [ TYPE ] PREFIX [ tos TOS ] [ proto RTPROTO ]"
1357
1358#define iptunnel_trivial_usage \
1359 "{ add | change | del | show } [ NAME ]\n" \
1360 "\t\t[ mode { ipip | gre | sit } ]\n" \
1361 "\t\t[ remote ADDR ] [ local ADDR ] [ ttl TTL ]"
1362#define iptunnel_full_usage \
1363 "iptunnel { add | change | del | show } [ NAME ]\n" \
1364 "\t\t\t[ mode { ipip | gre | sit } ] [ remote ADDR ] [ local ADDR ]\n" \
1365 "\t\t\t[ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n" \
1366 "\t\t\t[ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]"
1367
1368#define kill_trivial_usage \
1369 "[-signal] process-id [process-id ...]"
1370#define kill_full_usage \
1371 "Send a signal (default is SIGTERM) to the specified process(es).\n\n"\
1372 "Options:\n" \
1373 "\t-l\tList all signal names and numbers."
1374#define kill_example_usage \
1375 "$ ps | grep apache\n" \
1376 "252 root root S [apache]\n" \
1377 "263 www-data www-data S [apache]\n" \
1378 "264 www-data www-data S [apache]\n" \
1379 "265 www-data www-data S [apache]\n" \
1380 "266 www-data www-data S [apache]\n" \
1381 "267 www-data www-data S [apache]\n" \
1382 "$ kill 252\n"
1383
1384#define killall_trivial_usage \
1385 "[-q] [-signal] process-name [process-name ...]"
1386#define killall_full_usage \
1387 "Send a signal (default is SIGTERM) to the specified process(es).\n\n"\
1388 "Options:\n" \
1389 "\t-l\tList all signal names and numbers.\n"\
1390 "\t-q\tDo not complain if no processes were killed."
1391#define killall_example_usage \
1392 "$ killall apache\n"
1393
1394#define klogd_trivial_usage \
1395 "[-c n] [-n]"
1396#define klogd_full_usage \
1397 "Kernel logger.\n"\
1398 "Options:\n"\
1399 "\t-c n\tSets the default log level of console messages to n.\n"\
1400 "\t-n\tRun as a foreground process."
1401
1402#define length_trivial_usage \
1403 "STRING"
1404#define length_full_usage \
1405 "Prints out the length of the specified STRING."
1406#define length_example_usage \
1407 "$ length Hello\n" \
1408 "5\n"
1409
1410#define ln_trivial_usage \
1411 "[OPTION] TARGET... LINK_NAME|DIRECTORY"
1412#define ln_full_usage \
1413 "Create a link named LINK_NAME or DIRECTORY to the specified TARGET\n"\
1414 "\nYou may use '--' to indicate that all following arguments are non-options.\n\n" \
1415 "Options:\n" \
1416 "\t-s\tmake symbolic links instead of hard links\n" \
1417 "\t-f\tremove existing destination files\n" \
1418 "\t-n\tno dereference symlinks - treat like normal file"
1419#define ln_example_usage \
1420 "$ ln -s BusyBox /tmp/ls\n" \
1421 "$ ls -l /tmp/ls\n" \
1422 "lrwxrwxrwx 1 root root 7 Apr 12 18:39 ls -> BusyBox*\n"
1423
1424#define loadfont_trivial_usage \
1425 "< font"
1426#define loadfont_full_usage \
1427 "Loads a console font from standard input."
1428#define loadfont_example_usage \
1429 "$ loadfont < /etc/i18n/fontname\n"
1430
1431#define loadkmap_trivial_usage \
1432 "< keymap"
1433#define loadkmap_full_usage \
1434 "Loads a binary keyboard translation table from standard input."
1435#define loadkmap_example_usage \
1436 "$ loadkmap < /etc/i18n/lang-keymap\n"
1437
1438#define logger_trivial_usage \
1439 "[OPTION]... [MESSAGE]"
1440#define logger_full_usage \
1441 "Write MESSAGE to the system log. If MESSAGE is omitted, log stdin.\n\n" \
1442 "Options:\n" \
1443 "\t-s\tLog to stderr as well as the system log.\n" \
1444 "\t-t TAG\tLog using the specified tag (defaults to user name).\n" \
1445 "\t-p PRIORITY\tEnter the message with the specified priority.\n" \
1446 "\t\tThis may be numerical or a ``facility.level'' pair."
1447#define logger_example_usage \
1448 "$ logger "hello"\n"
1449
1450#define login_trivial_usage \
1451 "[OPTION]... [username] [ENV=VAR ...]"
1452#define login_full_usage \
1453 "Begin a new session on the system\n\n" \
1454 "Options:\n" \
1455 "\t-f\tDo not authenticate (user already authenticated)\n" \
1456 "\t-h\tName of the remote host for this login.\n" \
1457 "\t-p\tPreserve environment."
1458
1459#define logname_trivial_usage \
1460 ""
1461#define logname_full_usage \
1462 "Print the name of the current user."
1463#define logname_example_usage \
1464 "$ logname\n" \
1465 "root\n"
1466
1467#define logread_trivial_usage \
1468 "[OPTION]..."
1469#define logread_full_usage \
1470 "Shows the messages from syslogd (using circular buffer).\n\n" \
1471 "Options:\n" \
1472 "\t-f\t\toutput data as the log grows"
1473
1474#define losetup_trivial_usage \
1475 "[OPTION]... LOOPDEVICE FILE\n" \
1476 "or: losetup [OPTION]... -d LOOPDEVICE"
1477#define losetup_full_usage \
1478 "Associate LOOPDEVICE with FILE.\n\n" \
1479 "Options:\n" \
1480 "\t-d\t\tDisassociate LOOPDEVICE.\n" \
1481 "\t-o OFFSET\tStart OFFSET bytes into FILE."
1482
1483#ifdef CONFIG_FEATURE_LS_TIMESTAMPS
1484 #define USAGE_LS_TIMESTAMPS(a) a
1485#else
1486 #define USAGE_LS_TIMESTAMPS(a)
1487#endif
1488#ifdef CONFIG_FEATURE_LS_FILETYPES
1489 #define USAGE_LS_FILETYPES(a) a
1490#else
1491 #define USAGE_LS_FILETYPES(a)
1492#endif
1493#ifdef CONFIG_FEATURE_LS_FOLLOWLINKS
1494 #define USAGE_LS_FOLLOWLINKS(a) a
1495#else
1496 #define USAGE_LS_FOLLOWLINKS(a)
1497#endif
1498#ifdef CONFIG_FEATURE_LS_RECURSIVE
1499 #define USAGE_LS_RECURSIVE(a) a
1500#else
1501 #define USAGE_LS_RECURSIVE(a)
1502#endif
1503#ifdef CONFIG_FEATURE_LS_SORTFILES
1504 #define USAGE_LS_SORTFILES(a) a
1505#else
1506 #define USAGE_LS_SORTFILES(a)
1507#endif
1508#ifdef CONFIG_FEATURE_AUTOWIDTH
1509 #define USAGE_AUTOWIDTH(a) a
1510#else
1511 #define USAGE_AUTOWIDTH(a)
1512#endif
1513
1514#define ls_trivial_usage \
1515 "[-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...]"
1516#define ls_full_usage \
1517 "List directory contents\n\n" \
1518 "Options:\n" \
1519 "\t-1\tlist files in a single column\n" \
1520 "\t-A\tdo not list implied . and ..\n" \
1521 "\t-a\tdo not hide entries starting with .\n" \
1522 "\t-C\tlist entries by columns\n" \
1523 USAGE_LS_TIMESTAMPS("\t-c\twith -l: show ctime\n") \
1524 "\t-d\tlist directory entries instead of contents\n" \
1525 USAGE_LS_TIMESTAMPS("\t-e\tlist both full date and full time\n") \
1526 USAGE_LS_FILETYPES("\t-F\tappend indicator (one of */=@|) to entries\n") \
1527 "\t-i\tlist the i-node for each file\n" \
1528 "\t-l\tuse a long listing format\n" \
1529 "\t-n\tlist numeric UIDs and GIDs instead of names\n" \
1530 USAGE_LS_FILETYPES("\t-p\tappend indicator (one of /=@|) to entries\n") \
1531 USAGE_LS_FOLLOWLINKS("\t-L\tlist entries pointed to by symbolic links\n") \
1532 USAGE_LS_RECURSIVE("\t-R\tlist subdirectories recursively\n") \
1533 USAGE_LS_SORTFILES("\t-r\tsort the listing in reverse order\n") \
1534 USAGE_LS_SORTFILES("\t-S\tsort the listing by file size\n") \
1535 "\t-s\tlist the size of each file, in blocks\n" \
1536 USAGE_AUTOWIDTH("\t-T NUM\tassume Tabstop every NUM columns\n") \
1537 USAGE_LS_TIMESTAMPS("\t-t\twith -l: show modification time\n") \
1538 USAGE_LS_TIMESTAMPS("\t-u\twith -l: show access time\n") \
1539 USAGE_LS_SORTFILES("\t-v\tsort the listing by version\n") \
1540 USAGE_AUTOWIDTH("\t-w NUM\tassume the terminal is NUM columns wide\n") \
1541 "\t-x\tlist entries by lines instead of by columns\n" \
1542 USAGE_LS_SORTFILES("\t-X\tsort the listing by extension\n") \
1543 USAGE_HUMAN_READABLE( \
1544 "\t-h\tprint sizes in human readable format (e.g., 1K 243M 2G )\n") \
1545 USAGE_SELINUX("\t-k\tprint security context\n") \
1546 USAGE_SELINUX("\t-K\tprint security context in long format\n")
1547
1548#define lsmod_trivial_usage \
1549 ""
1550#define lsmod_full_usage \
1551 "List the currently loaded kernel modules."
1552
1553#define makedevs_trivial_usage \
1554 "NAME TYPE MAJOR MINOR FIRST LAST [s]"
1555#define makedevs_full_usage \
1556 "Creates a range of block or character special files\n\n" \
1557 "TYPEs include:\n" \
1558 "\tb:\tMake a block (buffered) device.\n" \
1559 "\tc or u:\tMake a character (un-buffered) device.\n" \
1560 "\tp:\tMake a named pipe. MAJOR and MINOR are ignored for named pipes.\n\n" \
1561 "FIRST specifies the number appended to NAME to create the first device.\n" \
1562 "LAST specifies the number of the last item that should be created.\n" \
1563 "If 's' is the last argument, the base device is created as well.\n\n" \
1564 "For example:\n" \
1565 "\tmakedevs /dev/ttyS c 4 66 2 63 -> ttyS2-ttyS63\n" \
1566 "\tmakedevs /dev/hda b 3 0 0 8 s -> hda,hda1-hda8"
1567#define makedevs_example_usage \
1568 "# makedevs /dev/ttyS c 4 66 2 63\n" \
1569 "[creates ttyS2-ttyS63]\n" \
1570 "# makedevs /dev/hda b 3 0 0 8 s\n" \
1571 "[creates hda,hda1-hda8]\n"
1572
1573#ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
1574#define USAGE_MD5_SHA1_SUM_CHECK(a) a
1575#else
1576#define USAGE_MD5_SHA1_SUM_CHECK(a)
1577#endif
1578
1579#define md5sum_trivial_usage \
1580 "[OPTION] [FILEs...]" \
1581 USAGE_MD5_SHA1_SUM_CHECK("\n or: md5sum [OPTION] -c [FILE]")
1582#define md5sum_full_usage \
1583 "Print" USAGE_MD5_SHA1_SUM_CHECK(" or check") " MD5 checksums.\n\n" \
1584 "Options:\n" \
1585 "With no FILE, or when FILE is -, read standard input." \
1586 USAGE_MD5_SHA1_SUM_CHECK("\n\n" \
1587 "\t-c\tcheck MD5 sums against given list\n" \
1588 "\nThe following two options are useful only when verifying checksums:\n" \
1589 "\t-s\tdon't output anything, status code shows success\n" \
1590 "\t-w\twarn about improperly formated MD5 checksum lines")
1591#define md5sum_example_usage \
1592 "$ md5sum < busybox\n" \
1593 "6fd11e98b98a58f64ff3398d7b324003\n" \
1594 "$ md5sum busybox\n" \
1595 "6fd11e98b98a58f64ff3398d7b324003 busybox\n" \
1596 "$ md5sum -c -\n" \
1597 "6fd11e98b98a58f64ff3398d7b324003 busybox\n" \
1598 "busybox: OK\n" \
1599 "^D\n"
1600
1601#define mesg_trivial_usage \
1602 "[y|n]"
1603#define mesg_full_usage \
1604 "mesg controls write access to your terminal\n" \
1605 "\ty\tAllow write access to your terminal.\n" \
1606 "\tn\tDisallow write access to your terminal.\n"
1607
1608#define mkdir_trivial_usage \
1609 "[OPTION] DIRECTORY..."
1610#define mkdir_full_usage \
1611 "Create the DIRECTORY(ies) if they do not already exist\n\n" \
1612 "Options:\n" \
1613 "\t-m\tset permission mode (as in chmod), not rwxrwxrwx - umask\n" \
1614 "\t-p\tno error if existing, make parent directories as needed"
1615#define mkdir_example_usage \
1616 "$ mkdir /tmp/foo\n" \
1617 "$ mkdir /tmp/foo\n" \
1618 "/tmp/foo: File exists\n" \
1619 "$ mkdir /tmp/foo/bar/baz\n" \
1620 "/tmp/foo/bar/baz: No such file or directory\n" \
1621 "$ mkdir -p /tmp/foo/bar/baz\n"
1622
1623#define mkfifo_trivial_usage \
1624 "[OPTIONS] name"
1625#define mkfifo_full_usage \
1626 "Creates a named pipe (identical to 'mknod name p')\n\n" \
1627 "Options:\n" \
1628 "\t-m\tcreate the pipe using the specified mode (default a=rw)"
1629
1630#define mkfs_minix_trivial_usage \
1631 "[-c | -l filename] [-nXX] [-iXX] /dev/name [blocks]"
1632#define mkfs_minix_full_usage \
1633 "Make a MINIX filesystem.\n\n" \
1634 "Options:\n" \
1635 "\t-c\t\tCheck the device for bad blocks\n" \
1636 "\t-n [14|30]\tSpecify the maximum length of filenames\n" \
1637 "\t-i INODES\tSpecify the number of inodes for the filesystem\n" \
1638 "\t-l FILENAME\tRead the bad blocks list from FILENAME\n" \
1639 "\t-v\t\tMake a Minix version 2 filesystem"
1640
1641#define mknod_trivial_usage \
1642 "[OPTIONS] NAME TYPE MAJOR MINOR"
1643#define mknod_full_usage \
1644 "Create a special file (block, character, or pipe).\n\n" \
1645 "Options:\n" \
1646 "\t-m\tcreate the special file using the specified mode (default a=rw)\n\n" \
1647 "TYPEs include:\n" \
1648 "\tb:\tMake a block (buffered) device.\n" \
1649 "\tc or u:\tMake a character (un-buffered) device.\n" \
1650 "\tp:\tMake a named pipe. MAJOR and MINOR are ignored for named pipes."
1651#define mknod_example_usage \
1652 "$ mknod /dev/fd0 b 2 0\n" \
1653 "$ mknod -m 644 /tmp/pipe p\n"
1654
1655#define mkswap_trivial_usage \
1656 "[-c] [-v0|-v1] device [block-count]"
1657#define mkswap_full_usage \
1658 "Prepare a disk partition to be used as a swap partition.\n\n" \
1659 "Options:\n" \
1660 "\t-c\t\tCheck for read-ability.\n" \
1661 "\t-v0\t\tMake version 0 swap [max 128 Megs].\n" \
1662 "\t-v1\t\tMake version 1 swap [big!] (default for kernels >\n\t\t\t2.1.117).\n" \
1663 "\tblock-count\tNumber of block to use (default is entire partition)."
1664
1665#define mktemp_trivial_usage \
1666 "[-dq] TEMPLATE"
1667#define mktemp_full_usage \
1668 "Creates a temporary file with its name based on TEMPLATE.\n" \
1669 "TEMPLATE is any name with six `Xs' (i.e., /tmp/temp.XXXXXX).\n\n" \
1670 "Options:\n" \
1671 "\t-d\t\tMake a directory instead of a file\n" \
1672 "\t-q\t\tFail silently if an error occurs"
1673#define mktemp_example_usage \
1674 "$ mktemp /tmp/temp.XXXXXX\n" \
1675 "/tmp/temp.mWiLjM\n" \
1676 "$ ls -la /tmp/temp.mWiLjM\n" \
1677 "-rw------- 1 andersen andersen 0 Apr 25 17:10 /tmp/temp.mWiLjM\n"
1678
1679#define modprobe_trivial_usage \
1680 "[-knqrsv] [MODULE ...]"
1681#define modprobe_full_usage \
1682 "Used for high level module loading and unloading.\n\n" \
1683 "Options:\n" \
1684 "\t-k\tMake module autoclean-able.\n" \
1685 "\t-n\tJust show what would be done.\n" \
1686 "\t-q\tQuiet output.\n" \
1687 "\t-r\tRemove module (stacks) or do autoclean.\n" \
1688 "\t-s\tReport via syslog instead of stderr.\n" \
1689 "\t-v\tVerbose output."
1690#define modprobe_example_usage \
1691 "$ modprobe cdrom\n"
1692
1693#define more_trivial_usage \
1694 "[FILE ...]"
1695#define more_full_usage \
1696 "More is a filter for viewing FILE one screenful at a time."
1697#define more_example_usage \
1698 "$ dmesg | more\n"
1699
1700#ifdef CONFIG_FEATURE_MOUNT_LOOP
1701 #define USAGE_MOUNT_LOOP(a) a
1702#else
1703 #define USAGE_MOUNT_LOOP(a)
1704#endif
1705#ifdef CONFIG_FEATURE_MTAB_SUPPORT
1706 #define USAGE_MTAB(a) a
1707#else
1708 #define USAGE_MTAB(a)
1709#endif
1710#define mount_trivial_usage \
1711 "[flags] DEVICE NODE [-o options,more-options]"
1712#define mount_full_usage \
1713 "Mount a filesystem. Autodetection of filesystem type requires the\n" \
1714 "/proc filesystem be already mounted.\n\n" \
1715 "Flags:\n" \
1716 "\t-a:\t\tMount all filesystems in fstab.\n" \
1717 USAGE_MTAB( \
1718 "\t-f:\t\t\"Fake\" Add entry to mount table but don't mount it.\n" \
1719 "\t-n:\t\tDon't write a mount table entry.\n" \
1720 ) \
1721 "\t-o option:\tOne of many filesystem options, listed below.\n" \
1722 "\t-r:\t\tMount the filesystem read-only.\n" \
1723 "\t-t fs-type:\tSpecify the filesystem type.\n" \
1724 "\t-w:\t\tMount for reading and writing (default).\n" \
1725 "\n" \
1726 "Options for use with the \"-o\" flag:\n" \
1727 "\tasync/sync:\tWrites are asynchronous / synchronous.\n" \
1728 "\tatime/noatime:\tEnable / disable updates to inode access times.\n" \
1729 "\tdev/nodev:\tAllow use of special device files / disallow them.\n" \
1730 "\texec/noexec:\tAllow use of executable files / disallow them.\n" \
1731 USAGE_MOUNT_LOOP( \
1732 "\tloop:\t\tMounts a file via loop device.\n" \
1733 ) \
1734 "\tsuid/nosuid:\tAllow set-user-id-root programs / disallow them.\n" \
1735 "\tremount:\tRe-mount a mounted filesystem, changing its flags.\n" \
1736 "\tro/rw:\t\tMount for read-only / read-write.\n" \
1737 "\tbind:\t\tUse the linux 2.4.x \"bind\" feature.\n" \
1738 "\nThere are EVEN MORE flags that are specific to each filesystem.\n" \
1739 "You'll have to see the written documentation for those filesystems."
1740#define mount_example_usage \
1741 "$ mount\n" \
1742 "/dev/hda3 on / type minix (rw)\n" \
1743 "proc on /proc type proc (rw)\n" \
1744 "devpts on /dev/pts type devpts (rw)\n" \
1745 "$ mount /dev/fd0 /mnt -t msdos -o ro\n" \
1746 "$ mount /tmp/diskimage /opt -t ext2 -o loop\n"
1747
1748#define mt_trivial_usage \
1749 "[-f device] opcode value"
1750#define mt_full_usage \
1751 "Control magnetic tape drive operation\n" \
1752 "\nAvailable Opcodes:\n\n" \
1753 "bsf bsfm bsr bss datacompression drvbuffer eof eom erase\n" \
1754 "fsf fsfm fsr fss load lock mkpart nop offline ras1 ras2\n" \
1755 "ras3 reset retension rewind rewoffline seek setblk setdensity\n" \
1756 "setpart tell unload unlock weof wset"
1757
1758#define mv_trivial_usage \
1759 "[OPTION]... SOURCE DEST\n" \
1760 "or: mv [OPTION]... SOURCE... DIRECTORY"
1761#define mv_full_usage \
1762 "Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\n" \
1763 "Options:\n" \
1764 "\t-f\tdon't prompt before overwriting\n" \
1765 "\t-i\tinteractive, prompt before overwrite"
1766#define mv_example_usage \
1767 "$ mv /tmp/foo /bin/bar\n"
1768
1769#define nameif_trivial_usage \
1770 "[-s] [-c FILE] [{IFNAME MACADDR}]"
1771#define nameif_full_usage \
1772 "Nameif renaming network interface while it in the down state.\n\n" \
1773 "Options:\n" \
1774 "\t-c FILE\t\tUse configuration file (default is /etc/mactab)\n" \
1775 "\t-s\t\tUse syslog (LOCAL0 facility).\n" \
1776 "\tIFNAME MACADDR\tnew_interface_name interface_mac_address"
1777#define nameif_example_usage \
1778 "$ nameif -s dmz0 00:A0:C9:8C:F6:3F\n" \
1779 " or\n" \
1780 "$ nameif -c /etc/my_mactab_file\n" \
1781
1782#define nc_trivial_usage \
1783 "[OPTIONS] [IP] [port]"
1784#define nc_full_usage \
1785 "Netcat opens a pipe to IP:port\n\n" \
1786 "Options:\n" \
1787 "\t-l\t\tlisten mode, for inbound connects\n" \
1788 "\t-p PORT\t\tlocal port number\n" \
1789 "\t-i SECS\t\tdelay interval for lines sent\n" \
1790 "\t-e PROG\t\tprogram to exec after connect (dangerous!)"
1791#define nc_example_usage \
1792 "$ nc foobar.somedomain.com 25\n" \
1793 "220 foobar ESMTP Exim 3.12 #1 Sat, 15 Apr 2000 00:03:02 -0600\n" \
1794 "help\n" \
1795 "214-Commands supported:\n" \
1796 "214- HELO EHLO MAIL RCPT DATA AUTH\n" \
1797 "214 NOOP QUIT RSET HELP\n" \
1798 "quit\n" \
1799 "221 foobar closing connection\n"
1800
1801#define netstat_trivial_usage \
1802 "[-laenrtuwx]"
1803#define netstat_full_usage \
1804 "Netstat displays Linux networking information.\n\n" \
1805 "Options:\n" \
1806 "\t-l display listening server sockets\n" \
1807 "\t-a display all sockets (default: connected)\n" \
1808 "\t-e display other/more information\n" \
1809 "\t-n don't resolve names\n" \
1810 "\t-r display routing table\n" \
1811 "\t-t tcp sockets\n" \
1812 "\t-u udp sockets\n" \
1813 "\t-w raw sockets\n" \
1814 "\t-x unix sockets"
1815
1816#define nslookup_trivial_usage \
1817 "[HOST] [SERVER]"
1818#define nslookup_full_usage \
1819 "Queries the nameserver for the IP address of the given HOST\n" \
1820 "optionally using a specified DNS server"
1821#define nslookup_example_usage \
1822 "$ nslookup localhost\n" \
1823 "Server: default\n" \
1824 "Address: default\n" \
1825 "\n" \
1826 "Name: debian\n" \
1827 "Address: 127.0.0.1\n"
1828
1829#define od_trivial_usage \
1830 "[-aBbcDdeFfHhIiLlOovXx] [FILE]"
1831#define od_full_usage \
1832 "Write an unambiguous representation, octal bytes by default, of FILE\n"\
1833 "to standard output. With no FILE, or when FILE is -, read standard input."
1834
1835#define openvt_trivial_usage \
1836 "<vtnum> <COMMAND> [ARGS...]"
1837#define openvt_full_usage \
1838 "Start a command on a new virtual terminal"
1839#define openvt_example_usage \
1840 "openvt 2 /bin/ash\n"
1841
1842#ifdef CONFIG_FEATURE_SHA1_PASSWORDS
1843 #define PASSWORD_ALG_TYPES(a) a
1844#else
1845 #define PASSWORD_ALG_TYPES(a)
1846#endif
1847#define passwd_trivial_usage \
1848 "[OPTION] [name]"
1849#define passwd_full_usage \
1850 "Change a user password. If no name is specified,\n" \
1851 "changes the password for the current user.\n" \
1852 "Options:\n" \
1853 "\t-a\tDefine which algorithm shall be used for the password.\n" \
1854 "\t\t\t(Choices: des, md5" \
1855 PASSWORD_ALG_TYPES(", sha1") \
1856 ")\n\t-d\tDelete the password for the specified user account.\n" \
1857 "\t-l\tLocks (disables) the specified user account.\n" \
1858 "\t-u\tUnlocks (re-enables) the specified user account."
1859
1860#define patch_trivial_usage \
1861 "[-p<num>]"
1862#define patch_full_usage \
1863 "[-p<num>]"
1864#define patch_example_usage \
1865 "$ patch -p1 <example.diff"
1866
1867#define pidof_trivial_usage \
1868 "process-name [OPTION] [process-name ...]"
1869#define pidof_full_usage \
1870 "Lists the PIDs of all processes with names that match the\n" \
1871 "names on the command line.\n" \
1872 "Options:\n" \
1873 "\t-s\t\tdisplay only a single PID."
1874#define pidof_example_usage \
1875 "$ pidof init\n" \
1876 "1\n"
1877
1878#ifndef CONFIG_FEATURE_FANCY_PING
1879#define ping_trivial_usage "host"
1880#define ping_full_usage "Send ICMP ECHO_REQUEST packets to network hosts"
1881#else
1882#define ping_trivial_usage \
1883 "[OPTION]... host"
1884#define ping_full_usage \
1885 "Send ICMP ECHO_REQUEST packets to network hosts.\n\n" \
1886 "Options:\n" \
1887 "\t-c COUNT\tSend only COUNT pings.\n" \
1888 "\t-s SIZE\t\tSend SIZE data bytes in packets (default=56).\n" \
1889 "\t-q\t\tQuiet mode, only displays output at start\n" \
1890 "\t\t\tand when finished."
1891#endif
1892#define ping_example_usage \
1893 "$ ping localhost\n" \
1894 "PING slag (127.0.0.1): 56 data bytes\n" \
1895 "64 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=20.1 ms\n" \
1896 "\n" \
1897 "--- debian ping statistics ---\n" \
1898 "1 packets transmitted, 1 packets received, 0% packet loss\n" \
1899 "round-trip min/avg/max = 20.1/20.1/20.1 ms\n"
1900
1901#ifndef CONFIG_FEATURE_FANCY_PING6
1902#define ping6_trivial_usage "host"
1903#define ping6_full_usage "Send ICMP ECHO_REQUEST packets to network hosts"
1904#else
1905#define ping6_trivial_usage \
1906 "[OPTION]... host"
1907#define ping6_full_usage \
1908 "Send ICMP ECHO_REQUEST packets to network hosts.\n\n" \
1909 "Options:\n" \
1910 "\t-c COUNT\tSend only COUNT pings.\n" \
1911 "\t-s SIZE\t\tSend SIZE data bytes in packets (default=56).\n" \
1912 "\t-q\t\tQuiet mode, only displays output at start\n" \
1913 "\t\t\tand when finished."
1914#endif
1915#define ping6_example_usage \
1916 "$ ping6 ip6-localhost\n" \
1917 "PING ip6-localhost (::1): 56 data bytes\n" \
1918 "64 bytes from ::1: icmp6_seq=0 ttl=64 time=20.1 ms\n" \
1919 "\n" \
1920 "--- ip6-localhost ping statistics ---\n" \
1921 "1 packets transmitted, 1 packets received, 0% packet loss\n" \
1922 "round-trip min/avg/max = 20.1/20.1/20.1 ms\n"
1923
1924#define pivot_root_trivial_usage \
1925 "NEW_ROOT PUT_OLD"
1926#define pivot_root_full_usage \
1927 "Move the current root file system to PUT_OLD and make NEW_ROOT\n" \
1928 "the new root file system."
1929
1930#define poweroff_trivial_usage \
1931 "[-d<delay>]"
1932#define poweroff_full_usage \
1933 "Halt the system and request that the kernel shut off the power.\n" \
1934 "Options:\n" \
1935 "\t-d\t\tdelay interval for shutting off."
1936
1937#define printf_trivial_usage \
1938 "FORMAT [ARGUMENT...]"
1939#define printf_full_usage \
1940 "Formats and prints ARGUMENT(s) according to FORMAT,\n" \
1941 "Where FORMAT controls the output exactly as in C printf."
1942#define printf_example_usage \
1943 "$ printf "Val=%d\\n" 5\n" \
1944 "Val=5\n"
1945
1946#ifdef CONFIG_SELINUX
1947#define USAGE_NONSELINUX(a)
1948#else
1949#define USAGE_NONSELINUX(a) a
1950#endif
1951
1952#define ps_trivial_usage \
1953 ""
1954#define ps_full_usage \
1955 "Report process status\n" \
1956 USAGE_NONSELINUX("\n\tThis version of ps accepts no options.") \
1957 USAGE_SELINUX("\nOptions:\n\t-c\tshow SE Linux context")
1958
1959#define ps_example_usage \
1960 "$ ps\n" \
1961 " PID Uid Gid State Command\n" \
1962 " 1 root root S init\n" \
1963 " 2 root root S [kflushd]\n" \
1964 " 3 root root S [kupdate]\n" \
1965 " 4 root root S [kpiod]\n" \
1966 " 5 root root S [kswapd]\n" \
1967 " 742 andersen andersen S [bash]\n" \
1968 " 743 andersen andersen S -bash\n" \
1969 " 745 root root S [getty]\n" \
1970 " 2990 andersen andersen R ps\n"
1971
1972#define pwd_trivial_usage \
1973 ""
1974#define pwd_full_usage \
1975 "Print the full filename of the current working directory."
1976#define pwd_example_usage \
1977 "$ pwd\n" \
1978 "/root\n"
1979
1980#define rdate_trivial_usage \
1981 "[-sp] HOST"
1982#define rdate_full_usage \
1983 "Get and possibly set the system date and time from a remote HOST.\n\n" \
1984 "Options:\n" \
1985 "\t-s\tSet the system date and time (default).\n" \
1986 "\t-p\tPrint the date and time."
1987
1988#define readlink_trivial_usage \
1989 ""
1990#define readlink_full_usage \
1991 "Displays the value of a symbolic link."
1992
1993#define realpath_trivial_usage \
1994 "pathname ..."
1995#define realpath_full_usage \
1996 "Returns the absolute pathnames of given argument."
1997
1998#define reboot_trivial_usage \
1999 "[-d<delay>]"
2000#define reboot_full_usage \
2001 "Reboot the system.\n" \
2002 "Options:\n" \
2003 "\t-d\t\tdelay interval for rebooting."
2004
2005#define renice_trivial_usage \
2006 "priority pid [pid ...]"
2007#define renice_full_usage \
2008 "Changes priority of running processes. Allowed priorities range\n" \
2009 "from 20 (the process runs only when nothing else is running) to 0\n" \
2010 "(default priority) to -20 (almost nothing else ever gets to run)."
2011
2012#define reset_trivial_usage \
2013 ""
2014#define reset_full_usage \
2015 "Resets the screen."
2016
2017#define rm_trivial_usage \
2018 "[OPTION]... FILE..."
2019#define rm_full_usage \
2020 "Remove (unlink) the FILE(s). You may use '--' to\n" \
2021 "indicate that all following arguments are non-options.\n\n" \
2022 "Options:\n" \
2023 "\t-i\t\talways prompt before removing each destination\n" \
2024 "\t-f\t\tremove existing destinations, never prompt\n" \
2025 "\t-r or -R\tremove the contents of directories recursively"
2026#define rm_example_usage \
2027 "$ rm -rf /tmp/foo\n"
2028
2029#define rmdir_trivial_usage \
2030 "[OPTION]... DIRECTORY..."
2031#define rmdir_full_usage \
2032 "Remove the DIRECTORY(ies), if they are empty."
2033#define rmdir_example_usage \
2034 "# rmdir /tmp/foo\n"
2035
2036#define rmmod_trivial_usage \
2037 "[OPTION]... [MODULE]..."
2038#define rmmod_full_usage \
2039 "Unloads the specified kernel modules from the kernel.\n\n" \
2040 "Options:\n" \
2041 "\t-a\tRemove all unused modules (recursively)"
2042#define rmmod_example_usage \
2043 "$ rmmod tulip\n"
2044
2045#ifdef CONFIG_FEATURE_IPV6
2046 #define USAGE_ROUTE_IPV6(a) a
2047#else
2048 #define USAGE_ROUTE_IPV6(a) "\t"
2049#endif
2050
2051
2052#define route_trivial_usage \
2053 "[{add|del|delete}]"
2054#define route_full_usage \
2055 "Edit the kernel's routing tables.\n\n" \
2056 "Options:\n" \
2057 "\t-n\t\tDont resolve names.\n" \
2058 "\t-e\t\tDisplay other/more information.\n" \
2059 "\t-A inet" USAGE_ROUTE_IPV6("{6}") "\tSelect address family."
2060
2061#define rpm_trivial_usage \
2062 "-i -q[ildc]p package.rpm"
2063#define rpm_full_usage \
2064 "Manipulates RPM packages" \
2065 "\n\nOptions:" \
2066 "\n\t-i Install package" \
2067 "\n\t-q Query package" \
2068 "\n\t-p Query uninstalled package" \
2069 "\n\t-i Show information" \
2070 "\n\t-l List contents" \
2071 "\n\t-d List documents" \
2072 "\n\t-c List config files"
2073
2074#define rpm2cpio_trivial_usage \
2075 "package.rpm"
2076#define rpm2cpio_full_usage \
2077 "Outputs a cpio archive of the rpm file."
2078
2079#define run_parts_trivial_usage \
2080 "[-t] [-a ARG] [-u MASK] DIRECTORY"
2081#define run_parts_full_usage \
2082 "Run a bunch of scripts in a directory.\n\n" \
2083 "Options:\n" \
2084 "\t-t\tPrints what would be run, but does not actually run anything.\n" \
2085 "\t-a ARG\tPass ARG as an argument for every program invoked.\n" \
2086 "\t-u MASK\tSet the umask to MASK before executing every program."
2087
2088#define rx_trivial_usage \
2089 "FILE"
2090#define rx_full_usage \
2091 "Receive a file using the xmodem protocol."
2092#define rx_example_usage \
2093 "$ rx /tmp/foo\n"
2094
2095#define sed_trivial_usage \
2096 "[-efinr] pattern [files...]"
2097#define sed_full_usage \
2098 "Options:\n" \
2099 "\t-e script\tadd the script to the commands to be executed\n" \
2100 "\t-f scriptfile\tadd script-file contents to the\n" \
2101 "\t\t\tcommands to be executed\n" \
2102 "\t-i\t\tedit files in-place\n" \
2103 "\t-n\t\tsuppress automatic printing of pattern space\n" \
2104 "\t-r\t\tuse extended regular expression syntax\n" \
2105 "\n" \
2106 "If no -e or -f is given, the first non-option argument is taken as the sed\n"\
2107 "script to interpret. All remaining arguments are names of input files; if no\n"\
2108 "input files are specified, then the standard input is read. Source files\n" \
2109 "will not be modified unless -i option is given."
2110
2111#define sed_example_usage \
2112 "$ echo "foo" | sed -e 's/f[a-zA-Z]o/bar/g'\n" \
2113 "bar\n"
2114
2115#define seq_trivial_usage \
2116 "[first [increment]] last"
2117#define seq_full_usage \
2118 "Print numbers from FIRST to LAST, in steps of INCREMENT.\n" \
2119 "FIRST, INCREMENT default to 1\n" \
2120 "Arguments:\n" \
2121 "\tLAST\n" \
2122 "\tFIRST\tLAST\n" \
2123 "\tFIRST\tINCREMENT\tLAST"
2124
2125#define setkeycodes_trivial_usage \
2126 "SCANCODE KEYCODE ..."
2127#define setkeycodes_full_usage \
2128 "Set entries into the kernel's scancode-to-keycode map,\n" \
2129 "allowing unusual keyboards to generate usable keycodes.\n\n" \
2130 "SCANCODE may be either xx or e0xx (hexadecimal),\n" \
2131 "and KEYCODE is given in decimal"
2132#define setkeycodes_example_usage \
2133 "$ setkeycodes e030 127\n"
2134
2135#define lash_trivial_usage \
2136 "[FILE]...\n" \
2137 "or: sh -c command [args]..."
2138#define lash_full_usage \
2139 "The BusyBox LAme SHell (command interpreter)"
2140#define lash_notes_usage \
2141 "This command does not yet have proper documentation.\n\n" \
2142 "Use lash just as you would use any other shell. It properly handles pipes,\n" \
2143 "redirects, job control, can be used as the shell for scripts, and has a\n" \
2144 "sufficient set of builtins to do what is needed. It does not (yet) support\n" \
2145 "Bourne Shell syntax. If you need things like "if-then-else", "while", and such\n" \
2146 "use ash or bash. If you just need a very simple and extremely small shell,\n" \
2147 "this will do the job."
2148
2149#define last_trivial_usage \
2150 ""
2151#define last_full_usage \
2152 "Shows listing of the last users that logged into the system"
2153
2154#define sha1sum_trivial_usage \
2155 "[OPTION] [FILEs...]" \
2156 USAGE_MD5_SHA1_SUM_CHECK("\n or: sha1sum [OPTION] -c [FILE]")
2157#define sha1sum_full_usage \
2158 "Print" USAGE_MD5_SHA1_SUM_CHECK(" or check") " SHA1 checksums.\n\n" \
2159 "Options:\n" \
2160 "With no FILE, or when FILE is -, read standard input." \
2161 USAGE_MD5_SHA1_SUM_CHECK("\n\n" \
2162 "\t-c\tcheck SHA1 sums against given list\n" \
2163 "\nThe following two options are useful only when verifying checksums:\n" \
2164 "\t-s\tdon't output anything, status code shows success\n" \
2165 "\t-w\twarn about improperly formated SHA1 checksum lines")
2166
2167#ifdef CONFIG_FEATURE_FANCY_SLEEP
2168 #define USAGE_FANCY_SLEEP(a) a
2169 #define USAGE_NOT_FANCY_SLEEP(a)
2170#else
2171 #define USAGE_FANCY_SLEEP(a)
2172 #define USAGE_NOT_FANCY_SLEEP(a) a
2173#endif
2174
2175#define sleep_trivial_usage \
2176 USAGE_FANCY_SLEEP("[") "N" USAGE_FANCY_SLEEP("]...")
2177#define sleep_full_usage \
2178 USAGE_NOT_FANCY_SLEEP("Pause for N seconds.") \
2179 USAGE_FANCY_SLEEP( \
2180 "Pause for a time equal to the total of the args given, where each arg can\n" \
2181 "\t\thave an optional suffix of (s)econds, (m)inutes, (h)ours, or (d)ays.")
2182#define sleep_example_usage \
2183 "$ sleep 2\n" \
2184 "[2 second delay results]\n" \
2185 USAGE_FANCY_SLEEP("$ sleep 1d 3h 22m 8s\n" \
2186 "[98528 second delay results]\n")
2187
2188#ifdef CONFIG_FEATURE_SORT_UNIQUE
2189 #define USAGE_SORT_UNIQUE(a) a
2190#else
2191 #define USAGE_SORT_UNIQUE(a)
2192#endif
2193#ifdef CONFIG_FEATURE_SORT_REVERSE
2194 #define USAGE_SORT_REVERSE(a) a
2195#else
2196 #define USAGE_SORT_REVERSE(a)
2197#endif
2198#define sort_trivial_usage \
2199 "[-n" USAGE_SORT_REVERSE("r") USAGE_SORT_UNIQUE("u") "] [FILE]..."
2200#define sort_full_usage \
2201 "Sorts lines of text in the specified files\n\n"\
2202 "Options:\n" \
2203 USAGE_SORT_UNIQUE("\t-u\tsuppress duplicate lines\n") \
2204 USAGE_SORT_REVERSE("\t-r\tsort in reverse order\n") \
2205 "\t-n\tsort numerics"
2206#define sort_example_usage \
2207 "$ echo -e \"e\\nf\\nb\\nd\\nc\\na\" | sort\n" \
2208 "a\n" \
2209 "b\n" \
2210 "c\n" \
2211 "d\n" \
2212 "e\n" \
2213 "f\n"
2214
2215#define start_stop_daemon_trivial_usage \
2216 "[OPTIONS] [--start|--stop] ... [-- arguments...]\n"
2217#define start_stop_daemon_full_usage \
2218 "Program to start and stop services."\
2219 "\n\nOptions:"\
2220 "\n\t-S|--start\t\t\tstart"\
2221 "\n\t-K|--stop\t\t\tstop"\
2222 "\n\t-a|--startas <pathname>\t\tstarts process specified by pathname"\
2223 "\n\t-b|--background\t\t\tforce process into background"\
2224 "\n\t-u|--user <username>|<uid>\tstop this user's processes"\
2225 "\n\t-x|--exec <executable>\t\tprogram to either start or check"\
2226 "\n\t-m|--make-pidfile <filename>\tcreate the -p file and enter pid in it"\
2227 "\n\t-n|--name <process-name>\tstop processes with this name"\
2228 "\n\t-p|--pidfile <pid-file>\t\tsave or load pid using a pid-file"\
2229 "\n\t-q|--quiet\t\t\tbe quiet" \
2230 "\n\t-s|--signal <signal>\t\tsignal to send (default TERM)"
2231
2232#define strings_trivial_usage \
2233 "[-afo] [-n length] [file ... ]"
2234#define strings_full_usage \
2235 "Display printable strings in a binary file." \
2236 "\n\nOptions:" \
2237 "\n\t-a\tScan the whole files (this is the default)."\
2238 "\n\t-f\tPrecede each string with the name of the file where it was found." \
2239 "\n\t-n N\tSpecifies that at least N characters forms a sequence (default 4)" \
2240 "\n\t-o\tEach string is preceded by its decimal offset in the file."
2241
2242#define stty_trivial_usage \
2243 "[-a|g] [-F DEVICE] [SETTING]..."
2244#define stty_full_usage \
2245 "Without arguments, prints baud rate, line discipline," \
2246 "\nand deviations from stty sane." \
2247 "\n\nOptions:" \
2248 "\n\t-F DEVICE\topen device instead of stdin" \
2249 "\n\t-a\t\tprint all current settings in human-readable form" \
2250 "\n\t-g\t\tprint in stty-readable form" \
2251 "\n\t[SETTING]\tsee manpage"
2252
2253#define su_trivial_usage \
2254 "[OPTION]... [-] [username]"
2255#define su_full_usage \
2256 "Change user id or become root.\n" \
2257 "Options:\n" \
2258 "\t-p\tPreserve environment"
2259
2260#define sulogin_trivial_usage \
2261 "[OPTION]... [tty-device]"
2262#define sulogin_full_usage \
2263 "Single user login\n" \
2264 "Options:\n" \
2265 "\t-f\tDo not authenticate (user already authenticated)\n" \
2266 "\t-h\tName of the remote host for this login.\n" \
2267 "\t-p\tPreserve environment."
2268
2269#define swapoff_trivial_usage \
2270 "[OPTION] [DEVICE]"
2271#define swapoff_full_usage \
2272 "Stop swapping virtual memory pages on DEVICE.\n\n" \
2273 "Options:\n" \
2274 "\t-a\tStop swapping on all swap devices"
2275
2276#define swapon_trivial_usage \
2277 "[OPTION] [DEVICE]"
2278#define swapon_full_usage \
2279 "Start swapping virtual memory pages on DEVICE.\n\n" \
2280 "Options:\n" \
2281 "\t-a\tStart swapping on all swap devices"
2282
2283#define sync_trivial_usage \
2284 ""
2285#define sync_full_usage \
2286 "Write all buffered filesystem blocks to disk."
2287
2288
2289#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
2290 #define USAGE_ROTATE_LOGFILE(a) a
2291#else
2292 #define USAGE_ROTATE_LOGFILE(a)
2293#endif
2294#ifdef CONFIG_FEATURE_REMOTE_LOG
2295 #define USAGE_REMOTE_LOG(a) a
2296#else
2297 #define USAGE_REMOTE_LOG(a)
2298#endif
2299#ifdef CONFIG_FEATURE_IPC_SYSLOG
2300 #define USAGE_IPC_LOG(a) a
2301#else
2302 #define USAGE_IPC_LOG(a)
2303#endif
2304
2305#ifdef CONFIG_SYSCTL
2306#define sysctl_trivial_usage \
2307 "[OPTIONS]... [VALUE]...\n"
2308#define sysctl_full_usage
2309 "sysctl - configure kernel parameters at runtime\n\n" \
2310 "Options:\n" \
2311 "\t-n\tUse this option to disable printing of the key name when printing values.\n" \
2312 "\t-w\tUse this option when you want to change a sysctl setting.\n" \
2313 "\t-p\tLoad in sysctl settings from the file specified or /etc/sysctl.conf if none given.\n" \
2314 "\t-a\tDisplay all values currently available.\n" \
2315 "\t-A\tDisplay all values currently available in table form."
2316#define sysctl_example_usage
2317 "sysctl [-n] variable ...\n" \
2318 "sysctl [-n] -w variable=value ...\n" \
2319 "sysctl [-n] -a\n" \
2320 "sysctl [-n] -p <file>\t(default /etc/sysctl.conf)\n" \
2321 "sysctl [-n] -A\n"
2322#endif
2323
2324#define syslogd_trivial_usage \
2325 "[OPTION]..."
2326#define syslogd_full_usage \
2327 "Linux system and kernel logging utility.\n" \
2328 "Note that this version of syslogd ignores /etc/syslog.conf.\n\n" \
2329 "Options:\n" \
2330 "\t-m MIN\t\tMinutes between MARK lines (default=20, 0=off)\n" \
2331 "\t-n\t\tRun as a foreground process\n" \
2332 "\t-O FILE\t\tUse an alternate log file (default=/var/log/messages)\n" \
2333 "\t-S\t\tMake logging output smaller." \
2334 USAGE_ROTATE_LOGFILE( \
2335 "\n\t-s SIZE\t\tMax size (KB) before rotate (default=200KB, 0=off)\n" \
2336 "\t-b NUM\t\tNumber of rotated logs to keep (default=1, max=99, 0=purge)") \
2337 USAGE_REMOTE_LOG( \
2338 "\n\t-R HOST[:PORT]\tLog to IP or hostname on PORT (default PORT=514/UDP)\n" \
2339 "\t-L\t\tLog locally and via network logging (default is network only)") \
2340 USAGE_IPC_LOG( \
2341 "\n\t-C [size(KiB)]\tLog to a circular buffer (read the buffer using logread)")
2342#define syslogd_example_usage \
2343 "$ syslogd -R masterlog:514\n" \
2344 "$ syslogd -R 192.168.1.1:601\n"
2345
2346
2347#ifndef CONFIG_FEATURE_FANCY_TAIL
2348 #define USAGE_UNSIMPLE_TAIL(a)
2349#else
2350 #define USAGE_UNSIMPLE_TAIL(a) a
2351#endif
2352#define tail_trivial_usage \
2353 "[OPTION]... [FILE]..."
2354#define tail_full_usage \
2355 "Print last 10 lines of each FILE to standard output.\n" \
2356 "With more than one FILE, precede each with a header giving the\n" \
2357 "file name. With no FILE, or when FILE is -, read standard input.\n\n" \
2358 "Options:\n" \
2359 USAGE_UNSIMPLE_TAIL("\t-c N[kbm]\toutput the last N bytes\n") \
2360 "\t-n N[kbm]\tprint last N lines instead of last 10\n" \
2361 "\t-f\t\toutput data as the file grows" \
2362 USAGE_UNSIMPLE_TAIL( "\n\t-q\t\tnever output headers giving file names\n" \
2363 "\t-s SEC\t\twait SEC seconds between reads with -f\n" \
2364 "\t-v\t\talways output headers giving file names\n\n" \
2365 "If the first character of N (bytes or lines) is a '+', output begins with \n" \
2366 "the Nth item from the start of each file, otherwise, print the last N items\n" \
2367 "in the file. N bytes may be suffixed by k (x1024), b (x512), or m (1024^2)." )
2368#define tail_example_usage \
2369 "$ tail -n 1 /etc/resolv.conf\n" \
2370 "nameserver 10.0.0.1\n"
2371
2372#ifdef CONFIG_FEATURE_TAR_CREATE
2373 #define USAGE_TAR_CREATE(a) a
2374#else
2375 #define USAGE_TAR_CREATE(a)
2376#endif
2377#ifdef CONFIG_FEATURE_TAR_EXCLUDE
2378 #define USAGE_TAR_EXCLUDE(a) a
2379#else
2380 #define USAGE_TAR_EXCLUDE(a)
2381#endif
2382#ifdef CONFIG_FEATURE_TAR_GZIP
2383 #define USAGE_TAR_GZIP(a) a
2384#else
2385 #define USAGE_TAR_GZIP(a)
2386#endif
2387#ifdef CONFIG_FEATURE_TAR_BZIP2
2388 #define USAGE_TAR_BZIP2(a) a
2389#else
2390 #define USAGE_TAR_BZIP2(a)
2391#endif
2392#ifdef CONFIG_FEATURE_TAR_COMPRESS
2393 #define USAGE_TAR_COMPRESS(a) a
2394#else
2395 #define USAGE_TAR_COMPRESS(a)
2396#endif
2397
2398#define tar_trivial_usage \
2399 "-[" USAGE_TAR_CREATE("c") USAGE_TAR_GZIP("z") USAGE_TAR_BZIP2("j") USAGE_TAR_COMPRESS("Z") "xtvO] " \
2400 USAGE_TAR_EXCLUDE("[-X FILE]") \
2401 "[-f TARFILE] [-C DIR] [FILE(s)] ..."
2402#define tar_full_usage \
2403 "Create, extract, or list files from a tar file.\n\n" \
2404 "Options:\n" \
2405 USAGE_TAR_CREATE("\tc\t\tcreate\n") \
2406 "\tx\t\textract\n" \
2407 "\tt\t\tlist\n" \
2408 "\nArchive format selection:\n" \
2409 USAGE_TAR_GZIP("\tz\t\tFilter the archive through gzip\n") \
2410 USAGE_TAR_BZIP2("\tj\t\tFilter the archive through bzip2\n") \
2411 USAGE_TAR_COMPRESS("\tZ\t\tFilter the archive through compress\n") \
2412 "\nFile selection:\n" \
2413 "\tf\t\tname of TARFILE or \"-\" for stdin\n" \
2414 "\tO\t\textract to stdout\n" \
2415 USAGE_TAR_EXCLUDE( \
2416 "\texclude\t\tfile to exclude\n" \
2417 "\tX\t\tfile with names to exclude\n" \
2418 ) \
2419 "\tC\t\tchange to directory DIR before operation\n" \
2420 "\tv\t\tverbosely list files processed"
2421#define tar_example_usage \
2422 "$ zcat /tmp/tarball.tar.gz | tar -xf -\n" \
2423 "$ tar -cf /tmp/tarball.tar /usr/local\n"
2424
2425#define tee_trivial_usage \
2426 "[OPTION]... [FILE]..."
2427#define tee_full_usage \
2428 "Copy standard input to each FILE, and also to standard output.\n\n" \
2429 "Options:\n" \
2430 "\t-a\tappend to the given FILEs, do not overwrite\n" \
2431 "\t-i\tignore interrupt signals (SIGINT)"
2432#define tee_example_usage \
2433 "$ echo "Hello" | tee /tmp/foo\n" \
2434 "$ cat /tmp/foo\n" \
2435 "Hello\n"
2436
2437#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
2438#define telnet_trivial_usage \
2439 "[-a] [-l USER] HOST [PORT]"
2440#define telnet_full_usage \
2441 "Telnet is used to establish interactive communication with another\n" \
2442 "computer over a network using the TELNET protocol.\n\n" \
2443 "Options:\n" \
2444 "\t-a\t\tAttempt an automatic login with the USER variable.\n" \
2445 "\t-l USER\t\tAttempt an automatic login with the USER argument.\n" \
2446 "\tHOST\t\tThe official name, alias or the IP address of the\n" \
2447 "\t\t\tremote host.\n" \
2448 "\tPORT\t\tThe remote port number to connect to. If it is not\n" \
2449 "\t\t\tspecified, the default telnet (23) port is used."
2450#else
2451#define telnet_trivial_usage \
2452 "HOST [PORT]"
2453#define telnet_full_usage \
2454 "Telnet is used to establish interactive communication with another\n"\
2455 "computer over a network using the TELNET protocol."
2456#endif
2457
2458#ifdef CONFIG_FEATURE_TELNETD_INETD
2459#define telnetd_trivial_usage \
2460 "(inetd mode) [OPTION]"
2461#define telnetd_full_usage \
2462 "Telnetd uses incoming TELNET connections via inetd.\n"\
2463 "Options:\n" \
2464 "\t-l LOGIN\texec LOGIN on connect (default /bin/sh)\n" \
2465 "\t-f issue_file\tDisplay issue_file instead of /etc/issue."
2466#else
2467#define telnetd_trivial_usage \
2468 "[OPTION]"
2469#define telnetd_full_usage \
2470 "Telnetd listens for incoming TELNET connections on PORT.\n"\
2471 "Options:\n" \
2472 "\t-p PORT\tlisten for connections on PORT (default 23)\n"\
2473 "\t-l LOGIN\texec LOGIN on connect (default /bin/sh)\n"\
2474 "\t-f issue_file\tDisplay issue_file instead of /etc/issue."
2475#endif
2476
2477#define test_trivial_usage \
2478 "EXPRESSION\n or [ EXPRESSION ]"
2479#define test_full_usage \
2480 "Checks file types and compares values returning an exit\n" \
2481 "code determined by the value of EXPRESSION."
2482#define test_example_usage \
2483 "$ test 1 -eq 2\n" \
2484 "$ echo $?\n" \
2485 "1\n" \
2486 "$ test 1 -eq 1\n" \
2487 "$ echo $?\n" \
2488 "0\n" \
2489 "$ [ -d /etc ]\n" \
2490 "$ echo $?\n" \
2491 "0\n" \
2492 "$ [ -d /junk ]\n" \
2493 "$ echo $?\n" \
2494 "1\n"
2495
2496#ifdef CONFIG_FEATURE_TFTP_GET
2497 #define USAGE_TFTP_GET(a) a
2498#else
2499 #define USAGE_TFTP_GET(a)
2500#endif
2501#ifdef CONFIG_FEATURE_TFTP_PUT
2502 #define USAGE_TFTP_PUT(a) a
2503#else
2504 #define USAGE_TFTP_PUT(a)
2505#endif
2506#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
2507 #define USAGE_TFTP_BS(a) a
2508#else
2509 #define USAGE_TFTP_BS(a)
2510#endif
2511
2512#define tftp_trivial_usage \
2513 "[OPTION]... HOST [PORT]"
2514#define tftp_full_usage \
2515 "Transfers a file from/to a tftp server using \"octet\" mode.\n\n" \
2516 "Options:\n" \
2517 "\t-l FILE\tLocal FILE.\n" \
2518 "\t-r FILE\tRemote FILE." \
2519 USAGE_TFTP_GET( \
2520 "\n\t-g\tGet file." \
2521 ) \
2522 USAGE_TFTP_PUT( \
2523 "\n\t-p\tPut file." \
2524 ) \
2525 USAGE_TFTP_BS( \
2526 "\n\t-b SIZE\tTransfer blocks of SIZE octets." \
2527 )
2528#define time_trivial_usage \
2529 "[OPTION]... COMMAND [ARGS...]"
2530#define time_full_usage \
2531 "Runs the program COMMAND with arguments ARGS. When COMMAND finishes,\n" \
2532 "COMMAND's resource usage information is displayed\n\n" \
2533 "Options:\n" \
2534 "\t-v\tDisplays verbose resource usage information."
2535
2536#define top_trivial_usage \
2537 "[-d <seconds>]"
2538#define top_full_usage \
2539 "top provides an view of processor activity in real time.\n" \
2540 "This utility reads the status for all processes in /proc each <seconds>\n" \
2541 "and shows the status for however many processes will fit on the screen.\n" \
2542 "This utility will not show processes that are started after program startup,\n" \
2543 "but it will show the EXIT status for and PIDs that exit while it is running."
2544
2545#define touch_trivial_usage \
2546 "[-c] FILE [FILE ...]"
2547#define touch_full_usage \
2548 "Update the last-modified date on the given FILE[s].\n\n" \
2549 "Options:\n" \
2550 "\t-c\tDo not create any files"
2551#define touch_example_usage \
2552 "$ ls -l /tmp/foo\n" \
2553 "/bin/ls: /tmp/foo: No such file or directory\n" \
2554 "$ touch /tmp/foo\n" \
2555 "$ ls -l /tmp/foo\n" \
2556 "-rw-rw-r-- 1 andersen andersen 0 Apr 15 01:11 /tmp/foo\n"
2557
2558#define tr_trivial_usage \
2559 "[-cds] STRING1 [STRING2]"
2560#define tr_full_usage \
2561 "Translate, squeeze, and/or delete characters from\n" \
2562 "standard input, writing to standard output.\n\n" \
2563 "Options:\n" \
2564 "\t-c\ttake complement of STRING1\n" \
2565 "\t-d\tdelete input characters coded STRING1\n" \
2566 "\t-s\tsqueeze multiple output characters of STRING2 into one character"
2567#define tr_example_usage \
2568 "$ echo "gdkkn vnqkc" | tr [a-y] [b-z]\n" \
2569 "hello world\n"
2570
2571#define traceroute_trivial_usage \
2572 "[-dnrv] [-m max_ttl] [-p port#] [-q nqueries]\n"\
2573 "\t[-s src_addr] [-t tos] [-w wait] host [data size]"
2574#define traceroute_full_usage \
2575 "trace the route ip packets follow going to \"host\"\n" \
2576 "Options:\n" \
2577 "\t-d\tset SO_DEBUG options to socket\n" \
2578 "\t-n\tPrint hop addresses numerically rather than symbolically\n" \
2579 "\t-r\tBypass the normal routing tables and send directly to a host\n" \
2580 "\t-v\tVerbose output\n" \
2581 "\t-m max_ttl\tSet the max time-to-live (max number of hops)\n" \
2582 "\t-p port#\tSet the base UDP port number used in probes\n" \
2583 "\t\t(default is 33434)\n" \
2584 "\t-q nqueries\tSet the number of probes per ``ttl'' to nqueries\n" \
2585 "\t\t(default is 3)\n" \
2586 "\t-s src_addr\tUse the following IP address as the source address\n" \
2587 "\t-t tos\tSet the type-of-service in probe packets to the following value\n" \
2588 "\t\t(default 0)\n" \
2589 "\t-w wait\tSet the time (in seconds) to wait for a response to a probe\n" \
2590 "\t\t(default 3 sec.)."
2591
2592
2593#define true_trivial_usage \
2594 ""
2595#define true_full_usage \
2596 "Return an exit code of TRUE (0)."
2597#define true_example_usage \
2598 "$ true\n" \
2599 "$ echo $?\n" \
2600 "0\n"
2601
2602#define tty_trivial_usage \
2603 ""
2604#define tty_full_usage \
2605 "Print the file name of the terminal connected to standard input.\n\n"\
2606 "Options:\n" \
2607 "\t-s\tprint nothing, only return an exit status"
2608#define tty_example_usage \
2609 "$ tty\n" \
2610 "/dev/tty2\n"
2611
2612#define udhcpc_trivial_usage \
2613 "[-fbnqv] [-c CLIENTID] [-H HOSTNAME] [-i INTERFACE]\n[-p pidfile] [-r IP] [-s script]"
2614#define udhcpc_full_usage \
2615 "\t-c,\t--clientid=CLIENTID\tClient identifier\n" \
2616 "\t-H,\t--hostname=HOSTNAME\tClient hostname\n" \
2617 "\t-h,\t \tAlias for -H\n" \
2618 "\t-f,\t--foreground\tDo not fork after getting lease\n" \
2619 "\t-b,\t--background\tFork to background if lease cannot be immediately negotiated.\n" \
2620 "\t-i,\t--interface=INTERFACE\tInterface to use (default: eth0)\n" \
2621 "\t-n,\t--now\tExit with failure if lease cannot be immediately negotiated.\n" \
2622 "\t-p,\t--pidfile=file\tStore process ID of daemon in file\n" \
2623 "\t-q,\t--quit\tQuit after obtaining lease\n" \
2624 "\t-r,\t--request=IP\tIP address to request (default: none)\n" \
2625 "\t-s,\t--script=file\tRun file at dhcp events (default: /usr/share/udhcpc/default.script)\n" \
2626 "\t-v,\t--version\tDisplay version"
2627
2628#define udhcpd_trivial_usage \
2629 "[configfile]\n" \
2630
2631#define udhcpd_full_usage \
2632 ""
2633
2634#ifdef CONFIG_FEATURE_MOUNT_FORCE
2635 #define USAGE_MOUNT_FORCE(a) a
2636#else
2637 #define USAGE_MOUNT_FORCE(a)
2638#endif
2639#define umount_trivial_usage \
2640 "[flags] FILESYSTEM|DIRECTORY"
2641#define umount_full_usage \
2642 "Unmount file systems\n" \
2643 "\nFlags:\n" "\t-a\tUnmount all file systems" \
2644 USAGE_MTAB(" in /etc/mtab\n\t-n\tDon't erase /etc/mtab entries") \
2645 "\n\t-r\tTry to remount devices as read-only if mount is busy" \
2646 USAGE_MOUNT_FORCE("\n\t-f\tForce umount (i.e., unreachable NFS server)") \
2647 USAGE_MOUNT_LOOP("\n\t-l\tDo not free loop device (if a loop device has been used)")
2648#define umount_example_usage \
2649 "$ umount /dev/hdc1 \n"
2650
2651#define uname_trivial_usage \
2652 "[OPTION]..."
2653#define uname_full_usage \
2654 "Print certain system information. With no OPTION, same as -s.\n\n" \
2655 "Options:\n" \
2656 "\t-a\tprint all information\n" \
2657 "\t-m\tthe machine (hardware) type\n" \
2658 "\t-n\tprint the machine's network node hostname\n" \
2659 "\t-r\tprint the operating system release\n" \
2660 "\t-s\tprint the operating system name\n" \
2661 "\t-p\tprint the host processor type\n" \
2662 "\t-v\tprint the operating system version"
2663#define uname_example_usage \
2664 "$ uname -a\n" \
2665 "Linux debian 2.4.23 #2 Tue Dec 23 17:09:10 MST 2003 i686 GNU/Linux\n"
2666
2667#define uncompress_trivial_usage \
2668 "[-c] [-f] [ name ... ]"
2669#define uncompress_full_usage \
2670 "Uncompress .Z file[s]\n" \
2671 "Options:\n" \
2672 "\t-c\textract to stdout\n" \
2673 "\t-f\tforce overwrite an existing file"
2674
2675#define uniq_trivial_usage \
2676 "[OPTION]... [INPUT [OUTPUT]]"
2677#define uniq_full_usage \
2678 "Discard all but one of successive identical lines from INPUT\n" \
2679 "(or standard input), writing to OUTPUT (or standard output).\n\n" \
2680 "Options:\n" \
2681 "\t-c\tprefix lines by the number of occurrences\n" \
2682 "\t-d\tonly print duplicate lines\n" \
2683 "\t-u\tonly print unique lines\n" \
2684 "\t-f N\tskip the first N fields\n" \
2685 "\t-s N\tskip the first N chars (after any skipped fields)"
2686#define uniq_example_usage \
2687 "$ echo -e \"a\\na\\nb\\nc\\nc\\na\" | sort | uniq\n" \
2688 "a\n" \
2689 "b\n" \
2690 "c\n"
2691
2692#define unix2dos_trivial_usage \
2693 "[option] [FILE]"
2694#define unix2dos_full_usage \
2695 "Converts FILE from unix format to dos format. When no option\n" \
2696 "is given, the input is converted to the opposite output format.\n" \
2697 "When no file is given, uses stdin for input and stdout for output.\n" \
2698 "Options:\n" \
2699 "\t-u\toutput will be in UNIX format\n" \
2700 "\t-d\toutput will be in DOS format"
2701
2702#define unzip_trivial_usage \
2703 "[-opts[modifiers]] file[.zip] [list] [-x xlist] [-d exdir]"
2704#define unzip_full_usage \
2705 "Extracts files from ZIP archives.\n\n" \
2706 "Options:\n" \
2707 "\t-l\tlist archive contents (short form)\n" \
2708 "\t-n\tnever overwrite existing files (default)\n" \
2709 "\t-o\toverwrite files without prompting\n" \
2710 "\t-p\tsend output to stdout\n" \
2711 "\t-q\tbe quiet\n" \
2712 "\t-x\texclude these files\n" \
2713 "\t-d\textract files into this directory"
2714
2715#define uptime_trivial_usage \
2716 ""
2717#define uptime_full_usage \
2718 "Display the time since the last boot."
2719#define uptime_example_usage \
2720 "$ uptime\n" \
2721 " 1:55pm up 2:30, load average: 0.09, 0.04, 0.00\n"
2722
2723#define usleep_trivial_usage \
2724 "N"
2725#define usleep_full_usage \
2726 "Pause for N microseconds."
2727#define usleep_example_usage \
2728 "$ usleep 1000000\n" \
2729 "[pauses for 1 second]\n"
2730
2731#define uudecode_trivial_usage \
2732 "[FILE]..."
2733#define uudecode_full_usage \
2734 "Uudecode a file that is uuencoded.\n\n" \
2735 "Options:\n" \
2736 "\t-o FILE\tdirect output to FILE"
2737#define uudecode_example_usage \
2738 "$ uudecode -o busybox busybox.uu\n" \
2739 "$ ls -l busybox\n" \
2740 "-rwxr-xr-x 1 ams ams 245264 Jun 7 21:35 busybox\n"
2741
2742#define uuencode_trivial_usage \
2743 "[OPTION] [INFILE] REMOTEFILE"
2744#define uuencode_full_usage \
2745 "Uuencode a file.\n\n" \
2746 "Options:\n" \
2747 "\t-m\tuse base64 encoding per RFC1521"
2748#define uuencode_example_usage \
2749 "$ uuencode busybox busybox\n" \
2750 "begin 755 busybox\n" \
2751 "<encoded file snipped>\n" \
2752 "$ uudecode busybox busybox > busybox.uu\n" \
2753 "$\n"
2754
2755#define vconfig_trivial_usage \
2756 "COMMAND [OPTIONS] ..."
2757#define vconfig_full_usage \
2758 "vconfig lets you create and remove virtual ethernet devices.\n\n" \
2759 "Options:\n" \
2760 "\tadd [interface-name] [vlan_id]\n" \
2761 "\trem [vlan-name]\n" \
2762 "\tset_flag [interface-name] [flag-num] [0 | 1]\n" \
2763 "\tset_egress_map [vlan-name] [skb_priority] [vlan_qos]\n" \
2764 "\tset_ingress_map [vlan-name] [skb_priority] [vlan_qos]\n" \
2765 "\tset_name_type [name-type]"
2766
2767#define vi_trivial_usage \
2768 "[OPTION] [FILE]..."
2769#define vi_full_usage \
2770 "edit FILE.\n\n" \
2771 "Options:\n" \
2772 "\t-R\tRead-only- do not write to the file."
2773
2774#define vlock_trivial_usage \
2775 "[OPTIONS]"
2776#define vlock_full_usage \
2777 "Lock a virtual terminal. A password is required to unlock\n" \
2778 "Options:\n" \
2779 "\t-a\tLock all VTs"
2780
2781#define watch_trivial_usage \
2782 "[-n <seconds>] COMMAND..."
2783#define watch_full_usage \
2784 "Executes a program periodically.\n" \
2785 "Options:\n" \
2786 "\t-n\tLoop period in seconds - default is 2."
2787#define watch_example_usage \
2788 "$ watch date\n" \
2789 "Mon Dec 17 10:31:40 GMT 2000\n" \
2790 "Mon Dec 17 10:31:42 GMT 2000\n" \
2791 "Mon Dec 17 10:31:44 GMT 2000"
2792
2793#define watchdog_trivial_usage \
2794 "[-t <seconds>] DEV"
2795#define watchdog_full_usage \
2796 "Periodically write to watchdog device DEV.\n" \
2797 "Options:\n" \
2798 "\t-t\tTimer period in seconds - default is 30."
2799
2800#define wc_trivial_usage \
2801 "[OPTION]... [FILE]..."
2802#define wc_full_usage \
2803 "Print line, word, and byte counts for each FILE, and a total line if\n" \
2804 "more than one FILE is specified. With no FILE, read standard input.\n\n" \
2805 "Options:\n" \
2806 "\t-c\tprint the byte counts\n" \
2807 "\t-l\tprint the newline counts\n" \
2808 "\t-L\tprint the length of the longest line\n" \
2809 "\t-w\tprint the word counts"
2810#define wc_example_usage \
2811 "$ wc /etc/passwd\n" \
2812 " 31 46 1365 /etc/passwd\n"
2813
2814#define wget_trivial_usage \
2815 "[-c|--continue] [-q|--quiet] [-O|--output-document file]\n" \
2816 "\t\t[--header 'header: value'] [-Y|--proxy on/off] [-P DIR] url"
2817#define wget_full_usage \
2818 "wget retrieves files via HTTP or FTP\n\n" \
2819 "Options:\n" \
2820 "\t-c\tcontinue retrieval of aborted transfers\n" \
2821 "\t-q\tquiet mode - do not print\n" \
2822 "\t-P\tSet directory prefix to DIR\n" \
2823 "\t-O\tsave to filename ('-' for stdout)\n" \
2824 "\t-Y\tuse proxy ('on' or 'off')"
2825
2826#define which_trivial_usage \
2827 "[COMMAND ...]"
2828#define which_full_usage \
2829 "Locates a COMMAND."
2830#define which_example_usage \
2831 "$ which login\n" \
2832 "/bin/login\n"
2833
2834#define who_trivial_usage \
2835 " "
2836#define who_full_usage \
2837 "Prints the current user names and related information"
2838
2839#define whoami_trivial_usage \
2840 ""
2841#define whoami_full_usage \
2842 "Prints the user name associated with the current effective user id."
2843
2844#ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
2845#define USAGE_XARGS_CONFIRMATION(a) a
2846#else
2847#define USAGE_XARGS_CONFIRMATION(a)
2848#endif
2849#ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
2850#define USAGE_XARGS_TERMOPT(a) a
2851#else
2852#define USAGE_XARGS_TERMOPT(a)
2853#endif
2854#ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
2855#define USAGE_XARGS_ZERO_TERM(a) a
2856#else
2857#define USAGE_XARGS_ZERO_TERM(a)
2858#endif
2859
2860
2861#define xargs_trivial_usage \
2862 "[COMMAND] [OPTIONS] [ARGS...]"
2863#define xargs_full_usage \
2864 "Executes COMMAND on every item given by standard input.\n\n" \
2865 "Options:\n" \
2866 USAGE_XARGS_CONFIRMATION("\t-p\tPrompt the user about whether to run each command\n") \
2867 "\t-r\tDo not run command for empty readed lines\n" \
2868 USAGE_XARGS_TERMOPT("\t-x\tExit if the size is exceeded\n") \
2869 USAGE_XARGS_ZERO_TERM("\t-0\tInput filenames are terminated by a null character\n") \
2870 "\t-t\tPrint the command line on stderr before executing it."
2871#define xargs_example_usage \
2872 "$ ls | xargs gzip\n" \
2873 "$ find . -name '*.c' -print | xargs rm\n"
2874
2875#define yes_trivial_usage \
2876 "[OPTION]... [STRING]..."
2877#define yes_full_usage \
2878 "Repeatedly outputs a line with all specified STRING(s), or 'y'."
2879
2880#define zcat_trivial_usage \
2881 "FILE"
2882#define zcat_full_usage \
2883 "Uncompress to stdout."
2884
2885#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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Init Utilities"
7
8config CONFIG_INIT
9 bool "init"
10 default n
11 help
12 init is the first program run when the system boots.
13
14config CONFIG_FEATURE_USE_INITTAB
15 bool " Support reading an inittab file?"
16 default y
17 depends on CONFIG_INIT
18 help
19 Allow init to read an inittab file when the system boot.
20
21config CONFIG_FEATURE_INITRD
22 bool " Support running init from within an initrd?"
23 default y
24 depends on CONFIG_INIT
25 help
26 Allow init to be called from an initrd as linuxrc.
27
28config CONFIG_FEATURE_INIT_COREDUMPS
29 bool " Support dumping core for child processes (debugging only)?"
30 default n
31 depends on CONFIG_INIT
32 help
33 If this option is enabled and the file /.init_enable_core
34 exists, then init will call setrlimit() to allow unlimited
35 core file sizes. If this option is disabled, processes
36 will not generate any core files.
37
38config CONFIG_FEATURE_EXTRA_QUIET
39 bool " Should init be _extra_ quiet on boot?"
40 default y
41 depends on CONFIG_INIT
42 help
43 Prevent init from logging some messages to the console
44 during boot.
45
46config CONFIG_HALT
47 bool "halt"
48 default y
49 help
50 Stop all processes and halt the system.
51
52config CONFIG_POWEROFF
53 bool "poweroff"
54 default y
55 help
56 Stop all processes and (try to) power off the system.
57
58config CONFIG_REBOOT
59 bool "reboot"
60 default y
61 help
62 Stop all processes and reboot the system.
63
64config CONFIG_MESG
65 bool "mesg"
66 default y
67 help
68 Mesg controls access to your terminal by others. It is typically
69 used to allow or disallow other users to write to your terminal
70
71endmenu
72
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/init
23INIT_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include $(srcdir)/Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20INIT_AR:=init.a
21ifndef $(INIT_DIR)
22INIT_DIR:=$(top_builddir)/init/
23endif
24srcdir=$(top_srcdir)/init
25
26INIT-y:=
27INIT-$(CONFIG_HALT) += halt.o
28INIT-$(CONFIG_INIT) += init.o
29INIT-$(CONFIG_MESG) += mesg.o
30INIT-$(CONFIG_POWEROFF) += poweroff.o
31INIT-$(CONFIG_REBOOT) += reboot.o
32
33ifeq ($(CONFIG_HALT), y)
34CONFIG_INIT_SHARED=y
35else
36ifeq ($(CONFIG_INIT), y)
37CONFIG_INIT_SHARED=y
38else
39ifeq ($(CONFIG_POWEROFF), y)
40CONFIG_INIT_SHARED=y
41else
42ifeq ($(CONFIG_REBOOT), y)
43CONFIG_INIT_SHARED=y
44else
45CONFIG_INIT_SHARED=n
46endif
47endif
48endif
49endif
50
51ifeq ($(CONFIG_INIT_SHARED), y)
52INIT-$(CONFIG_INIT_SHARED) += init_shared.o
53endif
54
55libraries-y+=$(INIT_DIR)$(INIT_AR)
56
57$(INIT_DIR)$(INIT_AR): $(patsubst %,$(INIT_DIR)%, $(INIT-y))
58 $(AR) -ro $@ $(patsubst %,$(INIT_DIR)%, $(INIT-y))
59
60$(INIT_DIR)%.o: $(srcdir)/%.c
61 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
62
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini halt implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <signal.h>
24#include <stdlib.h>
25#include <unistd.h>
26#include <getopt.h>
27#include <sys/reboot.h>
28#include "busybox.h"
29#include "init_shared.h"
30
31
32extern int halt_main(int argc, char **argv)
33{
34 char *delay; /* delay in seconds before rebooting */
35
36 if(bb_getopt_ulflags(argc, argv, "d:", &delay)) {
37 sleep(atoi(delay));
38 }
39
40#ifndef CONFIG_INIT
41#ifndef RB_HALT_SYSTEM
42#define RB_HALT_SYSTEM 0xcdef0123
43#endif
44 return(bb_shutdown_system(RB_HALT_SYSTEM));
45#else
46 return kill_init(SIGUSR1);
47#endif
48}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini init implementation for busybox
4 *
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 * Adjusted by so many folks, it's impossible to keep track.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
24
25/* Turn this on to disable all the dangerous
26 rebooting stuff when debugging.
27#define DEBUG_INIT
28*/
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <errno.h>
33#include <paths.h>
34#include <signal.h>
35#include <stdarg.h>
36#include <string.h>
37#include <termios.h>
38#include <unistd.h>
39#include <limits.h>
40#include <sys/fcntl.h>
41#include <sys/ioctl.h>
42#include <sys/mount.h>
43#include <sys/types.h>
44#include <sys/wait.h>
45#include <sys/reboot.h>
46#include "busybox.h"
47
48#include "init_shared.h"
49
50
51#ifdef CONFIG_SYSLOGD
52# include <sys/syslog.h>
53#endif
54
55
56#define INIT_BUFFS_SIZE 256
57
58/* From <linux/vt.h> */
59struct vt_stat {
60 unsigned short v_active; /* active vt */
61 unsigned short v_signal; /* signal to send */
62 unsigned short v_state; /* vt bitmask */
63};
64static const int VT_GETSTATE = 0x5603; /* get global vt state info */
65
66/* From <linux/serial.h> */
67struct serial_struct {
68 int type;
69 int line;
70 unsigned int port;
71 int irq;
72 int flags;
73 int xmit_fifo_size;
74 int custom_divisor;
75 int baud_base;
76 unsigned short close_delay;
77 char io_type;
78 char reserved_char[1];
79 int hub6;
80 unsigned short closing_wait; /* time to wait before closing */
81 unsigned short closing_wait2; /* no longer used... */
82 unsigned char *iomem_base;
83 unsigned short iomem_reg_shift;
84 unsigned int port_high;
85 unsigned long iomap_base; /* cookie passed into ioremap */
86 int reserved[1];
87};
88
89
90#ifndef _PATH_STDPATH
91#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin"
92#endif
93
94#if defined CONFIG_FEATURE_INIT_COREDUMPS
95/*
96 * When a file named CORE_ENABLE_FLAG_FILE exists, setrlimit is called
97 * before processes are spawned to set core file size as unlimited.
98 * This is for debugging only. Don't use this is production, unless
99 * you want core dumps lying about....
100 */
101#define CORE_ENABLE_FLAG_FILE "/.init_enable_core"
102#include <sys/resource.h>
103#include <sys/time.h>
104#endif
105
106#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
107
108#define INITTAB "/etc/inittab" /* inittab file location */
109#ifndef INIT_SCRIPT
110#define INIT_SCRIPT "/etc/init.d/rcS" /* Default sysinit script. */
111#endif
112
113#define MAXENV 16 /* Number of env. vars */
114
115#define CONSOLE_BUFF_SIZE 32
116
117/* Allowed init action types */
118#define SYSINIT 0x001
119#define RESPAWN 0x002
120#define ASKFIRST 0x004
121#define WAIT 0x008
122#define ONCE 0x010
123#define CTRLALTDEL 0x020
124#define SHUTDOWN 0x040
125#define RESTART 0x080
126
127/* A mapping between "inittab" action name strings and action type codes. */
128struct init_action_type {
129 const char *name;
130 int action;
131};
132
133static const struct init_action_type actions[] = {
134 {"sysinit", SYSINIT},
135 {"respawn", RESPAWN},
136 {"askfirst", ASKFIRST},
137 {"wait", WAIT},
138 {"once", ONCE},
139 {"ctrlaltdel", CTRLALTDEL},
140 {"shutdown", SHUTDOWN},
141 {"restart", RESTART},
142 {0, 0}
143};
144
145/* Set up a linked list of init_actions, to be read from inittab */
146struct init_action {
147 pid_t pid;
148 char command[INIT_BUFFS_SIZE];
149 char terminal[CONSOLE_BUFF_SIZE];
150 struct init_action *next;
151 int action;
152};
153
154/* Static variables */
155static struct init_action *init_action_list = NULL;
156static char console[CONSOLE_BUFF_SIZE] = _PATH_CONSOLE;
157
158#ifndef CONFIG_SYSLOGD
159static char *log = VC_5;
160#endif
161static sig_atomic_t got_cont = 0;
162static const int LOG = 0x1;
163static const int CONSOLE = 0x2;
164
165#if defined CONFIG_FEATURE_EXTRA_QUIET
166static const int MAYBE_CONSOLE = 0x0;
167#else
168#define MAYBE_CONSOLE CONSOLE
169#endif
170#ifndef RB_HALT_SYSTEM
171static const int RB_HALT_SYSTEM = 0xcdef0123;
172static const int RB_ENABLE_CAD = 0x89abcdef;
173static const int RB_DISABLE_CAD = 0;
174
175#define RB_POWER_OFF 0x4321fedc
176static const int RB_AUTOBOOT = 0x01234567;
177#endif
178
179static const char * const environment[] = {
180 "HOME=/",
181 "PATH=" _PATH_STDPATH,
182 "SHELL=/bin/sh",
183 "USER=root",
184 NULL
185};
186
187/* Function prototypes */
188static void delete_init_action(struct init_action *a);
189static int waitfor(const struct init_action *a);
190static void halt_signal(int sig);
191
192
193static void loop_forever(void)
194{
195 while (1)
196 sleep(1);
197}
198
199/* Print a message to the specified device.
200 * Device may be bitwise-or'd from LOG | CONSOLE */
201#ifndef DEBUG_INIT
202static inline void messageD(int device, const char *fmt, ...)
203{
204}
205#else
206#define messageD message
207#endif
208static void message(int device, const char *fmt, ...)
209 __attribute__ ((format(printf, 2, 3)));
210static void message(int device, const char *fmt, ...)
211{
212 va_list arguments;
213 int l;
214 char msg[1024];
215#ifndef CONFIG_SYSLOGD
216 static int log_fd = -1;
217#endif
218
219 msg[0] = '\r';
220 va_start(arguments, fmt);
221 l = vsnprintf(msg + 1, sizeof(msg) - 2, fmt, arguments) + 1;
222 va_end(arguments);
223
224#ifdef CONFIG_SYSLOGD
225 /* Log the message to syslogd */
226 if (device & LOG) {
227 /* don`t out "\r\n" */
228 openlog(bb_applet_name, 0, LOG_DAEMON);
229 syslog(LOG_INFO, "%s", msg);
230 closelog();
231 }
232
233 msg[l++] = '\n';
234 msg[l] = 0;
235#else
236
237 msg[l++] = '\n';
238 msg[l] = 0;
239 /* Take full control of the log tty, and never close it.
240 * It's mine, all mine! Muhahahaha! */
241 if (log_fd < 0) {
242 if ((log_fd = device_open(log, O_RDWR | O_NDELAY | O_NOCTTY)) < 0) {
243 log_fd = -2;
244 bb_error_msg("Bummer, can't write to log on %s!", log);
245 device = CONSOLE;
246 } else {
247 fcntl(log_fd, F_SETFD, FD_CLOEXEC);
248 }
249 }
250 if ((device & LOG) && (log_fd >= 0)) {
251 bb_full_write(log_fd, msg, l);
252 }
253#endif
254
255 if (device & CONSOLE) {
256 int fd = device_open(_PATH_CONSOLE,
257 O_WRONLY | O_NOCTTY | O_NDELAY);
258 /* Always send console messages to /dev/console so people will see them. */
259 if (fd >= 0) {
260 bb_full_write(fd, msg, l);
261 close(fd);
262#ifdef DEBUG_INIT
263 /* all descriptors may be closed */
264 } else {
265 bb_error_msg("Bummer, can't print: ");
266 va_start(arguments, fmt);
267 vfprintf(stderr, fmt, arguments);
268 va_end(arguments);
269#endif
270 }
271 }
272}
273
274/* Set terminal settings to reasonable defaults */
275static void set_term(int fd)
276{
277 struct termios tty;
278
279 tcgetattr(fd, &tty);
280
281 /* set control chars */
282 tty.c_cc[VINTR] = 3; /* C-c */
283 tty.c_cc[VQUIT] = 28; /* C-\ */
284 tty.c_cc[VERASE] = 127; /* C-? */
285 tty.c_cc[VKILL] = 21; /* C-u */
286 tty.c_cc[VEOF] = 4; /* C-d */
287 tty.c_cc[VSTART] = 17; /* C-q */
288 tty.c_cc[VSTOP] = 19; /* C-s */
289 tty.c_cc[VSUSP] = 26; /* C-z */
290
291 /* use line dicipline 0 */
292 tty.c_line = 0;
293
294 /* Make it be sane */
295 tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
296 tty.c_cflag |= CREAD | HUPCL | CLOCAL;
297
298
299 /* input modes */
300 tty.c_iflag = ICRNL | IXON | IXOFF;
301
302 /* output modes */
303 tty.c_oflag = OPOST | ONLCR;
304
305 /* local modes */
306 tty.c_lflag =
307 ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
308
309 tcsetattr(fd, TCSANOW, &tty);
310}
311
312/* How much memory does this machine have?
313 Units are kBytes to avoid overflow on 4GB machines */
314static unsigned int check_free_memory(void)
315{
316 struct sysinfo info;
317 unsigned int result, u, s = 10;
318
319 if (sysinfo(&info) != 0) {
320 bb_perror_msg("Error checking free memory");
321 return -1;
322 }
323
324 /* Kernels 2.0.x and 2.2.x return info.mem_unit==0 with values in bytes.
325 * Kernels 2.4.0 return info.mem_unit in bytes. */
326 u = info.mem_unit;
327 if (u == 0)
328 u = 1;
329 while ((u & 1) == 0 && s > 0) {
330 u >>= 1;
331 s--;
332 }
333 result = (info.totalram >> s) + (info.totalswap >> s);
334 if (((unsigned long long)result * (unsigned long long)u) > UINT_MAX) {
335 return(UINT_MAX);
336 } else {
337 return(result * u);
338 }
339}
340
341static void console_init(void)
342{
343 int fd;
344 int tried = 0;
345 struct vt_stat vt;
346 struct serial_struct sr;
347 char *s;
348
349 if ((s = getenv("CONSOLE")) != NULL || (s = getenv("console")) != NULL) {
350 safe_strncpy(console, s, sizeof(console));
351#if #cpu(sparc)
352 /* sparc kernel supports console=tty[ab] parameter which is also
353 * passed to init, so catch it here */
354 /* remap tty[ab] to /dev/ttyS[01] */
355 if (strcmp(s, "ttya") == 0)
356 safe_strncpy(console, SC_0, sizeof(console));
357 else if (strcmp(s, "ttyb") == 0)
358 safe_strncpy(console, SC_1, sizeof(console));
359#endif
360 } else {
361 /* 2.2 kernels: identify the real console backend and try to use it */
362 if (ioctl(0, TIOCGSERIAL, &sr) == 0) {
363 /* this is a serial console */
364 snprintf(console, sizeof(console) - 1, SC_FORMAT, sr.line);
365 } else if (ioctl(0, VT_GETSTATE, &vt) == 0) {
366 /* this is linux virtual tty */
367 snprintf(console, sizeof(console) - 1, VC_FORMAT, vt.v_active);
368 } else {
369 safe_strncpy(console, _PATH_CONSOLE, sizeof(console));
370 tried++;
371 }
372 }
373
374 while ((fd = open(console, O_RDONLY | O_NONBLOCK)) < 0 && tried < 2) {
375 /* Can't open selected console -- try
376 logical system console and VT_MASTER */
377 safe_strncpy(console, (tried == 0 ? _PATH_CONSOLE : CURRENT_VC),
378 sizeof(console));
379 tried++;
380 }
381 if (fd < 0) {
382 /* Perhaps we should panic here? */
383#ifndef CONFIG_SYSLOGD
384 log =
385#endif
386 safe_strncpy(console, "/dev/null", sizeof(console));
387 } else {
388 s = getenv("TERM");
389 /* check for serial console */
390 if (ioctl(fd, TIOCGSERIAL, &sr) == 0) {
391 /* Force the TERM setting to vt102 for serial console --
392 * if TERM is set to linux (the default) */
393 if (s == NULL || strcmp(s, "linux") == 0)
394 putenv("TERM=vt102");
395#ifndef CONFIG_SYSLOGD
396 log = console;
397#endif
398 } else {
399 if (s == NULL)
400 putenv("TERM=linux");
401 }
402 close(fd);
403 }
404 messageD(LOG, "console=%s", console);
405}
406
407static void fixup_argv(int argc, char **argv, char *new_argv0)
408{
409 int len;
410
411 /* Fix up argv[0] to be certain we claim to be init */
412 len = strlen(argv[0]);
413 memset(argv[0], 0, len);
414 safe_strncpy(argv[0], new_argv0, len + 1);
415
416 /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
417 len = 1;
418 while (argc > len) {
419 memset(argv[len], 0, strlen(argv[len]));
420 len++;
421 }
422}
423
424static pid_t run(const struct init_action *a)
425{
426 struct stat sb;
427 int i, junk;
428 pid_t pid, pgrp, tmp_pid;
429 char *s, *tmpCmd, *cmd[INIT_BUFFS_SIZE], *cmdpath;
430 char buf[INIT_BUFFS_SIZE + 6]; /* INIT_BUFFS_SIZE+strlen("exec ")+1 */
431 sigset_t nmask, omask;
432 static const char press_enter[] =
433#ifdef CUSTOMIZED_BANNER
434#include CUSTOMIZED_BANNER
435#endif
436 "\nPlease press Enter to activate this console. ";
437
438 /* Block sigchild while forking. */
439 sigemptyset(&nmask);
440 sigaddset(&nmask, SIGCHLD);
441 sigprocmask(SIG_BLOCK, &nmask, &omask);
442
443 if ((pid = fork()) == 0) {
444 /* Clean up */
445 close(0);
446 close(1);
447 close(2);
448 sigprocmask(SIG_SETMASK, &omask, NULL);
449
450 /* Reset signal handlers that were set by the parent process */
451 signal(SIGUSR1, SIG_DFL);
452 signal(SIGUSR2, SIG_DFL);
453 signal(SIGINT, SIG_DFL);
454 signal(SIGTERM, SIG_DFL);
455 signal(SIGHUP, SIG_DFL);
456 signal(SIGCONT, SIG_DFL);
457 signal(SIGSTOP, SIG_DFL);
458 signal(SIGTSTP, SIG_DFL);
459
460 /* Create a new session and make ourself the process
461 * group leader */
462 setsid();
463
464 /* Open the new terminal device */
465 if ((device_open(a->terminal, O_RDWR)) < 0) {
466 if (stat(a->terminal, &sb) != 0) {
467 message(LOG | CONSOLE, "device '%s' does not exist.",
468 a->terminal);
469 _exit(1);
470 }
471 message(LOG | CONSOLE, "Bummer, can't open %s", a->terminal);
472 _exit(1);
473 }
474
475 /* Make sure the terminal will act fairly normal for us */
476 set_term(0);
477 /* Setup stdout, stderr for the new process so
478 * they point to the supplied terminal */
479 dup(0);
480 dup(0);
481
482 /* If the init Action requires us to wait, then force the
483 * supplied terminal to be the controlling tty. */
484 if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
485
486 /* Now fork off another process to just hang around */
487 if ((pid = fork()) < 0) {
488 message(LOG | CONSOLE, "Can't fork!");
489 _exit(1);
490 }
491
492 if (pid > 0) {
493
494 /* We are the parent -- wait till the child is done */
495 signal(SIGINT, SIG_IGN);
496 signal(SIGTSTP, SIG_IGN);
497 signal(SIGQUIT, SIG_IGN);
498 signal(SIGCHLD, SIG_DFL);
499
500 /* Wait for child to exit */
501 while ((tmp_pid = waitpid(pid, &junk, 0)) != pid) {
502 if (tmp_pid == -1 && errno == ECHILD) {
503 break;
504 }
505 /* FIXME handle other errors */
506 }
507
508 /* See if stealing the controlling tty back is necessary */
509 pgrp = tcgetpgrp(0);
510 if (pgrp != getpid())
511 _exit(0);
512
513 /* Use a temporary process to steal the controlling tty. */
514 if ((pid = fork()) < 0) {
515 message(LOG | CONSOLE, "Can't fork!");
516 _exit(1);
517 }
518 if (pid == 0) {
519 setsid();
520 ioctl(0, TIOCSCTTY, 1);
521 _exit(0);
522 }
523 while ((tmp_pid = waitpid(pid, &junk, 0)) != pid) {
524 if (tmp_pid < 0 && errno == ECHILD)
525 break;
526 }
527 _exit(0);
528 }
529
530 /* Now fall though to actually execute things */
531 }
532
533 /* See if any special /bin/sh requiring characters are present */
534 if (strpbrk(a->command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) {
535 cmd[0] = (char *)DEFAULT_SHELL;
536 cmd[1] = "-c";
537 cmd[2] = strcat(strcpy(buf, "exec "), a->command);
538 cmd[3] = NULL;
539 } else {
540 /* Convert command (char*) into cmd (char**, one word per string) */
541 strcpy(buf, a->command);
542 s = buf;
543 for (tmpCmd = buf, i = 0; (tmpCmd = strsep(&s, " \t")) != NULL;) {
544 if (*tmpCmd != '\0') {
545 cmd[i] = tmpCmd;
546 i++;
547 }
548 }
549 cmd[i] = NULL;
550 }
551
552 cmdpath = cmd[0];
553
554 /*
555 Interactive shells want to see a dash in argv[0]. This
556 typically is handled by login, argv will be setup this
557 way if a dash appears at the front of the command path
558 (like "-/bin/sh").
559 */
560
561 if (*cmdpath == '-') {
562
563 /* skip over the dash */
564 ++cmdpath;
565
566 /* find the last component in the command pathname */
567 s = bb_get_last_path_component(cmdpath);
568
569 /* make a new argv[0] */
570 if ((cmd[0] = malloc(strlen(s) + 2)) == NULL) {
571 message(LOG | CONSOLE, bb_msg_memory_exhausted);
572 cmd[0] = cmdpath;
573 } else {
574 cmd[0][0] = '-';
575 strcpy(cmd[0] + 1, s);
576 }
577 }
578
579#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
580 if (a->action & ASKFIRST) {
581 char c;
582 /*
583 * Save memory by not exec-ing anything large (like a shell)
584 * before the user wants it. This is critical if swap is not
585 * enabled and the system has low memory. Generally this will
586 * be run on the second virtual console, and the first will
587 * be allowed to start a shell or whatever an init script
588 * specifies.
589 */
590 messageD(LOG, "Waiting for enter to start '%s'"
591 "(pid %d, terminal %s)\n",
592 cmdpath, getpid(), a->terminal);
593 bb_full_write(1, press_enter, sizeof(press_enter) - 1);
594 while(read(0, &c, 1) == 1 && c != '\n')
595 ;
596 }
597#endif
598
599 /* Log the process name and args */
600 message(LOG, "Starting pid %d, console %s: '%s'",
601 getpid(), a->terminal, cmdpath);
602
603#if defined CONFIG_FEATURE_INIT_COREDUMPS
604 if (stat(CORE_ENABLE_FLAG_FILE, &sb) == 0) {
605 struct rlimit limit;
606
607 limit.rlim_cur = RLIM_INFINITY;
608 limit.rlim_max = RLIM_INFINITY;
609 setrlimit(RLIMIT_CORE, &limit);
610 }
611#endif
612
613 /* Now run it. The new program will take over this PID,
614 * so nothing further in init.c should be run. */
615 execv(cmdpath, cmd);
616
617 /* We're still here? Some error happened. */
618 message(LOG | CONSOLE, "Bummer, could not run '%s': %m", cmdpath);
619 _exit(-1);
620 }
621 sigprocmask(SIG_SETMASK, &omask, NULL);
622 return pid;
623}
624
625static int waitfor(const struct init_action *a)
626{
627 int pid;
628 int status, wpid;
629
630 pid = run(a);
631 while (1) {
632 wpid = waitpid(pid,&status,0);
633 if (wpid == pid)
634 break;
635 if (wpid == -1 && errno == ECHILD) {
636 /* we missed its termination */
637 break;
638 }
639 /* FIXME other errors should maybe trigger an error, but allow
640 * the program to continue */
641 }
642 return wpid;
643}
644
645/* Run all commands of a particular type */
646static void run_actions(int action)
647{
648 struct init_action *a, *tmp;
649
650 for (a = init_action_list; a; a = tmp) {
651 tmp = a->next;
652 if (a->action == action) {
653 if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
654 waitfor(a);
655 delete_init_action(a);
656 } else if (a->action & ONCE) {
657 run(a);
658 delete_init_action(a);
659 } else if (a->action & (RESPAWN | ASKFIRST)) {
660 /* Only run stuff with pid==0. If they have
661 * a pid, that means it is still running */
662 if (a->pid == 0) {
663 a->pid = run(a);
664 }
665 }
666 }
667 }
668}
669
670#ifndef DEBUG_INIT
671static void init_reboot(unsigned long magic)
672{
673 pid_t pid;
674 /* We have to fork here, since the kernel calls do_exit(0) in
675 * linux/kernel/sys.c, which can cause the machine to panic when
676 * the init process is killed.... */
677 if ((pid = fork()) == 0) {
678 reboot(magic);
679 _exit(0);
680 }
681 waitpid (pid, NULL, 0);
682}
683
684static void shutdown_system(void)
685{
686 sigset_t block_signals;
687
688 /* run everything to be run at "shutdown". This is done _prior_
689 * to killing everything, in case people wish to use scripts to
690 * shut things down gracefully... */
691 run_actions(SHUTDOWN);
692
693 /* first disable all our signals */
694 sigemptyset(&block_signals);
695 sigaddset(&block_signals, SIGHUP);
696 sigaddset(&block_signals, SIGCHLD);
697 sigaddset(&block_signals, SIGUSR1);
698 sigaddset(&block_signals, SIGUSR2);
699 sigaddset(&block_signals, SIGINT);
700 sigaddset(&block_signals, SIGTERM);
701 sigaddset(&block_signals, SIGCONT);
702 sigaddset(&block_signals, SIGSTOP);
703 sigaddset(&block_signals, SIGTSTP);
704 sigprocmask(SIG_BLOCK, &block_signals, NULL);
705
706 /* Allow Ctrl-Alt-Del to reboot system. */
707 init_reboot(RB_ENABLE_CAD);
708
709 message(CONSOLE | LOG, "The system is going down NOW !!");
710 sync();
711
712 /* Send signals to every process _except_ pid 1 */
713 message(CONSOLE | LOG, "Sending SIGTERM to all processes.");
714 kill(-1, SIGTERM);
715 sleep(1);
716 sync();
717
718 message(CONSOLE | LOG, "Sending SIGKILL to all processes.");
719 kill(-1, SIGKILL);
720 sleep(1);
721
722 sync();
723}
724
725static void exec_signal(int sig)
726{
727 struct init_action *a, *tmp;
728 sigset_t unblock_signals;
729
730 for (a = init_action_list; a; a = tmp) {
731 tmp = a->next;
732 if (a->action & RESTART) {
733 struct stat sb;
734
735 shutdown_system();
736
737 /* unblock all signals, blocked in shutdown_system() */
738 sigemptyset(&unblock_signals);
739 sigaddset(&unblock_signals, SIGHUP);
740 sigaddset(&unblock_signals, SIGCHLD);
741 sigaddset(&unblock_signals, SIGUSR1);
742 sigaddset(&unblock_signals, SIGUSR2);
743 sigaddset(&unblock_signals, SIGINT);
744 sigaddset(&unblock_signals, SIGTERM);
745 sigaddset(&unblock_signals, SIGCONT);
746 sigaddset(&unblock_signals, SIGSTOP);
747 sigaddset(&unblock_signals, SIGTSTP);
748 sigprocmask(SIG_UNBLOCK, &unblock_signals, NULL);
749
750 /* Close whatever files are open. */
751 close(0);
752 close(1);
753 close(2);
754
755 /* Open the new terminal device */
756 if ((device_open(a->terminal, O_RDWR)) < 0) {
757 if (stat(a->terminal, &sb) != 0) {
758 message(LOG | CONSOLE, "device '%s' does not exist.", a->terminal);
759 } else {
760 message(LOG | CONSOLE, "Bummer, can't open %s", a->terminal);
761 }
762 halt_signal(SIGUSR1);
763 }
764
765 /* Make sure the terminal will act fairly normal for us */
766 set_term(0);
767 /* Setup stdout, stderr on the supplied terminal */
768 dup(0);
769 dup(0);
770
771 messageD(CONSOLE | LOG, "Trying to re-exec %s", a->command);
772 execl(a->command, a->command, NULL);
773
774 message(CONSOLE | LOG, "exec of '%s' failed: %m",
775 a->command);
776 sync();
777 sleep(2);
778 init_reboot(RB_HALT_SYSTEM);
779 loop_forever();
780 }
781 }
782}
783
784static void halt_signal(int sig)
785{
786 shutdown_system();
787 message(CONSOLE | LOG,
788#if #cpu(s390)
789 /* Seems the s390 console is Wierd(tm). */
790 "The system is halted. You may reboot now."
791#else
792 "The system is halted. Press Reset or turn off power"
793#endif
794 );
795 sync();
796
797 /* allow time for last message to reach serial console */
798 sleep(2);
799
800 if (sig == SIGUSR2)
801 init_reboot(RB_POWER_OFF);
802 else
803 init_reboot(RB_HALT_SYSTEM);
804
805 loop_forever();
806}
807
808static void reboot_signal(int sig)
809{
810 shutdown_system();
811 message(CONSOLE | LOG, "Please stand by while rebooting the system.");
812 sync();
813
814 /* allow time for last message to reach serial console */
815 sleep(2);
816
817 init_reboot(RB_AUTOBOOT);
818
819 loop_forever();
820}
821
822static void ctrlaltdel_signal(int sig)
823{
824 run_actions(CTRLALTDEL);
825}
826
827/* The SIGSTOP & SIGTSTP handler */
828static void stop_handler(int sig)
829{
830 int saved_errno = errno;
831
832 got_cont = 0;
833 while (!got_cont)
834 pause();
835 got_cont = 0;
836 errno = saved_errno;
837}
838
839/* The SIGCONT handler */
840static void cont_handler(int sig)
841{
842 got_cont = 1;
843}
844
845#endif /* ! DEBUG_INIT */
846
847static void new_init_action(int action, const char *command, const char *cons)
848{
849 struct init_action *new_action, *a, *last;
850
851 if (*cons == '\0')
852 cons = console;
853
854 /* do not run entries if console device is not available */
855 if (access(cons, R_OK | W_OK))
856 return;
857 if (strcmp(cons, "/dev/null") == 0 && (action & ASKFIRST))
858 return;
859
860 new_action = calloc((size_t) (1), sizeof(struct init_action));
861 if (!new_action) {
862 message(LOG | CONSOLE, "Memory allocation failure");
863 loop_forever();
864 }
865
866 /* Append to the end of the list */
867 for (a = last = init_action_list; a; a = a->next) {
868 /* don't enter action if it's already in the list,
869 * but do overwrite existing actions */
870 if ((strcmp(a->command, command) == 0) &&
871 (strcmp(a->terminal, cons) ==0)) {
872 a->action = action;
873 free(new_action);
874 return;
875 }
876 last = a;
877 }
878 if (last) {
879 last->next = new_action;
880 } else {
881 init_action_list = new_action;
882 }
883 strcpy(new_action->command, command);
884 new_action->action = action;
885 strcpy(new_action->terminal, cons);
886#if 0 /* calloc zeroed always */
887 new_action->pid = 0;
888#endif
889 messageD(LOG|CONSOLE, "command='%s' action='%d' terminal='%s'\n",
890 new_action->command, new_action->action, new_action->terminal);
891}
892
893static void delete_init_action(struct init_action *action)
894{
895 struct init_action *a, *b = NULL;
896
897 for (a = init_action_list; a; b = a, a = a->next) {
898 if (a == action) {
899 if (b == NULL) {
900 init_action_list = a->next;
901 } else {
902 b->next = a->next;
903 }
904 free(a);
905 break;
906 }
907 }
908}
909
910/* Make sure there is enough memory to do something useful. *
911 * Calls "swapon -a" if needed so be sure /etc/fstab is present... */
912static void check_memory(void)
913{
914 struct stat statBuf;
915
916 if (check_free_memory() > 1000)
917 return;
918
919#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
920 if (stat("/etc/fstab", &statBuf) == 0) {
921 /* swapon -a requires /proc typically */
922 new_init_action(SYSINIT, "/bin/mount -t proc proc /proc", "");
923 /* Try to turn on swap */
924 new_init_action(SYSINIT, "/sbin/swapon -a", "");
925 run_actions(SYSINIT); /* wait and removing */
926 if (check_free_memory() < 1000)
927 goto goodnight;
928 } else
929 goto goodnight;
930 return;
931#endif
932
933 goodnight:
934 message(CONSOLE, "Sorry, your computer does not have enough memory.");
935 loop_forever();
936}
937
938/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
939 * then parse_inittab() simply adds in some default
940 * actions(i.e., runs INIT_SCRIPT and then starts a pair
941 * of "askfirst" shells). If CONFIG_FEATURE_USE_INITTAB
942 * _is_ defined, but /etc/inittab is missing, this
943 * results in the same set of default behaviors.
944 */
945static void parse_inittab(void)
946{
947#ifdef CONFIG_FEATURE_USE_INITTAB
948 FILE *file;
949 char buf[INIT_BUFFS_SIZE], lineAsRead[INIT_BUFFS_SIZE];
950 char tmpConsole[CONSOLE_BUFF_SIZE];
951 char *id, *runlev, *action, *command, *eol;
952 const struct init_action_type *a = actions;
953
954
955 file = fopen(INITTAB, "r");
956 if (file == NULL) {
957 /* No inittab file -- set up some default behavior */
958#endif
959 /* Reboot on Ctrl-Alt-Del */
960 new_init_action(CTRLALTDEL, "/sbin/reboot", "");
961 /* Umount all filesystems on halt/reboot */
962 new_init_action(SHUTDOWN, "/bin/umount -a -r", "");
963#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
964 /* Swapoff on halt/reboot */
965 new_init_action(SHUTDOWN, "/sbin/swapoff -a", "");
966#endif
967 /* Prepare to restart init when a HUP is received */
968 new_init_action(RESTART, "/sbin/init", "");
969 /* Askfirst shell on tty1-4 */
970 new_init_action(ASKFIRST, bb_default_login_shell, "");
971 new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
972 new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
973 new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
974 /* sysinit */
975 new_init_action(SYSINIT, INIT_SCRIPT, "");
976
977 return;
978#ifdef CONFIG_FEATURE_USE_INITTAB
979 }
980
981 while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) {
982 /* Skip leading spaces */
983 for (id = buf; *id == ' ' || *id == '\t'; id++);
984
985 /* Skip the line if it's a comment */
986 if (*id == '#' || *id == '\n')
987 continue;
988
989 /* Trim the trailing \n */
990 eol = strrchr(id, '\n');
991 if (eol != NULL)
992 *eol = '\0';
993
994 /* Keep a copy around for posterity's sake (and error msgs) */
995 strcpy(lineAsRead, buf);
996
997 /* Separate the ID field from the runlevels */
998 runlev = strchr(id, ':');
999 if (runlev == NULL || *(runlev + 1) == '\0') {
1000 message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead);
1001 continue;
1002 } else {
1003 *runlev = '\0';
1004 ++runlev;
1005 }
1006
1007 /* Separate the runlevels from the action */
1008 action = strchr(runlev, ':');
1009 if (action == NULL || *(action + 1) == '\0') {
1010 message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead);
1011 continue;
1012 } else {
1013 *action = '\0';
1014 ++action;
1015 }
1016
1017 /* Separate the action from the command */
1018 command = strchr(action, ':');
1019 if (command == NULL || *(command + 1) == '\0') {
1020 message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead);
1021 continue;
1022 } else {
1023 *command = '\0';
1024 ++command;
1025 }
1026
1027 /* Ok, now process it */
1028 for (a = actions; a->name != 0; a++) {
1029 if (strcmp(a->name, action) == 0) {
1030 if (*id != '\0') {
1031 if(strncmp(id, "/dev/", 5) == 0)
1032 id += 5;
1033 strcpy(tmpConsole, "/dev/");
1034 safe_strncpy(tmpConsole + 5, id,
1035 CONSOLE_BUFF_SIZE - 5);
1036 id = tmpConsole;
1037 }
1038 new_init_action(a->action, command, id);
1039 break;
1040 }
1041 }
1042 if (a->name == 0) {
1043 /* Choke on an unknown action */
1044 message(LOG | CONSOLE, "Bad inittab entry: %s", lineAsRead);
1045 }
1046 }
1047 fclose(file);
1048 return;
1049#endif /* CONFIG_FEATURE_USE_INITTAB */
1050}
1051
1052#ifdef CONFIG_FEATURE_USE_INITTAB
1053static void reload_signal(int sig)
1054{
1055 struct init_action *a, *tmp;
1056
1057 message(LOG, "Reloading /etc/inittab");
1058
1059 /* disable old entrys */
1060 for (a = init_action_list; a; a = a->next ) {
1061 a->action = ONCE;
1062 }
1063
1064 parse_inittab();
1065
1066 /* remove unused entrys */
1067 for (a = init_action_list; a; a = tmp) {
1068 tmp = a->next;
1069 if (a->action & (ONCE | SYSINIT | WAIT ) &&
1070 a->pid == 0 ) {
1071 delete_init_action(a);
1072 }
1073 }
1074 run_actions(RESPAWN);
1075 return;
1076}
1077#endif /* CONFIG_FEATURE_USE_INITTAB */
1078
1079extern int init_main(int argc, char **argv)
1080{
1081 struct init_action *a;
1082 pid_t wpid;
1083 int status;
1084
1085 if (argc > 1 && !strcmp(argv[1], "-q")) {
1086 return kill_init(SIGHUP);
1087 }
1088#ifndef DEBUG_INIT
1089 /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */
1090 if (getpid() != 1
1091#ifdef CONFIG_FEATURE_INITRD
1092 && strstr(bb_applet_name, "linuxrc") == NULL
1093#endif
1094 ) {
1095 bb_show_usage();
1096 }
1097 /* Set up sig handlers -- be sure to
1098 * clear all of these in run() */
1099 signal(SIGHUP, exec_signal);
1100 signal(SIGUSR1, halt_signal);
1101 signal(SIGUSR2, halt_signal);
1102 signal(SIGINT, ctrlaltdel_signal);
1103 signal(SIGTERM, reboot_signal);
1104 signal(SIGCONT, cont_handler);
1105 signal(SIGSTOP, stop_handler);
1106 signal(SIGTSTP, stop_handler);
1107
1108 /* Turn off rebooting via CTL-ALT-DEL -- we get a
1109 * SIGINT on CAD so we can shut things down gracefully... */
1110 init_reboot(RB_DISABLE_CAD);
1111#endif
1112
1113 /* Figure out where the default console should be */
1114 console_init();
1115
1116 /* Close whatever files are open, and reset the console. */
1117 close(0);
1118 close(1);
1119 close(2);
1120
1121 if (device_open(console, O_RDWR | O_NOCTTY) == 0) {
1122 set_term(0);
1123 close(0);
1124 }
1125
1126 chdir("/");
1127 setsid();
1128 {
1129 const char * const *e;
1130 /* Make sure environs is set to something sane */
1131 for(e = environment; *e; e++)
1132 putenv((char *) *e);
1133 }
1134 /* Hello world */
1135 message(MAYBE_CONSOLE | LOG, "init started: %s", bb_msg_full_version);
1136
1137 /* Make sure there is enough memory to do something useful. */
1138 check_memory();
1139
1140 /* Check if we are supposed to be in single user mode */
1141 if (argc > 1 && (!strcmp(argv[1], "single") ||
1142 !strcmp(argv[1], "-s") || !strcmp(argv[1], "1"))) {
1143 /* Start a shell on console */
1144 new_init_action(RESPAWN, bb_default_login_shell, "");
1145 } else {
1146 /* Not in single user mode -- see what inittab says */
1147
1148 /* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
1149 * then parse_inittab() simply adds in some default
1150 * actions(i.e., runs INIT_SCRIPT and then starts a pair
1151 * of "askfirst" shells */
1152 parse_inittab();
1153 }
1154
1155 /* Make the command line just say "init" -- thats all, nothing else */
1156 fixup_argv(argc, argv, "init");
1157
1158 /* Now run everything that needs to be run */
1159
1160 /* First run the sysinit command */
1161 run_actions(SYSINIT);
1162
1163 /* Next run anything that wants to block */
1164 run_actions(WAIT);
1165
1166 /* Next run anything to be run only once */
1167 run_actions(ONCE);
1168
1169#ifdef CONFIG_FEATURE_USE_INITTAB
1170 /* Redefine SIGHUP to reread /etc/inittab */
1171 signal(SIGHUP, reload_signal);
1172#else
1173 signal(SIGHUP, SIG_IGN);
1174#endif /* CONFIG_FEATURE_USE_INITTAB */
1175
1176
1177 /* Now run the looping stuff for the rest of forever */
1178 while (1) {
1179 /* run the respawn stuff */
1180 run_actions(RESPAWN);
1181
1182 /* run the askfirst stuff */
1183 run_actions(ASKFIRST);
1184
1185 /* Don't consume all CPU time -- sleep a bit */
1186 sleep(1);
1187
1188 /* Wait for a child process to exit */
1189 wpid = wait(&status);
1190 while (wpid > 0) {
1191 /* Find out who died and clean up their corpse */
1192 for (a = init_action_list; a; a = a->next) {
1193 if (a->pid == wpid) {
1194 /* Set the pid to 0 so that the process gets
1195 * restarted by run_actions() */
1196 a->pid = 0;
1197 message(LOG, "Process '%s' (pid %d) exited. "
1198 "Scheduling it for restart.",
1199 a->command, wpid);
1200 }
1201 }
1202 /* see if anyone else is waiting to be reaped */
1203 wpid = waitpid (-1, &status, WNOHANG);
1204 }
1205 }
1206}
1207
1208/*
1209Local Variables:
1210c-file-style: "linux"
1211c-basic-offset: 4
1212tab-width: 4
1213End:
1214*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Stuff shared between init, reboot, halt, and poweroff
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <signal.h>
24#include <stdlib.h>
25#include <unistd.h>
26#include <getopt.h>
27#include <sys/reboot.h>
28#include <sys/syslog.h>
29#include "busybox.h"
30#include "init_shared.h"
31
32extern int kill_init(int sig)
33{
34#ifdef CONFIG_FEATURE_INITRD
35 /* don't assume init's pid == 1 */
36 long *pid = find_pid_by_name("init");
37 if (!pid || *pid<=0) {
38 pid = find_pid_by_name("linuxrc");
39 if (!pid || *pid<=0)
40 bb_error_msg_and_die("no process killed");
41 }
42 return(kill(*pid, sig));
43#else
44 return(kill(1, sig));
45#endif
46}
47
48#ifndef CONFIG_INIT
49const char * const bb_shutdown_format = "\r%s\n";
50extern int bb_shutdown_system(unsigned long magic)
51{
52 int pri = LOG_KERN|LOG_NOTICE|LOG_FACMASK;
53 const char *message;
54
55 /* Don't kill ourself */
56 signal(SIGTERM,SIG_IGN);
57 signal(SIGHUP,SIG_IGN);
58 setpgrp();
59
60 /* Allow Ctrl-Alt-Del to reboot system. */
61#ifndef RB_ENABLE_CAD
62#define RB_ENABLE_CAD 0x89abcdef
63#endif
64 reboot(RB_ENABLE_CAD);
65
66 openlog(bb_applet_name, 0, pri);
67
68 message = "\nThe system is going down NOW !!";
69 syslog(pri, "%s", message);
70 printf(bb_shutdown_format, message);
71
72 sync();
73
74 /* Send signals to every process _except_ pid 1 */
75 message = "Sending SIGTERM to all processes.";
76 syslog(pri, "%s", message);
77 printf(bb_shutdown_format, message);
78
79 kill(-1, SIGTERM);
80 sleep(1);
81 sync();
82
83 message = "Sending SIGKILL to all processes.";
84 syslog(pri, "%s", message);
85 printf(bb_shutdown_format, message);
86
87 kill(-1, SIGKILL);
88 sleep(1);
89
90 sync();
91
92 reboot(magic);
93 return 0; /* Shrug */
94}
95#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 @@
1extern int kill_init(int sig);
2extern int bb_shutdown_system(unsigned long magic);
3
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 @@
1/*
2 * mesg implementation for busybox
3 *
4 * Copyright (c) 2002 Manuel Novoa III <mjn3@codepoet.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21#include <unistd.h>
22#include <stdlib.h>
23#include "libbb.h"
24
25#ifdef USE_TTY_GROUP
26#define S_IWGRP_OR_S_IWOTH S_IWGRP
27#else
28#define S_IWGRP_OR_S_IWOTH (S_IWGRP | S_IWOTH)
29#endif
30
31extern int mesg_main(int argc, char *argv[])
32{
33 struct stat sb;
34 char *tty;
35 char c = 0;
36
37 if ((--argc == 0)
38 || ((argc == 1) && (((c = **++argv) == 'y') || (c == 'n')))) {
39 if ((tty = ttyname(STDERR_FILENO)) == NULL) {
40 tty = "ttyname";
41 } else if (stat(tty, &sb) == 0) {
42 if (argc == 0) {
43 puts(((sb.st_mode & (S_IWGRP | S_IWOTH)) ==
44 0) ? "is n" : "is y");
45 return EXIT_SUCCESS;
46 }
47 if (chmod
48 (tty,
49 (c ==
50 'y') ? sb.st_mode | (S_IWGRP_OR_S_IWOTH) : sb.
51 st_mode & ~(S_IWGRP | S_IWOTH)) == 0) {
52 return EXIT_SUCCESS;
53 }
54 }
55 bb_perror_msg_and_die("%s", tty);
56 }
57 bb_show_usage();
58}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini poweroff implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <signal.h>
24#include <stdlib.h>
25#include <unistd.h>
26#include <getopt.h>
27#include <sys/reboot.h>
28#include "busybox.h"
29#include "init_shared.h"
30
31
32extern int poweroff_main(int argc, char **argv)
33{
34 char *delay; /* delay in seconds before rebooting */
35
36 if(bb_getopt_ulflags(argc, argv, "d:", &delay)) {
37 sleep(atoi(delay));
38 }
39
40#ifndef CONFIG_INIT
41#ifndef RB_POWER_OFF
42#define RB_POWER_OFF 0x4321fedc
43#endif
44 return(bb_shutdown_system(RB_POWER_OFF));
45#else
46 return kill_init(SIGUSR2);
47#endif
48}
49
50/*
51Local Variables:
52c-file-style: "linux"
53c-basic-offset: 4
54tab-width: 4
55End:
56*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini reboot implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <signal.h>
24#include <stdlib.h>
25#include <unistd.h>
26#include <getopt.h>
27#include <sys/reboot.h>
28#include "busybox.h"
29#include "init_shared.h"
30
31
32extern int reboot_main(int argc, char **argv)
33{
34 char *delay; /* delay in seconds before rebooting */
35
36 if(bb_getopt_ulflags(argc, argv, "d:", &delay)) {
37 sleep(atoi(delay));
38 }
39
40#ifndef CONFIG_INIT
41#ifndef RB_AUTOBOOT
42#define RB_AUTOBOOT 0x01234567
43#endif
44 return(bb_shutdown_system(RB_AUTOBOOT));
45#else
46 return kill_init(SIGTERM);
47#endif
48}
49
50/*
51Local Variables:
52c-file-style: "linux"
53c-basic-offset: 4
54tab-width: 4
55End:
56*/
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/libbb
23LIBBB_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19LIBBB_AR:=libbb.a
20ifndef $(LIBBB_DIR)
21LIBBB_DIR:=$(top_builddir)/libbb/
22endif
23srcdir=$(top_srcdir)/libbb
24
25LIBBB_SRC:= \
26 bb_asprintf.c ask_confirmation.c change_identity.c chomp.c \
27 compare_string_array.c concat_path_file.c copy_file.c copyfd.c \
28 correct_password.c create_icmp_socket.c create_icmp6_socket.c \
29 device_open.c dump.c error_msg.c error_msg_and_die.c find_mount_point.c \
30 find_pid_by_name.c find_root_device.c fgets_str.c full_read.c \
31 full_write.c get_last_path_component.c get_line_from_file.c get_ug_id.c \
32 get_terminal_width_height.c hash_fd.c herror_msg.c herror_msg_and_die.c \
33 human_readable.c inet_common.c inode_hash.c interface.c isdirectory.c \
34 kernel_version.c last_char_is.c llist_add_to.c login.c loop.c \
35 make_directory.c mode_string.c module_syscalls.c mtab.c mtab_file.c \
36 my_getgrgid.c my_getgrnam.c my_getpwnam.c my_getug.c\
37 my_getpwuid.c obscure.c parse_mode.c parse_number.c perror_msg.c \
38 perror_msg_and_die.c print_file.c get_console.c \
39 process_escape_sequence.c procps.c pwd2spwd.c pw_encrypt.c qmodule.c \
40 read_package_field.c recursive_action.c remove_file.c \
41 restricted_shell.c run_parts.c run_shell.c safe_read.c safe_write.c \
42 safe_strncpy.c setup_environment.c simplify_path.c syscalls.c \
43 trim.c u_signal_names.c vdprintf.c verror_msg.c \
44 vherror_msg.c vperror_msg.c wfopen.c xconnect.c xgetcwd.c \
45 xgethostbyname.c xgethostbyname2.c xreadlink.c xregcomp.c xgetlarg.c \
46 get_terminal_width_height.c fclose_nonstdin.c fflush_stdout_and_exit.c \
47 getopt_ulflags.c default_error_retval.c wfopen_input.c speed_table.c \
48 perror_nomsg_and_die.c perror_nomsg.c skip_whitespace.c bb_askpass.c \
49 warn_ignoring_args.c concat_subpath_file.c vfork_daemon_rexec.c
50
51LIBBB_OBJS=$(patsubst %.c,$(LIBBB_DIR)%.o, $(LIBBB_SRC))
52
53LIBBB_MSRC0:=$(srcdir)/messages.c
54LIBBB_MOBJ0:=full_version.o \
55 memory_exhausted.o invalid_date.o io_error.o \
56 write_error.o name_longer_than_foo.o unknown.o \
57 can_not_create_raw_socket.o perm_denied_are_you_root.o \
58 shadow_file.o passwd_file.o group_file.o gshadow_file.o nologin_file.o \
59 securetty_file.o motd_file.o \
60 msg_standard_input.o msg_standard_output.o shell_file.o
61
62LIBBB_MSRC1:=$(srcdir)/xfuncs.c
63LIBBB_MOBJ1:=xmalloc.o xrealloc.o xcalloc.o xstrdup.o xstrndup.o \
64 xfopen.o xopen.o xread.o xread_all.o xread_char.o \
65 xferror.o xferror_stdout.o xfflush_stdout.o strlen.o
66
67LIBBB_MSRC2:=$(srcdir)/printf.c
68LIBBB_MOBJ2:=bb_vfprintf.o bb_vprintf.o bb_fprintf.o bb_printf.o
69
70LIBBB_MSRC3:=$(srcdir)/xgetularg.c
71LIBBB_MOBJ3:=xgetularg_bnd_sfx.o xgetlarg_bnd_sfx.o getlarg10_sfx.o \
72 xgetularg_bnd.o xgetularg10_bnd.o xgetularg10.o
73
74LIBBB_MSRC4:=$(srcdir)/safe_strtol.c
75LIBBB_MOBJ4:=safe_strtoi.o safe_strtod.o safe_strtol.o safe_strtoul.o
76
77LIBBB_MOBJS0=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ0))
78LIBBB_MOBJS1=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ1))
79LIBBB_MOBJS2=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ2))
80LIBBB_MOBJS3=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ3))
81LIBBB_MOBJS4=$(patsubst %,$(LIBBB_DIR)%, $(LIBBB_MOBJ4))
82
83libraries-y+=$(LIBBB_DIR)$(LIBBB_AR)
84
85$(LIBBB_DIR)$(LIBBB_AR): $(LIBBB_OBJS) $(LIBBB_MOBJS0) $(LIBBB_MOBJS1) \
86 $(LIBBB_MOBJS2) $(LIBBB_MOBJS3) $(LIBBB_MOBJS4)
87 $(AR) -ro $@ $(LIBBB_OBJS) $(LIBBB_MOBJS0) $(LIBBB_MOBJS1) \
88 $(LIBBB_MOBJS2) $(LIBBB_MOBJS3) $(LIBBB_MOBJS4)
89
90$(LIBBB_DIR)%.o: $(srcdir)/%.c
91 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
92
93$(LIBBB_MOBJS0): $(LIBBB_MSRC0)
94 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@
95
96$(LIBBB_MOBJS1): $(LIBBB_MSRC1)
97 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@
98
99$(LIBBB_MOBJS2): $(LIBBB_MSRC2)
100 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@
101
102$(LIBBB_MOBJS3): $(LIBBB_MSRC3)
103 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@
104
105$(LIBBB_MOBJS4): $(LIBBB_MSRC4)
106 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@
107
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 @@
1Please see the LICENSE file for copyright information (GPLv2)
2
3libbb is BusyBox's utility library. All of this stuff used to be stuffed into
4a single file named utility.c. When I split utility.c to create libbb, some of
5the very oldest stuff ended up without their original copyright and licensing
6information (which is now lost in the mists of time). If you see something
7that you wrote that is mis-attributed, do let me know so we can fix that up.
8
9 Erik Andersen
10 <andersen@codepoet.org>
11
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * bb_ask_confirmation implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* Read a line from stdin. If the first non-whitespace char is 'y' or 'Y',
24 * return 1. Otherwise return 0.
25 */
26
27#include <stdio.h>
28#include <ctype.h>
29#include "libbb.h"
30
31int bb_ask_confirmation(void)
32{
33 int retval = 0;
34 int first = 1;
35 int c;
36
37 while (((c = getchar()) != EOF) && (c != '\n')) {
38 /* Make sure we get the actual function call for isspace,
39 * as speed is not critical here. */
40 if (first && !(isspace)(c)) {
41 --first;
42 if ((c == 'y') || (c == 'Y')) {
43 ++retval;
44 }
45 }
46 }
47
48 return retval;
49}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Ask for a password
4 * I use a static buffer in this function. Plan accordingly.
5 *
6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include <stdio.h>
24#include <string.h>
25#include <unistd.h>
26#include <fcntl.h>
27#include <signal.h>
28#include <termios.h>
29#include <sys/ioctl.h>
30#define PWD_BUFFER_SIZE 256
31
32
33/* do nothing signal handler */
34static void askpass_timeout(int ignore)
35{
36}
37
38char *bb_askpass(int timeout, const char * prompt)
39{
40 char *ret;
41 int i, size;
42 struct sigaction sa;
43 struct termios old, new;
44 static char passwd[PWD_BUFFER_SIZE];
45
46 tcgetattr(STDIN_FILENO, &old);
47
48 size = sizeof(passwd);
49 ret = passwd;
50 memset(passwd, 0, size);
51
52 fputs(prompt, stdout);
53 fflush(stdout);
54
55 tcgetattr(STDIN_FILENO, &new);
56 new.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
57 new.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP);
58 tcsetattr(STDIN_FILENO, TCSANOW, &new);
59
60 if (timeout) {
61 sa.sa_flags = 0;
62 sa.sa_handler = askpass_timeout;
63 sigaction(SIGALRM, &sa, NULL);
64 alarm(timeout);
65 }
66
67 if (read(STDIN_FILENO, passwd, size-1) <= 0) {
68 ret = NULL;
69 } else {
70 for(i = 0; i < size && passwd[i]; i++) {
71 if (passwd[i]== '\r' || passwd[i] == '\n') {
72 passwd[i]= 0;
73 break;
74 }
75 }
76 }
77
78 if (timeout) {
79 alarm(0);
80 }
81
82 tcsetattr(STDIN_FILENO, TCSANOW, &old);
83 fputs("\n", stdout);
84 fflush(stdout);
85 return ret;
86}
87
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 @@
1/*
2 Copyright (C) 2002 Vladimir Oleynik <dzo@simtreas.ru>
3*/
4
5#include <stdlib.h>
6#include <stdio.h>
7#include <stdarg.h>
8#include "libbb.h"
9
10void bb_xasprintf(char **string_ptr, const char *format, ...)
11{
12 va_list p;
13 int r;
14
15 va_start(p, format);
16 r = vasprintf(string_ptr, format, p);
17 va_end(p);
18
19 if (r < 0) {
20 bb_perror_msg_and_die("bb_xasprintf");
21 }
22}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <stdio.h>
32#include <errno.h>
33#include <unistd.h>
34#include <string.h>
35#include <stdlib.h>
36#include <syslog.h>
37#include <ctype.h>
38
39#include "libbb.h"
40
41
42/* Become the user and group(s) specified by PW. */
43const char *change_identity_e2str ( const struct passwd *pw )
44{
45 if ( initgroups ( pw-> pw_name, pw-> pw_gid ) == -1 )
46 return "cannot set groups";
47 endgrent ( );
48
49 if ( setgid ( pw-> pw_gid ))
50 return "cannot set group id";
51 if ( setuid ( pw->pw_uid ))
52 return "cannot set user id";
53 return NULL;
54}
55
56void change_identity ( const struct passwd *pw )
57{
58 const char *err_msg = change_identity_e2str(pw);
59
60 if(err_msg)
61 bb_perror_msg_and_die ( "%s", err_msg );
62}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) many different people.
6 * If you wrote this, please acknowledge your work.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 * USA
22 */
23
24#include <stdio.h>
25#include <string.h>
26#include "libbb.h"
27
28
29void chomp(char *s)
30{
31 char *lc = last_char_is(s, '\n');
32
33 if(lc)
34 *lc = 0;
35}
36
37
38/* END CODE */
39/*
40Local Variables:
41c-file-style: "linux"
42c-basic-offset: 4
43tab-width: 4
44End:
45*/
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <string.h>
18
19/* returns the array number of the string */
20extern unsigned short compare_string_array(const char *string_array[], const char *key)
21{
22 unsigned short i;
23
24 for (i = 0; string_array[i] != 0; i++) {
25 if (strcmp(string_array[i], key) == 0) {
26 break;
27 }
28 }
29 return(i);
30}
31
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) many different people.
6 * If you wrote this, please acknowledge your work.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 * USA
22 */
23
24/* concatenate path and file name to new allocation buffer,
25 * not addition '/' if path name already have '/'
26*/
27
28#include <string.h>
29#include "libbb.h"
30
31extern char *concat_path_file(const char *path, const char *filename)
32{
33 char *outbuf;
34 char *lc;
35
36 if (!path)
37 path="";
38 lc = last_char_is(path, '/');
39 while (*filename == '/')
40 filename++;
41 bb_xasprintf(&outbuf, "%s%s%s", path, (lc==NULL)? "/" : "", filename);
42
43 return outbuf;
44}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 */
22
23/*
24 This function make special for recursive actions with usage
25 concat_path_file(path, filename)
26 and skiping "." and ".." directory entries
27*/
28
29#include "libbb.h"
30
31extern char *concat_subpath_file(const char *path, const char *f)
32{
33 if(f && *f == '.' && (!f[1] || (f[1] == '.' && !f[2])))
34 return NULL;
35 return concat_path_file(path, f);
36}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini copy_file implementation for busybox
4 *
5 * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <unistd.h>
26#include <fcntl.h>
27#include <utime.h>
28#include <errno.h>
29#include <dirent.h>
30#include <stdlib.h>
31#include <string.h>
32
33#include "busybox.h"
34
35int copy_file(const char *source, const char *dest, int flags)
36{
37 struct stat source_stat;
38 struct stat dest_stat;
39 int dest_exists = 0;
40 int status = 0;
41
42 if ((!(flags & FILEUTILS_DEREFERENCE) &&
43 lstat(source, &source_stat) < 0) ||
44 ((flags & FILEUTILS_DEREFERENCE) &&
45 stat(source, &source_stat) < 0)) {
46 bb_perror_msg("%s", source);
47 return -1;
48 }
49
50 if (lstat(dest, &dest_stat) < 0) {
51 if (errno != ENOENT) {
52 bb_perror_msg("unable to stat `%s'", dest);
53 return -1;
54 }
55 } else {
56 if (source_stat.st_dev == dest_stat.st_dev &&
57 source_stat.st_ino == dest_stat.st_ino) {
58 bb_error_msg("`%s' and `%s' are the same file", source, dest);
59 return -1;
60 }
61 dest_exists = 1;
62 }
63
64 if (S_ISDIR(source_stat.st_mode)) {
65 DIR *dp;
66 struct dirent *d;
67 mode_t saved_umask = 0;
68
69 if (!(flags & FILEUTILS_RECUR)) {
70 bb_error_msg("%s: omitting directory", source);
71 return -1;
72 }
73
74 /* Create DEST. */
75 if (dest_exists) {
76 if (!S_ISDIR(dest_stat.st_mode)) {
77 bb_error_msg("`%s' is not a directory", dest);
78 return -1;
79 }
80 } else {
81 mode_t mode;
82 saved_umask = umask(0);
83
84 mode = source_stat.st_mode;
85 if (!(flags & FILEUTILS_PRESERVE_STATUS))
86 mode = source_stat.st_mode & ~saved_umask;
87 mode |= S_IRWXU;
88
89 if (mkdir(dest, mode) < 0) {
90 umask(saved_umask);
91 bb_perror_msg("cannot create directory `%s'", dest);
92 return -1;
93 }
94
95 umask(saved_umask);
96 }
97
98 /* Recursively copy files in SOURCE. */
99 if ((dp = opendir(source)) == NULL) {
100 bb_perror_msg("unable to open directory `%s'", source);
101 status = -1;
102 goto end;
103 }
104
105 while ((d = readdir(dp)) != NULL) {
106 char *new_source, *new_dest;
107
108 new_source = concat_subpath_file(source, d->d_name);
109 if(new_source == NULL)
110 continue;
111 new_dest = concat_path_file(dest, d->d_name);
112 if (copy_file(new_source, new_dest, flags) < 0)
113 status = -1;
114 free(new_source);
115 free(new_dest);
116 }
117 /* closedir have only EBADF error, but "dp" not changes */
118 closedir(dp);
119
120 if (!dest_exists &&
121 chmod(dest, source_stat.st_mode & ~saved_umask) < 0) {
122 bb_perror_msg("unable to change permissions of `%s'", dest);
123 status = -1;
124 }
125 } else if (S_ISREG(source_stat.st_mode)) {
126 int src_fd;
127 int dst_fd;
128#ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS
129 char *link_name;
130
131 if (!(flags & FILEUTILS_DEREFERENCE) &&
132 is_in_ino_dev_hashtable(&source_stat, &link_name)) {
133 if (link(link_name, dest) < 0) {
134 bb_perror_msg("unable to link `%s'", dest);
135 return -1;
136 }
137
138 return 0;
139 }
140#endif
141 src_fd = open(source, O_RDONLY);
142 if (src_fd == -1) {
143 bb_perror_msg("unable to open `%s'", source);
144 return(-1);
145 }
146
147 if (dest_exists) {
148 if (flags & FILEUTILS_INTERACTIVE) {
149 bb_error_msg("overwrite `%s'? ", dest);
150 if (!bb_ask_confirmation()) {
151 close (src_fd);
152 return 0;
153 }
154 }
155
156 dst_fd = open(dest, O_WRONLY|O_TRUNC);
157 if (dst_fd == -1) {
158 if (!(flags & FILEUTILS_FORCE)) {
159 bb_perror_msg("unable to open `%s'", dest);
160 close(src_fd);
161 return -1;
162 }
163
164 if (unlink(dest) < 0) {
165 bb_perror_msg("unable to remove `%s'", dest);
166 close(src_fd);
167 return -1;
168 }
169
170 dest_exists = 0;
171 }
172 }
173
174 if (!dest_exists) {
175 dst_fd = open(dest, O_WRONLY|O_CREAT, source_stat.st_mode);
176 if (dst_fd == -1) {
177 bb_perror_msg("unable to open `%s'", dest);
178 close(src_fd);
179 return(-1);
180 }
181 }
182
183 if (bb_copyfd_eof(src_fd, dst_fd) == -1)
184 status = -1;
185
186 if (close(dst_fd) < 0) {
187 bb_perror_msg("unable to close `%s'", dest);
188 status = -1;
189 }
190
191 if (close(src_fd) < 0) {
192 bb_perror_msg("unable to close `%s'", source);
193 status = -1;
194 }
195 }
196 else if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) ||
197 S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) ||
198 S_ISLNK(source_stat.st_mode)) {
199
200 if (dest_exists &&
201 ((flags & FILEUTILS_FORCE) == 0 || unlink(dest) < 0)) {
202 bb_perror_msg("unable to remove `%s'", dest);
203 return -1;
204
205 }
206 } else {
207 bb_error_msg("internal error: unrecognized file type");
208 return -1;
209 }
210 if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) ||
211 S_ISSOCK(source_stat.st_mode)) {
212 if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) {
213 bb_perror_msg("unable to create `%s'", dest);
214 return -1;
215 }
216 } else if (S_ISFIFO(source_stat.st_mode)) {
217 if (mkfifo(dest, source_stat.st_mode) < 0) {
218 bb_perror_msg("cannot create fifo `%s'", dest);
219 return -1;
220 }
221 } else if (S_ISLNK(source_stat.st_mode)) {
222 char *lpath;
223
224 lpath = xreadlink(source);
225 if (symlink(lpath, dest) < 0) {
226 bb_perror_msg("cannot create symlink `%s'", dest);
227 return -1;
228 }
229 free(lpath);
230
231#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
232 if (flags & FILEUTILS_PRESERVE_STATUS)
233 if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
234 bb_perror_msg("unable to preserve ownership of `%s'", dest);
235#endif
236
237#ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS
238 add_to_ino_dev_hashtable(&source_stat, dest);
239#endif
240
241 return 0;
242 }
243
244#ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS
245 if (! S_ISDIR(source_stat.st_mode)) {
246 add_to_ino_dev_hashtable(&source_stat, dest);
247 }
248#endif
249
250end:
251
252 if (flags & FILEUTILS_PRESERVE_STATUS) {
253 struct utimbuf times;
254
255 times.actime = source_stat.st_atime;
256 times.modtime = source_stat.st_mtime;
257 if (utime(dest, &times) < 0)
258 bb_perror_msg("unable to preserve times of `%s'", dest);
259 if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) {
260 source_stat.st_mode &= ~(S_ISUID | S_ISGID);
261 bb_perror_msg("unable to preserve ownership of `%s'", dest);
262 }
263 if (chmod(dest, source_stat.st_mode) < 0)
264 bb_perror_msg("unable to preserve permissions of `%s'", dest);
265 }
266
267 return status;
268}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <errno.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26
27#include "busybox.h"
28
29
30#if BUFSIZ < 4096
31#undef BUFSIZ
32#define BUFSIZ 4096
33#endif
34
35
36/* If size is 0 copy until EOF */
37static size_t bb_full_fd_action(int src_fd, int dst_fd, const size_t size)
38{
39 size_t read_total = 0;
40 RESERVE_CONFIG_BUFFER(buffer,BUFSIZ);
41
42 while ((size == 0) || (read_total < size)) {
43 size_t read_try;
44 ssize_t read_actual;
45
46 if ((size == 0) || (size - read_total > BUFSIZ)) {
47 read_try = BUFSIZ;
48 } else {
49 read_try = size - read_total;
50 }
51
52 read_actual = safe_read(src_fd, buffer, read_try);
53 if (read_actual > 0) {
54 if ((dst_fd >= 0) && (bb_full_write(dst_fd, buffer, (size_t) read_actual) != read_actual)) {
55 bb_perror_msg(bb_msg_write_error); /* match Read error below */
56 break;
57 }
58 }
59 else if (read_actual == 0) {
60 if (size) {
61 bb_error_msg("Unable to read all data");
62 }
63 break;
64 } else {
65 /* read_actual < 0 */
66 bb_perror_msg("Read error");
67 break;
68 }
69
70 read_total += read_actual;
71 }
72
73 RELEASE_CONFIG_BUFFER(buffer);
74
75 return(read_total);
76}
77
78
79extern int bb_copyfd_size(int fd1, int fd2, const off_t size)
80{
81 if (size) {
82 return(bb_full_fd_action(fd1, fd2, size));
83 }
84 return(0);
85}
86
87extern int bb_copyfd_eof(int fd1, int fd2)
88{
89 return(bb_full_fd_action(fd1, fd2, 0));
90}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <stdio.h>
32#include <errno.h>
33#include <unistd.h>
34#include <string.h>
35#include <stdlib.h>
36#include <syslog.h>
37#include <ctype.h>
38#include <crypt.h>
39
40#include "libbb.h"
41
42
43
44/* Ask the user for a password.
45 Return 1 if the user gives the correct password for entry PW,
46 0 if not. Return 1 without asking for a password if run by UID 0
47 or if PW has an empty password. */
48
49int correct_password ( const struct passwd *pw )
50{
51 char *unencrypted, *encrypted, *correct;
52
53#ifdef CONFIG_FEATURE_SHADOWPASSWDS
54 if (( strcmp ( pw-> pw_passwd, "x" ) == 0 ) || ( strcmp ( pw-> pw_passwd, "*" ) == 0 )) {
55 struct spwd *sp = getspnam ( pw-> pw_name );
56
57 if ( !sp )
58 bb_error_msg_and_die ( "no valid shadow password" );
59
60 correct = sp-> sp_pwdp;
61 }
62 else
63#endif
64 correct = pw-> pw_passwd;
65
66 if ( correct == 0 || correct[0] == '\0' )
67 return 1;
68
69 unencrypted = bb_askpass ( 0, "Password: " );
70 if ( !unencrypted )
71 {
72 return 0;
73 }
74 encrypted = crypt ( unencrypted, correct );
75 memset ( unencrypted, 0, bb_strlen ( unencrypted ));
76 return ( strcmp ( encrypted, correct ) == 0 ) ? 1 : 0;
77}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * create raw socket for icmp (IPv6 version) protocol test permission
6 * and drop root privileges if running setuid
7 *
8 */
9
10#include <sys/types.h>
11#include <netdb.h>
12#include <sys/socket.h>
13#include <errno.h>
14#include <unistd.h>
15#include "libbb.h"
16
17#ifdef CONFIG_FEATURE_IPV6
18int create_icmp6_socket(void)
19{
20 struct protoent *proto;
21 int sock;
22
23 proto = getprotobyname("ipv6-icmp");
24 /* if getprotobyname failed, just silently force
25 * proto->p_proto to have the correct value for "ipv6-icmp" */
26 if ((sock = socket(AF_INET6, SOCK_RAW,
27 (proto ? proto->p_proto : IPPROTO_ICMPV6))) < 0) {
28 if (errno == EPERM)
29 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
30 else
31 bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
32 }
33
34 /* drop root privs if running setuid */
35 setuid(getuid());
36
37 return sock;
38}
39#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * create raw socket for icmp protocol test permission
6 * and drop root privileges if running setuid
7 *
8 */
9
10#include <sys/types.h>
11#include <netdb.h>
12#include <sys/socket.h>
13#include <errno.h>
14#include <unistd.h>
15#include "libbb.h"
16
17int create_icmp_socket(void)
18{
19 struct protoent *proto;
20 int sock;
21
22 proto = getprotobyname("icmp");
23 /* if getprotobyname failed, just silently force
24 * proto->p_proto to have the correct value for "icmp" */
25 if ((sock = socket(AF_INET, SOCK_RAW,
26 (proto ? proto->p_proto : 1))) < 0) { /* 1 == ICMP */
27 if (errno == EPERM)
28 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
29 else
30 bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
31 }
32
33 /* drop root privs if running setuid */
34 setuid(getuid());
35
36 return sock;
37}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21/* Seems silly to copyright a global variable. ;-) Oh well.
22 *
23 * At least one applet (cmp) returns a value different from the typical
24 * EXIT_FAILURE values (1) when an error occurs. So, make it configurable
25 * by the applet. I suppose we could use a wrapper function to set it, but
26 * that too seems silly.
27 */
28
29#include <stdlib.h>
30#include "libbb.h"
31
32int 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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <fcntl.h>
24#include "libbb.h"
25
26
27/* try to open up the specified device */
28extern int device_open(const char *device, int mode)
29{
30 int m, f, fd = -1;
31
32 m = mode | O_NONBLOCK;
33
34 /* Retry up to 5 times */
35 for (f = 0; f < 5; f++)
36 if ((fd = open(device, m, 0600)) >= 0)
37 break;
38 if (fd < 0)
39 return fd;
40 /* Reset original flags. */
41 if (m != mode)
42 fcntl(fd, F_SETFL, mode);
43 return fd;
44}
45
46/* END CODE */
47/*
48Local Variables:
49c-file-style: "linux"
50c-basic-offset: 4
51tab-width: 4
52End:
53*/
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 @@
1/*
2 * Support code for the hexdump and od applets,
3 * based on code from util-linux v 2.11l
4 *
5 * Copyright (c) 1989
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * Original copyright notice is retained at the end of this file.
23 */
24
25#include <stdlib.h>
26#include <string.h>
27#include <unistd.h>
28#include <ctype.h> /* for isdigit() */
29#include "libbb.h"
30#include "dump.h"
31
32enum _vflag bb_dump_vflag = FIRST;
33FS *bb_dump_fshead; /* head of format strings */
34static FU *endfu;
35static char **_argv;
36static off_t savaddress; /* saved address/offset in stream */
37static off_t eaddress; /* end address */
38static off_t address; /* address/offset in stream */
39off_t bb_dump_skip; /* bytes to skip */
40static int exitval; /* final exit value */
41int bb_dump_blocksize; /* data block size */
42int bb_dump_length = -1; /* max bytes to read */
43
44static const char index_str[] = ".#-+ 0123456789";
45
46static const char size_conv_str[] =
47"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG";
48
49static const char lcc[] = "diouxX";
50
51int bb_dump_size(FS * fs)
52{
53 register FU *fu;
54 register int bcnt, cur_size;
55 register char *fmt;
56 const char *p;
57 int prec;
58
59 /* figure out the data block bb_dump_size needed for each format unit */
60 for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
61 if (fu->bcnt) {
62 cur_size += fu->bcnt * fu->reps;
63 continue;
64 }
65 for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
66 if (*fmt != '%')
67 continue;
68 /*
69 * bb_dump_skip any special chars -- save precision in
70 * case it's a %s format.
71 */
72 while (strchr(index_str + 1, *++fmt));
73 if (*fmt == '.' && isdigit(*++fmt)) {
74 prec = atoi(fmt);
75 while (isdigit(*++fmt));
76 }
77 if (!(p = strchr(size_conv_str + 12, *fmt))) {
78 if (*fmt == 's') {
79 bcnt += prec;
80 } else if (*fmt == '_') {
81 ++fmt;
82 if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) {
83 bcnt += 1;
84 }
85 }
86 } else {
87 bcnt += size_conv_str[p - (size_conv_str + 12)];
88 }
89 }
90 cur_size += bcnt * fu->reps;
91 }
92 return (cur_size);
93}
94
95static void rewrite(FS * fs)
96{
97 enum { NOTOKAY, USEBCNT, USEPREC } sokay;
98 register PR *pr, **nextpr = NULL;
99 register FU *fu;
100 register char *p1, *p2, *p3;
101 char savech, *fmtp;
102 const char *byte_count_str;
103 int nconv, prec = 0;
104
105 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
106 /*
107 * break each format unit into print units; each
108 * conversion character gets its own.
109 */
110 for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
111 /* NOSTRICT */
112 /* DBU:[dvae@cray.com] calloc so that forward ptrs start out NULL*/
113 pr = (PR *) xcalloc(1,sizeof(PR));
114 if (!fu->nextpr)
115 fu->nextpr = pr;
116 /* ignore nextpr -- its unused inside the loop and is
117 * uninitialized 1st time thru.
118 */
119
120 /* bb_dump_skip preceding text and up to the next % sign */
121 for (p1 = fmtp; *p1 && *p1 != '%'; ++p1);
122
123 /* only text in the string */
124 if (!*p1) {
125 pr->fmt = fmtp;
126 pr->flags = F_TEXT;
127 break;
128 }
129
130 /*
131 * get precision for %s -- if have a byte count, don't
132 * need it.
133 */
134 if (fu->bcnt) {
135 sokay = USEBCNT;
136 /* bb_dump_skip to conversion character */
137 for (++p1; strchr(index_str, *p1); ++p1);
138 } else {
139 /* bb_dump_skip any special chars, field width */
140 while (strchr(index_str + 1, *++p1));
141 if (*p1 == '.' && isdigit(*++p1)) {
142 sokay = USEPREC;
143 prec = atoi(p1);
144 while (isdigit(*++p1));
145 } else
146 sokay = NOTOKAY;
147 }
148
149 p2 = p1 + 1; /* set end pointer */
150
151 /*
152 * figure out the byte count for each conversion;
153 * rewrite the format as necessary, set up blank-
154 * pbb_dump_adding for end of data.
155 */
156
157 if (*p1 == 'c') {
158 pr->flags = F_CHAR;
159 DO_BYTE_COUNT_1:
160 byte_count_str = "\001";
161 DO_BYTE_COUNT:
162 if (fu->bcnt) {
163 do {
164 if (fu->bcnt == *byte_count_str) {
165 break;
166 }
167 } while (*++byte_count_str);
168 }
169 /* Unlike the original, output the remainder of the format string. */
170 if (!*byte_count_str) {
171 bb_error_msg_and_die("bad byte count for conversion character %s.", p1);
172 }
173 pr->bcnt = *byte_count_str;
174 } else if (*p1 == 'l') {
175 ++p2;
176 ++p1;
177 DO_INT_CONV:
178 {
179 const char *e;
180 if (!(e = strchr(lcc, *p1))) {
181 goto DO_BAD_CONV_CHAR;
182 }
183 pr->flags = F_INT;
184 if (e > lcc + 1) {
185 pr->flags = F_UINT;
186 }
187 byte_count_str = "\004\002\001";
188 goto DO_BYTE_COUNT;
189 }
190 /* NOTREACHED */
191 } else if (strchr(lcc, *p1)) {
192 goto DO_INT_CONV;
193 } else if (strchr("eEfgG", *p1)) {
194 pr->flags = F_DBL;
195 byte_count_str = "\010\004";
196 goto DO_BYTE_COUNT;
197 } else if (*p1 == 's') {
198 pr->flags = F_STR;
199 if (sokay == USEBCNT) {
200 pr->bcnt = fu->bcnt;
201 } else if (sokay == USEPREC) {
202 pr->bcnt = prec;
203 } else { /* NOTOKAY */
204 bb_error_msg_and_die("%%s requires a precision or a byte count.");
205 }
206 } else if (*p1 == '_') {
207 ++p2;
208 switch (p1[1]) {
209 case 'A':
210 endfu = fu;
211 fu->flags |= F_IGNORE;
212 /* FALLTHROUGH */
213 case 'a':
214 pr->flags = F_ADDRESS;
215 ++p2;
216 if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) {
217 goto DO_BAD_CONV_CHAR;
218 }
219 *p1 = p1[2];
220 break;
221 case 'c':
222 pr->flags = F_C;
223 /* *p1 = 'c'; set in conv_c */
224 goto DO_BYTE_COUNT_1;
225 case 'p':
226 pr->flags = F_P;
227 *p1 = 'c';
228 goto DO_BYTE_COUNT_1;
229 case 'u':
230 pr->flags = F_U;
231 /* *p1 = 'c'; set in conv_u */
232 goto DO_BYTE_COUNT_1;
233 default:
234 goto DO_BAD_CONV_CHAR;
235 }
236 } else {
237 DO_BAD_CONV_CHAR:
238 bb_error_msg_and_die("bad conversion character %%%s.\n", p1);
239 }
240
241 /*
242 * copy to PR format string, set conversion character
243 * pointer, update original.
244 */
245 savech = *p2;
246 p1[1] = '\0';
247 pr->fmt = bb_xstrdup(fmtp);
248 *p2 = savech;
249 pr->cchar = pr->fmt + (p1 - fmtp);
250
251 /* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost.
252 * Skip subsequent text and up to the next % sign and tack the
253 * additional text onto fmt: eg. if fmt is "%x is a HEX number",
254 * we lose the " is a HEX number" part of fmt.
255 */
256 for (p3 = p2; *p3 && *p3 != '%'; p3++);
257 if (p3 > p2)
258 {
259 savech = *p3;
260 *p3 = '\0';
261 if (!(pr->fmt = realloc(pr->fmt, strlen(pr->fmt)+(p3-p2)+1)))
262 bb_perror_msg_and_die("hexdump");
263 strcat(pr->fmt, p2);
264 *p3 = savech;
265 p2 = p3;
266 }
267
268 fmtp = p2;
269
270 /* only one conversion character if byte count */
271 if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) {
272 bb_error_msg_and_die("byte count with multiple conversion characters.\n");
273 }
274 }
275 /*
276 * if format unit byte count not specified, figure it out
277 * so can adjust rep count later.
278 */
279 if (!fu->bcnt)
280 for (pr = fu->nextpr; pr; pr = pr->nextpr)
281 fu->bcnt += pr->bcnt;
282 }
283 /*
284 * if the format string interprets any data at all, and it's
285 * not the same as the bb_dump_blocksize, and its last format unit
286 * interprets any data at all, and has no iteration count,
287 * repeat it as necessary.
288 *
289 * if, rep count is greater than 1, no trailing whitespace
290 * gets output from the last iteration of the format unit.
291 */
292 for (fu = fs->nextfu;; fu = fu->nextfu) {
293 if (!fu->nextfu && fs->bcnt < bb_dump_blocksize &&
294 !(fu->flags & F_SETREP) && fu->bcnt)
295 fu->reps += (bb_dump_blocksize - fs->bcnt) / fu->bcnt;
296 if (fu->reps > 1) {
297 for (pr = fu->nextpr;; pr = pr->nextpr)
298 if (!pr->nextpr)
299 break;
300 for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
301 p2 = isspace(*p1) ? p1 : NULL;
302 if (p2)
303 pr->nospace = p2;
304 }
305 if (!fu->nextfu)
306 break;
307 }
308}
309
310static void do_skip(char *fname, int statok)
311{
312 struct stat sbuf;
313
314 if (statok) {
315 if (fstat(STDIN_FILENO, &sbuf)) {
316 bb_perror_msg_and_die("%s", fname);
317 }
318 if ((!(S_ISCHR(sbuf.st_mode) ||
319 S_ISBLK(sbuf.st_mode) ||
320 S_ISFIFO(sbuf.st_mode))) && bb_dump_skip >= sbuf.st_size) {
321 /* If bb_dump_size valid and bb_dump_skip >= size */
322 bb_dump_skip -= sbuf.st_size;
323 address += sbuf.st_size;
324 return;
325 }
326 }
327 if (fseek(stdin, bb_dump_skip, SEEK_SET)) {
328 bb_perror_msg_and_die("%s", fname);
329 }
330 savaddress = address += bb_dump_skip;
331 bb_dump_skip = 0;
332}
333
334static int next(char **argv)
335{
336 static int done;
337 int statok;
338
339 if (argv) {
340 _argv = argv;
341 return (1);
342 }
343 for (;;) {
344 if (*_argv) {
345 if (!(freopen(*_argv, "r", stdin))) {
346 bb_perror_msg("%s", *_argv);
347 exitval = 1;
348 ++_argv;
349 continue;
350 }
351 statok = done = 1;
352 } else {
353 if (done++)
354 return (0);
355 statok = 0;
356 }
357 if (bb_dump_skip)
358 do_skip(statok ? *_argv : "stdin", statok);
359 if (*_argv)
360 ++_argv;
361 if (!bb_dump_skip)
362 return (1);
363 }
364 /* NOTREACHED */
365}
366
367static u_char *get(void)
368{
369 static int ateof = 1;
370 static u_char *curp=NULL, *savp; /*DBU:[dave@cray.com]initialize curp */
371 register int n;
372 int need, nread;
373 u_char *tmpp;
374
375 if (!curp) {
376 address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/
377 curp = (u_char *) xmalloc(bb_dump_blocksize);
378 savp = (u_char *) xmalloc(bb_dump_blocksize);
379 } else {
380 tmpp = curp;
381 curp = savp;
382 savp = tmpp;
383 address = savaddress += bb_dump_blocksize;
384 }
385 for (need = bb_dump_blocksize, nread = 0;;) {
386 /*
387 * if read the right number of bytes, or at EOF for one file,
388 * and no other files are available, zero-pad the rest of the
389 * block and set the end flag.
390 */
391 if (!bb_dump_length || (ateof && !next((char **) NULL))) {
392 if (need == bb_dump_blocksize) {
393 return ((u_char *) NULL);
394 }
395 if (bb_dump_vflag != ALL && !bcmp(curp, savp, nread)) {
396 if (bb_dump_vflag != DUP) {
397 printf("*\n");
398 }
399 return ((u_char *) NULL);
400 }
401 bzero((char *) curp + nread, need);
402 eaddress = address + nread;
403 return (curp);
404 }
405 n = fread((char *) curp + nread, sizeof(u_char),
406 bb_dump_length == -1 ? need : MIN(bb_dump_length, need), stdin);
407 if (!n) {
408 if (ferror(stdin)) {
409 bb_perror_msg("%s", _argv[-1]);
410 }
411 ateof = 1;
412 continue;
413 }
414 ateof = 0;
415 if (bb_dump_length != -1) {
416 bb_dump_length -= n;
417 }
418 if (!(need -= n)) {
419 if (bb_dump_vflag == ALL || bb_dump_vflag == FIRST
420 || bcmp(curp, savp, bb_dump_blocksize)) {
421 if (bb_dump_vflag == DUP || bb_dump_vflag == FIRST) {
422 bb_dump_vflag = WAIT;
423 }
424 return (curp);
425 }
426 if (bb_dump_vflag == WAIT) {
427 printf("*\n");
428 }
429 bb_dump_vflag = DUP;
430 address = savaddress += bb_dump_blocksize;
431 need = bb_dump_blocksize;
432 nread = 0;
433 } else {
434 nread += n;
435 }
436 }
437}
438
439static void bpad(PR * pr)
440{
441 register char *p1, *p2;
442
443 /*
444 * remove all conversion flags; '-' is the only one valid
445 * with %s, and it's not useful here.
446 */
447 pr->flags = F_BPAD;
448 *pr->cchar = 's';
449 for (p1 = pr->fmt; *p1 != '%'; ++p1);
450 for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1);
451 while ((*p2++ = *p1++) != 0);
452}
453
454static const char conv_str[] =
455 "\0\\0\0"
456 "\007\\a\0" /* \a */
457 "\b\\b\0"
458 "\f\\b\0"
459 "\n\\n\0"
460 "\r\\r\0"
461 "\t\\t\0"
462 "\v\\v\0"
463 "\0";
464
465
466static void conv_c(PR * pr, u_char * p)
467{
468 const char *str = conv_str;
469 char buf[10];
470
471 do {
472 if (*p == *str) {
473 ++str;
474 goto strpr;
475 }
476 str += 4;
477 } while (*str);
478
479 if (isprint(*p)) {
480 *pr->cchar = 'c';
481 (void) printf(pr->fmt, *p);
482 } else {
483 sprintf(buf, "%03o", (int) *p);
484 str = buf;
485 strpr:
486 *pr->cchar = 's';
487 printf(pr->fmt, str);
488 }
489}
490
491static void conv_u(PR * pr, u_char * p)
492{
493 static const char list[] =
494 "nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0"
495 "bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_"
496 "dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0"
497 "can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us";
498
499 /* od used nl, not lf */
500 if (*p <= 0x1f) {
501 *pr->cchar = 's';
502 printf(pr->fmt, list + (4 * (int)*p));
503 } else if (*p == 0x7f) {
504 *pr->cchar = 's';
505 printf(pr->fmt, "del");
506 } else if (isprint(*p)) {
507 *pr->cchar = 'c';
508 printf(pr->fmt, *p);
509 } else {
510 *pr->cchar = 'x';
511 printf(pr->fmt, (int) *p);
512 }
513}
514
515static void display(void)
516{
517/* extern FU *endfu; */
518 register FS *fs;
519 register FU *fu;
520 register PR *pr;
521 register int cnt;
522 register u_char *bp;
523
524 off_t saveaddress;
525 u_char savech = 0, *savebp;
526
527 while ((bp = get()) != NULL) {
528 for (fs = bb_dump_fshead, savebp = bp, saveaddress = address; fs;
529 fs = fs->nextfs, bp = savebp, address = saveaddress) {
530 for (fu = fs->nextfu; fu; fu = fu->nextfu) {
531 if (fu->flags & F_IGNORE) {
532 break;
533 }
534 for (cnt = fu->reps; cnt; --cnt) {
535 for (pr = fu->nextpr; pr; address += pr->bcnt,
536 bp += pr->bcnt, pr = pr->nextpr) {
537 if (eaddress && address >= eaddress &&
538 !(pr->flags & (F_TEXT | F_BPAD))) {
539 bpad(pr);
540 }
541 if (cnt == 1 && pr->nospace) {
542 savech = *pr->nospace;
543 *pr->nospace = '\0';
544 }
545/* PRINT; */
546 switch (pr->flags) {
547 case F_ADDRESS:
548 printf(pr->fmt, (unsigned int) address);
549 break;
550 case F_BPAD:
551 printf(pr->fmt, "");
552 break;
553 case F_C:
554 conv_c(pr, bp);
555 break;
556 case F_CHAR:
557 printf(pr->fmt, *bp);
558 break;
559 case F_DBL:{
560 double dval;
561 float fval;
562
563 switch (pr->bcnt) {
564 case 4:
565 bcopy((char *) bp, (char *) &fval,
566 sizeof(fval));
567 printf(pr->fmt, fval);
568 break;
569 case 8:
570 bcopy((char *) bp, (char *) &dval,
571 sizeof(dval));
572 printf(pr->fmt, dval);
573 break;
574 }
575 break;
576 }
577 case F_INT:{
578 int ival;
579 short sval;
580
581 switch (pr->bcnt) {
582 case 1:
583 printf(pr->fmt, (int) *bp);
584 break;
585 case 2:
586 bcopy((char *) bp, (char *) &sval,
587 sizeof(sval));
588 printf(pr->fmt, (int) sval);
589 break;
590 case 4:
591 bcopy((char *) bp, (char *) &ival,
592 sizeof(ival));
593 printf(pr->fmt, ival);
594 break;
595 }
596 break;
597 }
598 case F_P:
599 printf(pr->fmt, isprint(*bp) ? *bp : '.');
600 break;
601 case F_STR:
602 printf(pr->fmt, (char *) bp);
603 break;
604 case F_TEXT:
605 printf(pr->fmt);
606 break;
607 case F_U:
608 conv_u(pr, bp);
609 break;
610 case F_UINT:{
611 unsigned int ival;
612 unsigned short sval;
613
614 switch (pr->bcnt) {
615 case 1:
616 printf(pr->fmt, (unsigned int) * bp);
617 break;
618 case 2:
619 bcopy((char *) bp, (char *) &sval,
620 sizeof(sval));
621 printf(pr->fmt, (unsigned int) sval);
622 break;
623 case 4:
624 bcopy((char *) bp, (char *) &ival,
625 sizeof(ival));
626 printf(pr->fmt, ival);
627 break;
628 }
629 break;
630 }
631 }
632 if (cnt == 1 && pr->nospace) {
633 *pr->nospace = savech;
634 }
635 }
636 }
637 }
638 }
639 }
640 if (endfu) {
641 /*
642 * if eaddress not set, error or file bb_dump_size was multiple of
643 * bb_dump_blocksize, and no partial block ever found.
644 */
645 if (!eaddress) {
646 if (!address) {
647 return;
648 }
649 eaddress = address;
650 }
651 for (pr = endfu->nextpr; pr; pr = pr->nextpr) {
652 switch (pr->flags) {
653 case F_ADDRESS:
654 (void) printf(pr->fmt, (unsigned int) eaddress);
655 break;
656 case F_TEXT:
657 (void) printf(pr->fmt);
658 break;
659 }
660 }
661 }
662}
663
664int bb_dump_dump(char **argv)
665{
666 register FS *tfs;
667
668 /* figure out the data block bb_dump_size */
669 for (bb_dump_blocksize = 0, tfs = bb_dump_fshead; tfs; tfs = tfs->nextfs) {
670 tfs->bcnt = bb_dump_size(tfs);
671 if (bb_dump_blocksize < tfs->bcnt) {
672 bb_dump_blocksize = tfs->bcnt;
673 }
674 }
675 /* rewrite the rules, do syntax checking */
676 for (tfs = bb_dump_fshead; tfs; tfs = tfs->nextfs) {
677 rewrite(tfs);
678 }
679
680 next(argv);
681 display();
682
683 return (exitval);
684}
685
686void bb_dump_add(const char *fmt)
687{
688 register const char *p;
689 register char *p1;
690 register char *p2;
691 static FS **nextfs;
692 FS *tfs;
693 FU *tfu, **nextfu;
694 const char *savep;
695
696 /* start new linked list of format units */
697 /* NOSTRICT */
698 tfs = (FS *) xcalloc(1,sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */
699 if (!bb_dump_fshead) {
700 bb_dump_fshead = tfs;
701 } else {
702 *nextfs = tfs;
703 }
704 nextfs = &tfs->nextfs;
705 nextfu = &tfs->nextfu;
706
707 /* take the format string and break it up into format units */
708 for (p = fmt;;) {
709 /* bb_dump_skip leading white space */
710 p = bb_skip_whitespace(p);
711 if (!*p) {
712 break;
713 }
714
715 /* allocate a new format unit and link it in */
716 /* NOSTRICT */
717 /* DBU:[dave@cray.com] calloc so that forward pointers start out NULL */
718 tfu = (FU *) xcalloc(1,sizeof(FU));
719 *nextfu = tfu;
720 nextfu = &tfu->nextfu;
721 tfu->reps = 1;
722
723 /* if leading digit, repetition count */
724 if (isdigit(*p)) {
725 for (savep = p; isdigit(*p); ++p);
726 if (!isspace(*p) && *p != '/') {
727 bb_error_msg_and_die("bad format {%s}", fmt);
728 }
729 /* may overwrite either white space or slash */
730 tfu->reps = atoi(savep);
731 tfu->flags = F_SETREP;
732 /* bb_dump_skip trailing white space */
733 p = bb_skip_whitespace(++p);
734 }
735
736 /* bb_dump_skip slash and trailing white space */
737 if (*p == '/') {
738 p = bb_skip_whitespace(++p);
739 }
740
741 /* byte count */
742 if (isdigit(*p)) {
743 for (savep = p; isdigit(*p); ++p);
744 if (!isspace(*p)) {
745 bb_error_msg_and_die("bad format {%s}", fmt);
746 }
747 tfu->bcnt = atoi(savep);
748 /* bb_dump_skip trailing white space */
749 p = bb_skip_whitespace(++p);
750 }
751
752 /* format */
753 if (*p != '"') {
754 bb_error_msg_and_die("bad format {%s}", fmt);
755 }
756 for (savep = ++p; *p != '"';) {
757 if (*p++ == 0) {
758 bb_error_msg_and_die("bad format {%s}", fmt);
759 }
760 }
761 tfu->fmt = xmalloc(p - savep + 1);
762 strncpy(tfu->fmt, savep, p - savep);
763 tfu->fmt[p - savep] = '\0';
764/* escape(tfu->fmt); */
765
766 p1 = tfu->fmt;
767
768 /* alphabetic escape sequences have to be done in place */
769 for (p2 = p1;; ++p1, ++p2) {
770 if (!*p1) {
771 *p2 = *p1;
772 break;
773 }
774 if (*p1 == '\\') {
775 const char *cs = conv_str + 4;
776 ++p1;
777 *p2 = *p1;
778 do {
779 if (*p1 == cs[2]) {
780 *p2 = cs[0];
781 break;
782 }
783 cs += 4;
784 } while (*cs);
785 }
786 }
787
788 p++;
789 }
790}
791
792/*
793 * Copyright (c) 1989 The Regents of the University of California.
794 * All rights reserved.
795 *
796 * Redistribution and use in source and binary forms, with or without
797 * modification, are permitted provided that the following conditions
798 * are met:
799 * 1. Redistributions of source code must retain the above copyright
800 * notice, this list of conditions and the following disclaimer.
801 * 2. Redistributions in binary form must reproduce the above copyright
802 * notice, this list of conditions and the following disclaimer in the
803 * documentation and/or other materials provided with the distribution.
804 * 3. Neither the name of the University nor the names of its contributors
805 * may be used to endorse or promote products derived from this software
806 * without specific prior written permission.
807 *
808 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
809 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
810 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
811 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
812 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
813 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
814 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
815 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
816 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
817 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
818 * SUCH DAMAGE.
819 */
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <errno.h>
24#include <string.h>
25#include <stdlib.h>
26#include "libbb.h"
27
28extern void bb_error_msg(const char *s, ...)
29{
30 va_list p;
31
32 va_start(p, s);
33 bb_verror_msg(s, p);
34 va_end(p);
35 putc('\n', stderr);
36}
37
38
39/* END CODE */
40/*
41Local Variables:
42c-file-style: "linux"
43c-basic-offset: 4
44tab-width: 4
45End:
46*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <errno.h>
24#include <string.h>
25#include <stdlib.h>
26#include "libbb.h"
27
28extern void bb_error_msg_and_die(const char *s, ...)
29{
30 va_list p;
31
32 va_start(p, s);
33 bb_verror_msg(s, p);
34 va_end(p);
35 putc('\n', stderr);
36 exit(bb_default_error_retval);
37}
38
39
40/* END CODE */
41/*
42Local Variables:
43c-file-style: "linux"
44c-basic-offset: 4
45tab-width: 4
46End:
47*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * fclose_nonstdin implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* A number of standard utilities can accept multiple command line args
24 * of '-' for stdin, according to SUSv3. So we encapsulate the check
25 * here to save a little space.
26 */
27
28#include <stdio.h>
29#include <libbb.h>
30
31int bb_fclose_nonstdin(FILE *f)
32{
33 if (f != stdin) {
34 return fclose(f);
35 }
36 return 0;
37}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * fflush_stdout_and_exit implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* Attempt to fflush(stdout), and exit with an error code if stdout is
24 * in an error state.
25 */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <libbb.h>
30
31void bb_fflush_stdout_and_exit(int retval)
32{
33 if (fflush(stdout)) {
34 retval = bb_default_error_retval;
35 }
36 exit(retval);
37}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) many different people.
6 * If you wrote this, please acknowledge your work.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include "libbb.h"
29
30/* Read up to (and including) TERMINATING_STRING from FILE and return it.
31 * Return NULL on EOF. */
32
33char *fgets_str(FILE *file, const char *terminating_string)
34{
35 char *linebuf = NULL;
36 const int term_length = strlen(terminating_string);
37 int end_string_offset;
38 int linebufsz = 0;
39 int idx = 0;
40 int ch;
41
42 while (1) {
43 ch = fgetc(file);
44 if (ch == EOF) {
45 free(linebuf);
46 return NULL;
47 }
48
49 /* grow the line buffer as necessary */
50 while (idx > linebufsz - 2) {
51 linebuf = xrealloc(linebuf, linebufsz += 1000);
52 }
53
54 linebuf[idx] = ch;
55 idx++;
56
57 /* Check for terminating string */
58 end_string_offset = idx - term_length;
59 if ((end_string_offset > 0) && (memcmp(&linebuf[end_string_offset], terminating_string, term_length) == 0)) {
60 idx -= term_length;
61 break;
62 }
63 }
64 linebuf[idx] = '\0';
65 return(linebuf);
66}
67
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <string.h>
24#include "libbb.h"
25
26
27#include <mntent.h>
28/*
29 * Given a block device, find the mount table entry if that block device
30 * is mounted.
31 *
32 * Given any other file (or directory), find the mount table entry for its
33 * filesystem.
34 */
35extern struct mntent *find_mount_point(const char *name, const char *table)
36{
37 struct stat s;
38 dev_t mountDevice;
39 FILE *mountTable;
40 struct mntent *mountEntry;
41
42 if (stat(name, &s) != 0)
43 return 0;
44
45 if ((s.st_mode & S_IFMT) == S_IFBLK)
46 mountDevice = s.st_rdev;
47 else
48 mountDevice = s.st_dev;
49
50
51 if ((mountTable = setmntent(table, "r")) == 0)
52 return 0;
53
54 while ((mountEntry = getmntent(mountTable)) != 0) {
55 if (strcmp(name, mountEntry->mnt_dir) == 0
56 || strcmp(name, mountEntry->mnt_fsname) == 0) /* String match. */
57 break;
58 if (stat(mountEntry->mnt_fsname, &s) == 0 && s.st_rdev == mountDevice) /* Match the device. */
59 break;
60 if (stat(mountEntry->mnt_dir, &s) == 0 && s.st_dev == mountDevice) /* Match the directory's mount point. */
61 break;
62 }
63 endmntent(mountTable);
64 return mountEntry;
65}
66
67
68/* END CODE */
69/*
70Local Variables:
71c-file-style: "linux"
72c-basic-offset: 4
73tab-width: 4
74End:
75*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <ctype.h>
24#include <string.h>
25#include <stdlib.h>
26#include "libbb.h"
27
28#define COMM_LEN 16 /* synchronize with size of comm in struct task_struct
29 in /usr/include/linux/sched.h */
30
31
32/* find_pid_by_name()
33 *
34 * Modified by Vladimir Oleynik for use with libbb/procps.c
35 * This finds the pid of the specified process.
36 * Currently, it's implemented by rummaging through
37 * the proc filesystem.
38 *
39 * Returns a list of all matching PIDs
40 */
41extern long* find_pid_by_name( const char* pidName)
42{
43 long* pidList;
44 int i=0;
45 procps_status_t * p;
46
47 pidList = xmalloc(sizeof(long));
48#ifdef CONFIG_SELINUX
49 while ((p = procps_scan(0, 0, NULL)) != 0) {
50#else
51 while ((p = procps_scan(0)) != 0) {
52#endif
53 if (strncmp(p->short_cmd, pidName, COMM_LEN-1) == 0) {
54 pidList=xrealloc( pidList, sizeof(long) * (i+2));
55 pidList[i++]=p->pid;
56 }
57 }
58
59 pidList[i] = i==0 ? -1 : 0;
60 return pidList;
61}
62
63/* END CODE */
64/*
65Local Variables:
66c-file-style: "linux"
67c-basic-offset: 4
68tab-width: 4
69End:
70*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <string.h>
24#include <dirent.h>
25#include <stdlib.h>
26#include "libbb.h"
27
28
29
30extern char *find_real_root_device_name(void)
31{
32 DIR *dir;
33 struct dirent *entry;
34 struct stat statBuf, rootStat;
35 char *fileName = NULL;
36 dev_t dev;
37
38 if (stat("/", &rootStat) != 0)
39 bb_perror_msg("could not stat '/'");
40 else {
41 /* This check is here in case they pass in /dev name */
42 if ((rootStat.st_mode & S_IFMT) == S_IFBLK)
43 dev = rootStat.st_rdev;
44 else
45 dev = rootStat.st_dev;
46
47 dir = opendir("/dev");
48 if (!dir)
49 bb_perror_msg("could not open '/dev'");
50 else {
51 while((entry = readdir(dir)) != NULL) {
52 const char *myname = entry->d_name;
53 /* Must skip ".." since that is "/", and so we
54 * would get a false positive on ".." */
55 if (myname[0] == '.' && myname[1] == '.' && !myname[2])
56 continue;
57#ifdef CONFIG_FEATURE_DEVFS
58 /* if there is a link named /dev/root skip that too */
59 if (strcmp(myname, "root")==0)
60 continue;
61#endif
62 fileName = concat_path_file("/dev", myname);
63
64 /* Some char devices have the same dev_t as block
65 * devices, so make sure this is a block device */
66 if (stat(fileName, &statBuf) == 0 &&
67 S_ISBLK(statBuf.st_mode)!=0 &&
68 statBuf.st_rdev == dev)
69 break;
70 free(fileName);
71 fileName=NULL;
72 }
73 closedir(dir);
74 }
75 }
76 if(fileName==NULL)
77 fileName = bb_xstrdup("/dev/root");
78 return fileName;
79}
80
81
82/* END CODE */
83/*
84Local Variables:
85c-file-style: "linux"
86c-basic-offset: 4
87tab-width: 4
88End:
89*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <unistd.h>
24#include "libbb.h"
25
26/*
27 * Read all of the supplied buffer from a file.
28 * This does multiple reads as necessary.
29 * Returns the amount read, or -1 on an error.
30 * A short read is returned on an end of file.
31 */
32ssize_t bb_full_read(int fd, void *buf, size_t len)
33{
34 ssize_t cc;
35 ssize_t total;
36
37 total = 0;
38
39 while (len > 0) {
40 cc = safe_read(fd, buf, len);
41
42 if (cc < 0)
43 return cc; /* read() returns -1 on failure. */
44
45 if (cc == 0)
46 break;
47
48 buf = ((char *)buf) + cc;
49 total += cc;
50 len -= cc;
51 }
52
53 return total;
54}
55
56/* END CODE */
57/*
58Local Variables:
59c-file-style: "linux"
60c-basic-offset: 4
61tab-width: 4
62End:
63*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <unistd.h>
24#include "libbb.h"
25
26/*
27 * Write all of the supplied buffer out to a file.
28 * This does multiple writes as necessary.
29 * Returns the amount written, or -1 on an error.
30 */
31ssize_t bb_full_write(int fd, const void *buf, size_t len)
32{
33 ssize_t cc;
34 ssize_t total;
35
36 total = 0;
37
38 while (len > 0) {
39 cc = safe_write(fd, buf, len);
40
41 if (cc < 0)
42 return cc; /* write() returns -1 on failure. */
43
44 total += cc;
45 buf = ((const char *)buf) + cc;
46 len -= cc;
47 }
48
49 return total;
50}
51
52
53/* END CODE */
54/*
55Local Variables:
56c-file-style: "linux"
57c-basic-offset: 4
58tab-width: 4
59End:
60*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) many different people. If you wrote this, please
6 * acknowledge your work.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include <stdio.h>
24#include <errno.h>
25#include <fcntl.h>
26#include <unistd.h>
27#include <sys/ioctl.h>
28#include "libbb.h"
29
30
31
32/* From <linux/kd.h> */
33static const int KDGKBTYPE = 0x4B33; /* get keyboard type */
34
35
36static int open_a_console(const char *fnam)
37{
38 int fd;
39
40 /* try read-write */
41 fd = open(fnam, O_RDWR);
42
43 /* if failed, try read-only */
44 if (fd < 0 && errno == EACCES)
45 fd = open(fnam, O_RDONLY);
46
47 /* if failed, try write-only */
48 if (fd < 0 && errno == EACCES)
49 fd = open(fnam, O_WRONLY);
50
51 return fd;
52}
53
54/*
55 * Get an fd for use with kbd/console ioctls.
56 * We try several things because opening /dev/console will fail
57 * if someone else used X (which does a chown on /dev/console).
58 */
59
60int get_console_fd(void)
61{
62 int fd;
63
64 static const char * const choise_console_names[] = {
65 CONSOLE_DEV, CURRENT_VC, CURRENT_TTY
66 };
67
68 for (fd = 2; fd >= 0; fd--) {
69 int fd4name;
70 int choise_fd;
71 char arg;
72
73 fd4name = open_a_console(choise_console_names[fd]);
74 chk_std:
75 choise_fd = fd4name >= 0 ? fd4name : fd;
76
77 arg = 0;
78 if (ioctl(choise_fd, KDGKBTYPE, &arg) == 0)
79 return choise_fd;
80 if(fd4name >= 0) {
81 close(fd4name);
82 fd4name = -1;
83 goto chk_std;
84 }
85 }
86
87 bb_error_msg("Couldn't get a file descriptor referring to the console");
88 return fd; /* total failure */
89}
90
91
92/* END CODE */
93/*
94Local Variables:
95c-file-style: "linux"
96c-basic-offset: 4
97tab-width: 4
98End:
99*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * bb_get_last_path_component implementation for busybox
4 *
5 * Copyright (C) 2001 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* Set to 1 if you want basename() behavior for NULL or "". */
24/* WARNING!!! Doing so will break basename applet at least! */
25#define EMULATE_BASENAME 0
26
27char *bb_get_last_path_component(char *path)
28{
29#if EMULATE_BASENAME
30 static const char null_or_empty[] = ".";
31#endif
32 char *first = path;
33 char *last;
34
35#if EMULATE_BASENAME
36 if (!path || !*path) {
37 return (char *) null_or_empty;
38 }
39#endif
40
41 last = path - 1;
42
43 while (*path) {
44 if ((*path != '/') && (path > ++last)) {
45 last = first = path;
46 }
47 ++path;
48 }
49
50 if (*first == '/') {
51 last = first;
52 }
53 last[1] = 0;
54
55 return first;
56}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) many different people.
6 * If you wrote this, please acknowledge your work.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include "libbb.h"
26
27/* get_line_from_file() - This function reads an entire line from a text file,
28 * up to a newline. It returns a malloc'ed char * which must be stored and
29 * free'ed by the caller. If 'c' is nonzero, the trailing '\n' (if any)
30 * is removed. In event of a read error or EOF, NULL is returned. */
31
32static char *private_get_line_from_file(FILE *file, int c)
33{
34#define GROWBY (80) /* how large we will grow strings by */
35
36 int ch;
37 int idx = 0;
38 char *linebuf = NULL;
39 int linebufsz = 0;
40
41 while ((ch = getc(file)) != EOF) {
42 /* grow the line buffer as necessary */
43 if (idx > linebufsz - 2) {
44 linebuf = xrealloc(linebuf, linebufsz += GROWBY);
45 }
46 linebuf[idx++] = (char)ch;
47 if (ch == '\n' || ch == '\0') {
48 if (c) {
49 --idx;
50 }
51 break;
52 }
53 }
54 if (linebuf) {
55 if (ferror(file)) {
56 free(linebuf);
57 return NULL;
58 }
59 linebuf[idx] = 0;
60 }
61 return linebuf;
62}
63
64extern char *bb_get_line_from_file(FILE *file)
65{
66 return private_get_line_from_file(file, 0);
67}
68
69extern char *bb_get_chomped_line_from_file(FILE *file)
70{
71 return private_get_line_from_file(file, 1);
72}
73
74
75/* END CODE */
76/*
77Local Variables:
78c-file-style: "linux"
79c-basic-offset: 4
80tab-width: 4
81End:
82*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Determine the width and height of the terminal.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <errno.h>
24#include <fcntl.h>
25#include <unistd.h>
26#include <unistd.h>
27#include <termios.h>
28#include <sys/ioctl.h>
29#include "busybox.h"
30
31/* It is perfectly ok to pass in a NULL for either width or for
32 * height, in which case that value will not be set. It is also
33 * perfectly ok to have CONFIG_FEATURE_AUTOWIDTH disabled, in
34 * which case you will always get 80x24 */
35void get_terminal_width_height(int fd, int *width, int *height)
36{
37 struct winsize win = { 0, 0, 0, 0 };
38#ifdef CONFIG_FEATURE_AUTOWIDTH
39 if (ioctl(fd, TIOCGWINSZ, &win) != 0) {
40 win.ws_row = 24;
41 win.ws_col = 80;
42 }
43#endif
44 if (win.ws_row <= 1) {
45 win.ws_row = 24;
46 }
47 if (win.ws_col <= 1) {
48 win.ws_col = 80;
49 }
50 if (height) {
51 *height = (int) win.ws_row;
52 }
53 if (width) {
54 *width = (int) win.ws_col;
55 }
56}
57
58/* END CODE */
59/*
60Local Variables:
61c-file-style: "linux"
62c-basic-offset: 4
63tab-width: 4
64End:
65*/
66
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 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Library General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 */
16
17#include <stdlib.h>
18
19extern unsigned long get_ug_id(const char *s, long (*my_getxxnam)(const char *))
20{
21 unsigned long r;
22 char *p;
23
24 r = strtoul(s, &p, 10);
25 if (*p || (s == p)) {
26 r = my_getxxnam(s);
27 }
28
29 return r;
30}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * universal getopt_ulflags implementation for busybox
4 *
5 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <getopt.h>
24#include <string.h>
25#include <assert.h>
26#include <stdlib.h>
27#include "libbb.h"
28
29/*
30You can set bb_opt_complementaly as string with one or more
31complementaly or incongruously options.
32If sequential founded option haved from this string
33then your incongruously pairs unsets and complementaly make add sets.
34Format:
35one char - option for check,
36chars - complementaly option for add sets.
37- chars - option triggered for unsets.
38~ chars - option incongruously.
39* - option list, called add_to_list(*ptr_from_usaged, optarg)
40: - separator.
41Example: du applet can have options "-s" and "-d size"
42If getopt found -s then -d option flag unset or if found -d then -s unset.
43For this result you must set bb_opt_complementaly = "s-d:d-s".
44Result have last option flag only from called arguments.
45Warning! You can check returned flag, pointer to "d:" argument seted
46to own optarg always.
47Example two: cut applet must only one type of list may be specified,
48and -b, -c and -f incongruously option, overwited option is error also.
49You must set bb_opt_complementaly = "b~cf:c~bf:f~bc".
50If called have more one specified, return value have error flag -
51high bite set (0x80000000UL).
52Example three: grep applet can have one or more "-e pattern" arguments.
53You should use bb_getopt_ulflags() as
54llist_t *paterns;
55bb_opt_complementaly = "e*";
56bb_getopt_ulflags (argc, argv, "e:", &paterns);
57*/
58
59const char *bb_opt_complementaly;
60
61typedef struct
62{
63 unsigned char opt;
64 char list_flg;
65 unsigned long switch_on;
66 unsigned long switch_off;
67 unsigned long incongruously;
68 void **optarg; /* char **optarg or llist_t **optarg */
69} t_complementaly;
70
71/* You can set bb_applet_long_options for parse called long options */
72
73static const struct option bb_default_long_options[] = {
74 /* { "help", 0, NULL, '?' }, */
75 { 0, 0, 0, 0 }
76};
77
78const struct option *bb_applet_long_options = bb_default_long_options;
79
80
81unsigned long
82bb_getopt_ulflags (int argc, char **argv, const char *applet_opts, ...)
83{
84 unsigned long flags = 0;
85 t_complementaly complementaly[sizeof(flags) * 8 + 1];
86 int c;
87 const unsigned char *s;
88 t_complementaly *on_off;
89 va_list p;
90
91 va_start (p, applet_opts);
92
93 /* skip GNU extension */
94 s = applet_opts;
95 if(*s == '+' || *s == '-')
96 s++;
97
98 c = 0;
99 on_off = complementaly;
100 for (; *s; s++) {
101 if(c >= (sizeof(flags)*8))
102 break;
103 on_off->opt = *s;
104 on_off->switch_on = (1 << c);
105 on_off->list_flg = 0;
106 on_off->switch_off = 0;
107 on_off->incongruously = 0;
108 on_off->optarg = NULL;
109 if (s[1] == ':') {
110 on_off->optarg = va_arg (p, void **);
111 do
112 s++;
113 while (s[1] == ':');
114 }
115 on_off++;
116 c++;
117 }
118 on_off->opt = 0;
119 c = 0;
120 for (s = bb_opt_complementaly; s && *s; s++) {
121 t_complementaly *pair;
122
123 if (*s == ':') {
124 c = 0;
125 continue;
126 }
127 if (c)
128 continue;
129 for (on_off = complementaly; on_off->opt; on_off++)
130 if (on_off->opt == *s)
131 break;
132 pair = on_off;
133 for(s++; *s && *s != ':'; s++) {
134 if (*s == '-' || *s == '~') {
135 c = *s;
136 } else if(*s == '*') {
137 pair->list_flg++;
138 } else {
139 unsigned long *pair_switch = &(pair->switch_on);
140
141 if(c)
142 pair_switch = c == '-' ? &(pair->switch_off) : &(pair->incongruously);
143 for (on_off = complementaly; on_off->opt; on_off++)
144 if (on_off->opt == *s) {
145 *pair_switch |= on_off->switch_on;
146 break;
147 }
148 }
149 }
150 s--;
151 }
152
153 while ((c = getopt_long (argc, argv, applet_opts,
154 bb_applet_long_options, NULL)) > 0) {
155 for (on_off = complementaly; on_off->opt != c; on_off++) {
156 if(!on_off->opt)
157 bb_show_usage ();
158 }
159 if(flags & on_off->incongruously)
160 flags |= 0x80000000UL;
161 flags &= ~on_off->switch_off;
162 flags |= on_off->switch_on;
163 if(on_off->list_flg) {
164 *(llist_t **)(on_off->optarg) =
165 llist_add_to(*(llist_t **)(on_off->optarg), optarg);
166 } else if (on_off->optarg) {
167 *(char **)(on_off->optarg) = optarg;
168 }
169 }
170 return flags;
171}
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 @@
1/*
2 * Based on shasum from http://www.netsw.org/crypto/hash/
3 * Majorly hacked up to use Dr Brian Gladman's sha1 code
4 *
5 * Copyright (C) 2003 Glenn L. McGrath
6 * Copyright (C) 2003 Erik Andersen
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 */
22
23#include <byteswap.h>
24#include <endian.h>
25#include <fcntl.h>
26#include <limits.h>
27#include <stdio.h>
28#include <stdint.h>
29#include <stdlib.h>
30#include <string.h>
31#include <unistd.h>
32
33#include "busybox.h"
34
35
36#ifdef CONFIG_SHA1SUM
37/*
38 ---------------------------------------------------------------------------
39 Begin Dr. Gladman's sha1 code
40 ---------------------------------------------------------------------------
41*/
42
43/*
44 ---------------------------------------------------------------------------
45 Copyright (c) 2002, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
46 All rights reserved.
47
48 LICENSE TERMS
49
50 The free distribution and use of this software in both source and binary
51 form is allowed (with or without changes) provided that:
52
53 1. distributions of this source code include the above copyright
54 notice, this list of conditions and the following disclaimer;
55
56 2. distributions in binary form include the above copyright
57 notice, this list of conditions and the following disclaimer
58 in the documentation and/or other associated materials;
59
60 3. the copyright holder's name is not used to endorse products
61 built using this software without specific written permission.
62
63 ALTERNATIVELY, provided that this notice is retained in full, this product
64 may be distributed under the terms of the GNU General Public License (GPL),
65 in which case the provisions of the GPL apply INSTEAD OF those given above.
66
67 DISCLAIMER
68
69 This software is provided 'as is' with no explicit or implied warranties
70 in respect of its properties, including, but not limited to, correctness
71 and/or fitness for purpose.
72 ---------------------------------------------------------------------------
73 Issue Date: 10/11/2002
74
75 This is a byte oriented version of SHA1 that operates on arrays of bytes
76 stored in memory. It runs at 22 cycles per byte on a Pentium P4 processor
77*/
78
79# define SHA1_BLOCK_SIZE 64
80# define SHA1_DIGEST_SIZE 20
81# define SHA1_HASH_SIZE SHA1_DIGEST_SIZE
82# define SHA2_GOOD 0
83# define SHA2_BAD 1
84
85# define rotl32(x,n) (((x) << n) | ((x) >> (32 - n)))
86
87# if __BYTE_ORDER == __BIG_ENDIAN
88# define swap_b32(x) (x)
89# elif defined(bswap_32)
90# define swap_b32(x) bswap_32(x)
91# else
92# define swap_b32(x) ((rotl32((x), 8) & 0x00ff00ff) | (rotl32((x), 24) & 0xff00ff00))
93# endif /* __BYTE_ORDER */
94
95# define SHA1_MASK (SHA1_BLOCK_SIZE - 1)
96
97/* reverse byte order in 32-bit words */
98#define ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z))))
99#define parity(x,y,z) ((x) ^ (y) ^ (z))
100#define maj(x,y,z) (((x) & (y)) | ((z) & ((x) | (y))))
101
102/* A normal version as set out in the FIPS. This version uses */
103/* partial loop unrolling and is optimised for the Pentium 4 */
104# define rnd(f,k) \
105 t = a; a = rotl32(a,5) + f(b,c,d) + e + k + w[i]; \
106 e = d; d = c; c = rotl32(b, 30); b = t
107
108/* type to hold the SHA1 context */
109struct sha1_ctx_t {
110 uint32_t count[2];
111 uint32_t hash[5];
112 uint32_t wbuf[16];
113};
114
115static void sha1_compile(struct sha1_ctx_t *ctx)
116{
117 uint32_t w[80], i, a, b, c, d, e, t;
118
119 /* note that words are compiled from the buffer into 32-bit */
120 /* words in big-endian order so an order reversal is needed */
121 /* here on little endian machines */
122 for (i = 0; i < SHA1_BLOCK_SIZE / 4; ++i)
123 w[i] = swap_b32(ctx->wbuf[i]);
124
125 for (i = SHA1_BLOCK_SIZE / 4; i < 80; ++i)
126 w[i] = rotl32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
127
128 a = ctx->hash[0];
129 b = ctx->hash[1];
130 c = ctx->hash[2];
131 d = ctx->hash[3];
132 e = ctx->hash[4];
133
134 for (i = 0; i < 20; ++i) {
135 rnd(ch, 0x5a827999);
136 }
137
138 for (i = 20; i < 40; ++i) {
139 rnd(parity, 0x6ed9eba1);
140 }
141
142 for (i = 40; i < 60; ++i) {
143 rnd(maj, 0x8f1bbcdc);
144 }
145
146 for (i = 60; i < 80; ++i) {
147 rnd(parity, 0xca62c1d6);
148 }
149
150 ctx->hash[0] += a;
151 ctx->hash[1] += b;
152 ctx->hash[2] += c;
153 ctx->hash[3] += d;
154 ctx->hash[4] += e;
155}
156
157static void sha1_begin(struct sha1_ctx_t *ctx)
158{
159 ctx->count[0] = ctx->count[1] = 0;
160 ctx->hash[0] = 0x67452301;
161 ctx->hash[1] = 0xefcdab89;
162 ctx->hash[2] = 0x98badcfe;
163 ctx->hash[3] = 0x10325476;
164 ctx->hash[4] = 0xc3d2e1f0;
165}
166
167/* SHA1 hash data in an array of bytes into hash buffer and call the */
168/* hash_compile function as required. */
169static void sha1_hash(const void *data, size_t len, void *ctx_v)
170{
171 struct sha1_ctx_t *ctx = (struct sha1_ctx_t *) ctx_v;
172 uint32_t pos = (uint32_t) (ctx->count[0] & SHA1_MASK);
173 uint32_t freeb = SHA1_BLOCK_SIZE - pos;
174 const unsigned char *sp = data;
175
176 if ((ctx->count[0] += len) < len)
177 ++(ctx->count[1]);
178
179 while (len >= freeb) { /* tranfer whole blocks while possible */
180 memcpy(((unsigned char *) ctx->wbuf) + pos, sp, freeb);
181 sp += freeb;
182 len -= freeb;
183 freeb = SHA1_BLOCK_SIZE;
184 pos = 0;
185 sha1_compile(ctx);
186 }
187
188 memcpy(((unsigned char *) ctx->wbuf) + pos, sp, len);
189}
190
191/* SHA1 Final padding and digest calculation */
192# if __BYTE_ORDER == __LITTLE_ENDIAN
193static uint32_t mask[4] = { 0x00000000, 0x000000ff, 0x0000ffff, 0x00ffffff };
194static uint32_t bits[4] = { 0x00000080, 0x00008000, 0x00800000, 0x80000000 };
195# else
196static uint32_t mask[4] = { 0x00000000, 0xff000000, 0xffff0000, 0xffffff00 };
197static uint32_t bits[4] = { 0x80000000, 0x00800000, 0x00008000, 0x00000080 };
198# endif /* __BYTE_ORDER */
199
200void sha1_end(unsigned char hval[], struct sha1_ctx_t *ctx)
201{
202 uint32_t i, cnt = (uint32_t) (ctx->count[0] & SHA1_MASK);
203
204 /* mask out the rest of any partial 32-bit word and then set */
205 /* the next byte to 0x80. On big-endian machines any bytes in */
206 /* the buffer will be at the top end of 32 bit words, on little */
207 /* endian machines they will be at the bottom. Hence the AND */
208 /* and OR masks above are reversed for little endian systems */
209 ctx->wbuf[cnt >> 2] =
210 (ctx->wbuf[cnt >> 2] & mask[cnt & 3]) | bits[cnt & 3];
211
212 /* we need 9 or more empty positions, one for the padding byte */
213 /* (above) and eight for the length count. If there is not */
214 /* enough space pad and empty the buffer */
215 if (cnt > SHA1_BLOCK_SIZE - 9) {
216 if (cnt < 60)
217 ctx->wbuf[15] = 0;
218 sha1_compile(ctx);
219 cnt = 0;
220 } else /* compute a word index for the empty buffer positions */
221 cnt = (cnt >> 2) + 1;
222
223 while (cnt < 14) /* and zero pad all but last two positions */
224 ctx->wbuf[cnt++] = 0;
225
226 /* assemble the eight byte counter in the buffer in big-endian */
227 /* format */
228
229 ctx->wbuf[14] = swap_b32((ctx->count[1] << 3) | (ctx->count[0] >> 29));
230 ctx->wbuf[15] = swap_b32(ctx->count[0] << 3);
231
232 sha1_compile(ctx);
233
234 /* extract the hash value as bytes in case the hash buffer is */
235 /* misaligned for 32-bit words */
236
237 for (i = 0; i < SHA1_DIGEST_SIZE; ++i)
238 hval[i] = (unsigned char) (ctx->hash[i >> 2] >> 8 * (~i & 3));
239}
240
241/*
242 ---------------------------------------------------------------------------
243 End of Dr. Gladman's sha1 code
244 ---------------------------------------------------------------------------
245*/
246#endif /* CONFIG_SHA1 */
247
248
249
250
251
252#ifdef CONFIG_MD5SUM
253/*
254 * md5sum.c - Compute MD5 checksum of files or strings according to the
255 * definition of MD5 in RFC 1321 from April 1992.
256 *
257 * Copyright (C) 1995-1999 Free Software Foundation, Inc.
258 * Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
259 *
260 *
261 * June 29, 2001 Manuel Novoa III
262 *
263 * Added MD5SUM_SIZE_VS_SPEED configuration option.
264 *
265 * Current valid values, with data from my system for comparison, are:
266 * (using uClibc and running on linux-2.4.4.tar.bz2)
267 * user times (sec) text size (386)
268 * 0 (fastest) 1.1 6144
269 * 1 1.4 5392
270 * 2 3.0 5088
271 * 3 (smallest) 5.1 4912
272 */
273
274# define MD5SUM_SIZE_VS_SPEED 2
275
276/* Handle endian-ness */
277# if __BYTE_ORDER == __LITTLE_ENDIAN
278# define SWAP(n) (n)
279# elif defined(bswap_32)
280# define SWAP(n) bswap_32(n)
281# else
282# define SWAP(n) ((n << 24) | ((n&65280)<<8) | ((n&16711680)>>8) | (n>>24))
283# endif
284
285# if MD5SUM_SIZE_VS_SPEED == 0
286/* This array contains the bytes used to pad the buffer to the next
287 64-byte boundary. (RFC 1321, 3.1: Step 1) */
288static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
289# endif /* MD5SUM_SIZE_VS_SPEED == 0 */
290
291/* Structure to save state of computation between the single steps. */
292struct md5_ctx_t {
293 uint32_t A;
294 uint32_t B;
295 uint32_t C;
296 uint32_t D;
297 uint32_t total[2];
298 uint32_t buflen;
299 char buffer[128];
300};
301
302/* Initialize structure containing state of computation.
303 * (RFC 1321, 3.3: Step 3)
304 */
305static void md5_begin(struct md5_ctx_t *ctx)
306{
307 ctx->A = 0x67452301;
308 ctx->B = 0xefcdab89;
309 ctx->C = 0x98badcfe;
310 ctx->D = 0x10325476;
311
312 ctx->total[0] = ctx->total[1] = 0;
313 ctx->buflen = 0;
314}
315
316/* These are the four functions used in the four steps of the MD5 algorithm
317 * and defined in the RFC 1321. The first function is a little bit optimized
318 * (as found in Colin Plumbs public domain implementation).
319 * #define FF(b, c, d) ((b & c) | (~b & d))
320 */
321# define FF(b, c, d) (d ^ (b & (c ^ d)))
322# define FG(b, c, d) FF (d, b, c)
323# define FH(b, c, d) (b ^ c ^ d)
324# define FI(b, c, d) (c ^ (b | ~d))
325
326/* Starting with the result of former calls of this function (or the
327 * initialization function update the context for the next LEN bytes
328 * starting at BUFFER.
329 * It is necessary that LEN is a multiple of 64!!!
330 */
331static void md5_hash_block(const void *buffer, size_t len, struct md5_ctx_t *ctx)
332{
333 uint32_t correct_words[16];
334 const uint32_t *words = buffer;
335 size_t nwords = len / sizeof(uint32_t);
336 const uint32_t *endp = words + nwords;
337
338# if MD5SUM_SIZE_VS_SPEED > 0
339 static const uint32_t C_array[] = {
340 /* round 1 */
341 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
342 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
343 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
344 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
345 /* round 2 */
346 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
347 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8,
348 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
349 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
350 /* round 3 */
351 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
352 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
353 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
354 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
355 /* round 4 */
356 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
357 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
358 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
359 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
360 };
361
362 static const char P_array[] = {
363# if MD5SUM_SIZE_VS_SPEED > 1
364 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */
365# endif /* MD5SUM_SIZE_VS_SPEED > 1 */
366 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */
367 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */
368 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */
369 };
370
371# if MD5SUM_SIZE_VS_SPEED > 1
372 static const char S_array[] = {
373 7, 12, 17, 22,
374 5, 9, 14, 20,
375 4, 11, 16, 23,
376 6, 10, 15, 21
377 };
378# endif /* MD5SUM_SIZE_VS_SPEED > 1 */
379# endif
380
381 uint32_t A = ctx->A;
382 uint32_t B = ctx->B;
383 uint32_t C = ctx->C;
384 uint32_t D = ctx->D;
385
386 /* First increment the byte count. RFC 1321 specifies the possible
387 length of the file up to 2^64 bits. Here we only compute the
388 number of bytes. Do a double word increment. */
389 ctx->total[0] += len;
390 if (ctx->total[0] < len)
391 ++ctx->total[1];
392
393 /* Process all bytes in the buffer with 64 bytes in each round of
394 the loop. */
395 while (words < endp) {
396 uint32_t *cwp = correct_words;
397 uint32_t A_save = A;
398 uint32_t B_save = B;
399 uint32_t C_save = C;
400 uint32_t D_save = D;
401
402# if MD5SUM_SIZE_VS_SPEED > 1
403# define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
404
405 const uint32_t *pc;
406 const char *pp;
407 const char *ps;
408 int i;
409 uint32_t temp;
410
411 for (i = 0; i < 16; i++) {
412 cwp[i] = SWAP(words[i]);
413 }
414 words += 16;
415
416# if MD5SUM_SIZE_VS_SPEED > 2
417 pc = C_array;
418 pp = P_array;
419 ps = S_array - 4;
420
421 for (i = 0; i < 64; i++) {
422 if ((i & 0x0f) == 0)
423 ps += 4;
424 temp = A;
425 switch (i >> 4) {
426 case 0:
427 temp += FF(B, C, D);
428 break;
429 case 1:
430 temp += FG(B, C, D);
431 break;
432 case 2:
433 temp += FH(B, C, D);
434 break;
435 case 3:
436 temp += FI(B, C, D);
437 }
438 temp += cwp[(int) (*pp++)] + *pc++;
439 CYCLIC(temp, ps[i & 3]);
440 temp += B;
441 A = D;
442 D = C;
443 C = B;
444 B = temp;
445 }
446# else
447 pc = C_array;
448 pp = P_array;
449 ps = S_array;
450
451 for (i = 0; i < 16; i++) {
452 temp = A + FF(B, C, D) + cwp[(int) (*pp++)] + *pc++;
453 CYCLIC(temp, ps[i & 3]);
454 temp += B;
455 A = D;
456 D = C;
457 C = B;
458 B = temp;
459 }
460
461 ps += 4;
462 for (i = 0; i < 16; i++) {
463 temp = A + FG(B, C, D) + cwp[(int) (*pp++)] + *pc++;
464 CYCLIC(temp, ps[i & 3]);
465 temp += B;
466 A = D;
467 D = C;
468 C = B;
469 B = temp;
470 }
471 ps += 4;
472 for (i = 0; i < 16; i++) {
473 temp = A + FH(B, C, D) + cwp[(int) (*pp++)] + *pc++;
474 CYCLIC(temp, ps[i & 3]);
475 temp += B;
476 A = D;
477 D = C;
478 C = B;
479 B = temp;
480 }
481 ps += 4;
482 for (i = 0; i < 16; i++) {
483 temp = A + FI(B, C, D) + cwp[(int) (*pp++)] + *pc++;
484 CYCLIC(temp, ps[i & 3]);
485 temp += B;
486 A = D;
487 D = C;
488 C = B;
489 B = temp;
490 }
491
492# endif /* MD5SUM_SIZE_VS_SPEED > 2 */
493# else
494 /* First round: using the given function, the context and a constant
495 the next context is computed. Because the algorithms processing
496 unit is a 32-bit word and it is determined to work on words in
497 little endian byte order we perhaps have to change the byte order
498 before the computation. To reduce the work for the next steps
499 we store the swapped words in the array CORRECT_WORDS. */
500
501# define OP(a, b, c, d, s, T) \
502 do \
503 { \
504 a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \
505 ++words; \
506 CYCLIC (a, s); \
507 a += b; \
508 } \
509 while (0)
510
511 /* It is unfortunate that C does not provide an operator for
512 cyclic rotation. Hope the C compiler is smart enough. */
513 /* gcc 2.95.4 seems to be --aaronl */
514# define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
515
516 /* Before we start, one word to the strange constants.
517 They are defined in RFC 1321 as
518
519 T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
520 */
521
522# if MD5SUM_SIZE_VS_SPEED == 1
523 const uint32_t *pc;
524 const char *pp;
525 int i;
526# endif /* MD5SUM_SIZE_VS_SPEED */
527
528 /* Round 1. */
529# if MD5SUM_SIZE_VS_SPEED == 1
530 pc = C_array;
531 for (i = 0; i < 4; i++) {
532 OP(A, B, C, D, 7, *pc++);
533 OP(D, A, B, C, 12, *pc++);
534 OP(C, D, A, B, 17, *pc++);
535 OP(B, C, D, A, 22, *pc++);
536 }
537# else
538 OP(A, B, C, D, 7, 0xd76aa478);
539 OP(D, A, B, C, 12, 0xe8c7b756);
540 OP(C, D, A, B, 17, 0x242070db);
541 OP(B, C, D, A, 22, 0xc1bdceee);
542 OP(A, B, C, D, 7, 0xf57c0faf);
543 OP(D, A, B, C, 12, 0x4787c62a);
544 OP(C, D, A, B, 17, 0xa8304613);
545 OP(B, C, D, A, 22, 0xfd469501);
546 OP(A, B, C, D, 7, 0x698098d8);
547 OP(D, A, B, C, 12, 0x8b44f7af);
548 OP(C, D, A, B, 17, 0xffff5bb1);
549 OP(B, C, D, A, 22, 0x895cd7be);
550 OP(A, B, C, D, 7, 0x6b901122);
551 OP(D, A, B, C, 12, 0xfd987193);
552 OP(C, D, A, B, 17, 0xa679438e);
553 OP(B, C, D, A, 22, 0x49b40821);
554# endif /* MD5SUM_SIZE_VS_SPEED == 1 */
555
556 /* For the second to fourth round we have the possibly swapped words
557 in CORRECT_WORDS. Redefine the macro to take an additional first
558 argument specifying the function to use. */
559# undef OP
560# define OP(f, a, b, c, d, k, s, T) \
561 do \
562 { \
563 a += f (b, c, d) + correct_words[k] + T; \
564 CYCLIC (a, s); \
565 a += b; \
566 } \
567 while (0)
568
569 /* Round 2. */
570# if MD5SUM_SIZE_VS_SPEED == 1
571 pp = P_array;
572 for (i = 0; i < 4; i++) {
573 OP(FG, A, B, C, D, (int) (*pp++), 5, *pc++);
574 OP(FG, D, A, B, C, (int) (*pp++), 9, *pc++);
575 OP(FG, C, D, A, B, (int) (*pp++), 14, *pc++);
576 OP(FG, B, C, D, A, (int) (*pp++), 20, *pc++);
577 }
578# else
579 OP(FG, A, B, C, D, 1, 5, 0xf61e2562);
580 OP(FG, D, A, B, C, 6, 9, 0xc040b340);
581 OP(FG, C, D, A, B, 11, 14, 0x265e5a51);
582 OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
583 OP(FG, A, B, C, D, 5, 5, 0xd62f105d);
584 OP(FG, D, A, B, C, 10, 9, 0x02441453);
585 OP(FG, C, D, A, B, 15, 14, 0xd8a1e681);
586 OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
587 OP(FG, A, B, C, D, 9, 5, 0x21e1cde6);
588 OP(FG, D, A, B, C, 14, 9, 0xc33707d6);
589 OP(FG, C, D, A, B, 3, 14, 0xf4d50d87);
590 OP(FG, B, C, D, A, 8, 20, 0x455a14ed);
591 OP(FG, A, B, C, D, 13, 5, 0xa9e3e905);
592 OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8);
593 OP(FG, C, D, A, B, 7, 14, 0x676f02d9);
594 OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
595# endif /* MD5SUM_SIZE_VS_SPEED == 1 */
596
597 /* Round 3. */
598# if MD5SUM_SIZE_VS_SPEED == 1
599 for (i = 0; i < 4; i++) {
600 OP(FH, A, B, C, D, (int) (*pp++), 4, *pc++);
601 OP(FH, D, A, B, C, (int) (*pp++), 11, *pc++);
602 OP(FH, C, D, A, B, (int) (*pp++), 16, *pc++);
603 OP(FH, B, C, D, A, (int) (*pp++), 23, *pc++);
604 }
605# else
606 OP(FH, A, B, C, D, 5, 4, 0xfffa3942);
607 OP(FH, D, A, B, C, 8, 11, 0x8771f681);
608 OP(FH, C, D, A, B, 11, 16, 0x6d9d6122);
609 OP(FH, B, C, D, A, 14, 23, 0xfde5380c);
610 OP(FH, A, B, C, D, 1, 4, 0xa4beea44);
611 OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9);
612 OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60);
613 OP(FH, B, C, D, A, 10, 23, 0xbebfbc70);
614 OP(FH, A, B, C, D, 13, 4, 0x289b7ec6);
615 OP(FH, D, A, B, C, 0, 11, 0xeaa127fa);
616 OP(FH, C, D, A, B, 3, 16, 0xd4ef3085);
617 OP(FH, B, C, D, A, 6, 23, 0x04881d05);
618 OP(FH, A, B, C, D, 9, 4, 0xd9d4d039);
619 OP(FH, D, A, B, C, 12, 11, 0xe6db99e5);
620 OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8);
621 OP(FH, B, C, D, A, 2, 23, 0xc4ac5665);
622# endif /* MD5SUM_SIZE_VS_SPEED == 1 */
623
624 /* Round 4. */
625# if MD5SUM_SIZE_VS_SPEED == 1
626 for (i = 0; i < 4; i++) {
627 OP(FI, A, B, C, D, (int) (*pp++), 6, *pc++);
628 OP(FI, D, A, B, C, (int) (*pp++), 10, *pc++);
629 OP(FI, C, D, A, B, (int) (*pp++), 15, *pc++);
630 OP(FI, B, C, D, A, (int) (*pp++), 21, *pc++);
631 }
632# else
633 OP(FI, A, B, C, D, 0, 6, 0xf4292244);
634 OP(FI, D, A, B, C, 7, 10, 0x432aff97);
635 OP(FI, C, D, A, B, 14, 15, 0xab9423a7);
636 OP(FI, B, C, D, A, 5, 21, 0xfc93a039);
637 OP(FI, A, B, C, D, 12, 6, 0x655b59c3);
638 OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92);
639 OP(FI, C, D, A, B, 10, 15, 0xffeff47d);
640 OP(FI, B, C, D, A, 1, 21, 0x85845dd1);
641 OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f);
642 OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
643 OP(FI, C, D, A, B, 6, 15, 0xa3014314);
644 OP(FI, B, C, D, A, 13, 21, 0x4e0811a1);
645 OP(FI, A, B, C, D, 4, 6, 0xf7537e82);
646 OP(FI, D, A, B, C, 11, 10, 0xbd3af235);
647 OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
648 OP(FI, B, C, D, A, 9, 21, 0xeb86d391);
649# endif /* MD5SUM_SIZE_VS_SPEED == 1 */
650# endif /* MD5SUM_SIZE_VS_SPEED > 1 */
651
652 /* Add the starting values of the context. */
653 A += A_save;
654 B += B_save;
655 C += C_save;
656 D += D_save;
657 }
658
659 /* Put checksum in context given as argument. */
660 ctx->A = A;
661 ctx->B = B;
662 ctx->C = C;
663 ctx->D = D;
664}
665
666/* Starting with the result of former calls of this function (or the
667 * initialization function update the context for the next LEN bytes
668 * starting at BUFFER.
669 * It is NOT required that LEN is a multiple of 64.
670 */
671
672static void md5_hash_bytes(const void *buffer, size_t len, struct md5_ctx_t *ctx)
673{
674 /* When we already have some bits in our internal buffer concatenate
675 both inputs first. */
676 if (ctx->buflen != 0) {
677 size_t left_over = ctx->buflen;
678 size_t add = 128 - left_over > len ? len : 128 - left_over;
679
680 memcpy(&ctx->buffer[left_over], buffer, add);
681 ctx->buflen += add;
682
683 if (left_over + add > 64) {
684 md5_hash_block(ctx->buffer, (left_over + add) & ~63, ctx);
685 /* The regions in the following copy operation cannot overlap. */
686 memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
687 (left_over + add) & 63);
688 ctx->buflen = (left_over + add) & 63;
689 }
690
691 buffer = (const char *) buffer + add;
692 len -= add;
693 }
694
695 /* Process available complete blocks. */
696 if (len > 64) {
697 md5_hash_block(buffer, len & ~63, ctx);
698 buffer = (const char *) buffer + (len & ~63);
699 len &= 63;
700 }
701
702 /* Move remaining bytes in internal buffer. */
703 if (len > 0) {
704 memcpy(ctx->buffer, buffer, len);
705 ctx->buflen = len;
706 }
707}
708
709static void md5_hash(const void *buffer, size_t length, void *md5_ctx)
710{
711 if (length % 64 == 0) {
712 md5_hash_block(buffer, length, md5_ctx);
713 } else {
714 md5_hash_bytes(buffer, length, md5_ctx);
715 }
716}
717
718/* Process the remaining bytes in the buffer and put result from CTX
719 * in first 16 bytes following RESBUF. The result is always in little
720 * endian byte order, so that a byte-wise output yields to the wanted
721 * ASCII representation of the message digest.
722 *
723 * IMPORTANT: On some systems it is required that RESBUF is correctly
724 * aligned for a 32 bits value.
725 */
726static void *md5_end(void *resbuf, struct md5_ctx_t *ctx)
727{
728 /* Take yet unprocessed bytes into account. */
729 uint32_t bytes = ctx->buflen;
730 size_t pad;
731
732 /* Now count remaining bytes. */
733 ctx->total[0] += bytes;
734 if (ctx->total[0] < bytes)
735 ++ctx->total[1];
736
737 pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
738# if MD5SUM_SIZE_VS_SPEED > 0
739 memset(&ctx->buffer[bytes], 0, pad);
740 ctx->buffer[bytes] = 0x80;
741# else
742 memcpy(&ctx->buffer[bytes], fillbuf, pad);
743# endif /* MD5SUM_SIZE_VS_SPEED > 0 */
744
745 /* Put the 64-bit file length in *bits* at the end of the buffer. */
746 *(uint32_t *) & ctx->buffer[bytes + pad] = SWAP(ctx->total[0] << 3);
747 *(uint32_t *) & ctx->buffer[bytes + pad + 4] =
748 SWAP(((ctx->total[1] << 3) | (ctx->total[0] >> 29)));
749
750 /* Process last bytes. */
751 md5_hash_block(ctx->buffer, bytes + pad + 8, ctx);
752
753 /* Put result from CTX in first 16 bytes following RESBUF. The result is
754 * always in little endian byte order, so that a byte-wise output yields
755 * to the wanted ASCII representation of the message digest.
756 *
757 * IMPORTANT: On some systems it is required that RESBUF is correctly
758 * aligned for a 32 bits value.
759 */
760 ((uint32_t *) resbuf)[0] = SWAP(ctx->A);
761 ((uint32_t *) resbuf)[1] = SWAP(ctx->B);
762 ((uint32_t *) resbuf)[2] = SWAP(ctx->C);
763 ((uint32_t *) resbuf)[3] = SWAP(ctx->D);
764
765 return resbuf;
766}
767#endif /* CONFIG_MD5SUM */
768
769
770
771
772extern int hash_fd(int src_fd, const size_t size, const uint8_t hash_algo,
773 uint8_t * hashval)
774{
775 int result = EXIT_SUCCESS;
776// size_t hashed_count = 0;
777 size_t blocksize = 0;
778 size_t remaining = size;
779 unsigned char *buffer = NULL;
780 void (*hash_fn_ptr)(const void *, size_t, void *) = NULL;
781 void *cx = NULL;
782
783#ifdef CONFIG_SHA1SUM
784 struct sha1_ctx_t sha1_cx;
785#endif
786#ifdef CONFIG_MD5SUM
787 struct md5_ctx_t md5_cx;
788#endif
789
790
791#ifdef CONFIG_SHA1SUM
792 if (hash_algo == HASH_SHA1) {
793 /* Ensure that BLOCKSIZE is a multiple of 64. */
794 blocksize = 65536;
795 buffer = xmalloc(blocksize);
796 hash_fn_ptr = sha1_hash;
797 cx = &sha1_cx;
798 }
799#endif
800#ifdef CONFIG_MD5SUM
801 if (hash_algo == HASH_MD5) {
802 blocksize = 4096;
803 buffer = xmalloc(blocksize + 72);
804 hash_fn_ptr = md5_hash;
805 cx = &md5_cx;
806 }
807#endif
808
809 /* Initialize the computation context. */
810#ifdef CONFIG_SHA1SUM
811 if (hash_algo == HASH_SHA1) {
812 sha1_begin(&sha1_cx);
813 }
814#endif
815#ifdef CONFIG_MD5SUM
816 if (hash_algo == HASH_MD5) {
817 md5_begin(&md5_cx);
818 }
819#endif
820 /* Iterate over full file contents. */
821 while ((remaining == (size_t) -1) || (remaining > 0)) {
822 size_t read_try;
823 ssize_t read_got;
824
825 if (remaining > blocksize) {
826 read_try = blocksize;
827 } else {
828 read_try = remaining;
829 }
830 read_got = bb_full_read(src_fd, buffer, read_try);
831 if (read_got < 1) {
832 /* count == 0 means short read
833 * count == -1 means read error */
834 result = read_got - 1;
835 break;
836 }
837 if (remaining != (size_t) -1) {
838 remaining -= read_got;
839 }
840
841 /* Process buffer */
842 hash_fn_ptr(buffer, read_got, cx);
843 }
844
845 /* Finalize and write the hash into our buffer. */
846#ifdef CONFIG_SHA1SUM
847 if (hash_algo == HASH_SHA1) {
848 sha1_end(hashval, &sha1_cx);
849 }
850#endif
851#ifdef CONFIG_MD5SUM
852 if (hash_algo == HASH_MD5) {
853 md5_end(hashval, &md5_cx);
854 }
855#endif
856
857 free(buffer);
858 return result;
859}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdarg.h>
23#include <stdlib.h>
24
25#include "libbb.h"
26
27extern void bb_herror_msg(const char *s, ...)
28{
29 va_list p;
30
31 va_start(p, s);
32 bb_vherror_msg(s, p);
33 va_end(p);
34}
35
36
37/* END CODE */
38/*
39Local Variables:
40c-file-style: "linux"
41c-basic-offset: 4
42tab-width: 4
43End:
44*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdarg.h>
23#include <stdlib.h>
24
25#include "libbb.h"
26
27extern void bb_herror_msg_and_die(const char *s, ...)
28{
29 va_list p;
30
31 va_start(p, s);
32 bb_vherror_msg(s, p);
33 va_end(p);
34 exit(bb_default_error_retval);
35}
36
37
38/* END CODE */
39/*
40Local Variables:
41c-file-style: "linux"
42c-basic-offset: 4
43tab-width: 4
44End:
45*/
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 @@
1/*
2 * June 30, 2001 Manuel Novoa III
3 *
4 * All-integer version (hey, not everyone has floating point) of
5 * make_human_readable_str, modified from similar code I had written
6 * for busybox several months ago.
7 *
8 * Notes:
9 * 1) I'm using an unsigned long long to hold the product size * block_size,
10 * as df (which calls this routine) could request a representation of a
11 * partition size in bytes > max of unsigned long. If long longs aren't
12 * available, it would be possible to do what's needed using polynomial
13 * representations (say, powers of 1024) and manipulating coefficients.
14 * The base ten "bytes" output could be handled similarly.
15 *
16 * 2) This routine always outputs a decimal point and a tenths digit when
17 * display_unit != 0. Hence, it isn't uncommon for the returned string
18 * to have a length of 5 or 6.
19 *
20 * It might be nice to add a flag to indicate no decimal digits in
21 * that case. This could be either an additional parameter, or a
22 * special value of display_unit. Such a flag would also be nice for du.
23 *
24 * Some code to omit the decimal point and tenths digit is sketched out
25 * and "#if 0"'d below.
26 */
27
28#include <stdio.h>
29#include "libbb.h"
30
31const char *make_human_readable_str(unsigned long long size,
32 unsigned long block_size, unsigned long display_unit)
33{
34 /* The code will adjust for additional (appended) units. */
35 static const char zero_and_units[] = { '0', 0, 'k', 'M', 'G', 'T' };
36 static const char fmt[] = "%Lu";
37 static const char fmt_tenths[] = "%Lu.%d%c";
38
39 static char str[21]; /* Sufficient for 64 bit unsigned integers. */
40
41 unsigned long long val;
42 int frac;
43 const char *u;
44 const char *f;
45
46 u = zero_and_units;
47 f = fmt;
48 frac = 0;
49
50 val = size * block_size;
51 if (val == 0) {
52 return u;
53 }
54
55 if (display_unit) {
56 val += display_unit/2; /* Deal with rounding. */
57 val /= display_unit; /* Don't combine with the line above!!! */
58 } else {
59 ++u;
60 while ((val >= KILOBYTE)
61 && (u < zero_and_units + sizeof(zero_and_units) - 1)) {
62 f = fmt_tenths;
63 ++u;
64 frac = ((((int)(val % KILOBYTE)) * 10) + (KILOBYTE/2)) / KILOBYTE;
65 val /= KILOBYTE;
66 }
67 if (frac >= 10) { /* We need to round up here. */
68 ++val;
69 frac = 0;
70 }
71#if 0
72 /* Sample code to omit decimal point and tenths digit. */
73 if ( /* no_tenths */ 1 ) {
74 if ( frac >= 5 ) {
75 ++val;
76 }
77 f = "%Lu%*c" /* fmt_no_tenths */ ;
78 frac = 1;
79 }
80#endif
81 }
82
83 /* If f==fmt then 'frac' and 'u' are ignored. */
84 snprintf(str, sizeof(str), f, val, frac, *u);
85
86 return str;
87}
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 @@
1/*
2 * stolen from net-tools-1.59 and stripped down for busybox by
3 * Erik Andersen <andersen@codepoet.org>
4 *
5 * Heavily modified by Manuel Novoa III Mar 12, 2001
6 *
7 * Version: $Id: inet_common.c,v 1.8 2004/03/10 07:42:38 mjn3 Exp $
8 *
9 */
10
11#include "inet_common.h"
12#include <stdio.h>
13#include <errno.h>
14#include <stdlib.h>
15#include <string.h>
16#include <unistd.h>
17#include "libbb.h"
18
19#ifdef DEBUG
20# include <resolv.h>
21#endif
22
23
24const char bb_INET_default[] = "default";
25
26int INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst)
27{
28 struct hostent *hp;
29 struct netent *np;
30
31 /* Grmpf. -FvK */
32 s_in->sin_family = AF_INET;
33 s_in->sin_port = 0;
34
35 /* Default is special, meaning 0.0.0.0. */
36 if (!strcmp(name, bb_INET_default)) {
37 s_in->sin_addr.s_addr = INADDR_ANY;
38 return (1);
39 }
40 /* Look to see if it's a dotted quad. */
41 if (inet_aton(name, &s_in->sin_addr)) {
42 return 0;
43 }
44 /* If we expect this to be a hostname, try hostname database first */
45#ifdef DEBUG
46 if (hostfirst) {
47 bb_error_msg("gethostbyname (%s)", name);
48 }
49#endif
50 if (hostfirst && (hp = gethostbyname(name)) != (struct hostent *) NULL) {
51 memcpy((char *) &s_in->sin_addr, (char *) hp->h_addr_list[0],
52 sizeof(struct in_addr));
53 return 0;
54 }
55 /* Try the NETWORKS database to see if this is a known network. */
56#ifdef DEBUG
57 bb_error_msg("getnetbyname (%s)", name);
58#endif
59 if ((np = getnetbyname(name)) != (struct netent *) NULL) {
60 s_in->sin_addr.s_addr = htonl(np->n_net);
61 return 1;
62 }
63 if (hostfirst) {
64 /* Don't try again */
65 errno = h_errno;
66 return -1;
67 }
68#ifdef DEBUG
69 res_init();
70 _res.options |= RES_DEBUG;
71#endif
72
73#ifdef DEBUG
74 bb_error_msg("gethostbyname (%s)", name);
75#endif
76 if ((hp = gethostbyname(name)) == (struct hostent *) NULL) {
77 errno = h_errno;
78 return -1;
79 }
80 memcpy((char *) &s_in->sin_addr, (char *) hp->h_addr_list[0],
81 sizeof(struct in_addr));
82
83 return 0;
84}
85
86/* cache */
87struct addr {
88 struct sockaddr_in addr;
89 char *name;
90 int host;
91 struct addr *next;
92};
93
94static struct addr *INET_nn = NULL; /* addr-to-name cache */
95
96/* numeric: & 0x8000: default instead of *,
97 * & 0x4000: host instead of net,
98 * & 0x0fff: don't resolve
99 */
100int INET_rresolve(char *name, size_t len, struct sockaddr_in *s_in,
101 int numeric, unsigned int netmask)
102{
103 struct hostent *ent;
104 struct netent *np;
105 struct addr *pn;
106 unsigned long ad, host_ad;
107 int host = 0;
108
109 /* Grmpf. -FvK */
110 if (s_in->sin_family != AF_INET) {
111#ifdef DEBUG
112 bb_error_msg("rresolve: unsupport address family %d !",
113 s_in->sin_family);
114#endif
115 errno = EAFNOSUPPORT;
116 return (-1);
117 }
118 ad = (unsigned long) s_in->sin_addr.s_addr;
119#ifdef DEBUG
120 bb_error_msg("rresolve: %08lx, mask %08x, num %08x", ad, netmask, numeric);
121#endif
122 if (ad == INADDR_ANY) {
123 if ((numeric & 0x0FFF) == 0) {
124 if (numeric & 0x8000)
125 safe_strncpy(name, bb_INET_default, len);
126 else
127 safe_strncpy(name, "*", len);
128 return (0);
129 }
130 }
131 if (numeric & 0x0FFF) {
132 safe_strncpy(name, inet_ntoa(s_in->sin_addr), len);
133 return (0);
134 }
135
136 if ((ad & (~netmask)) != 0 || (numeric & 0x4000))
137 host = 1;
138#if 0
139 INET_nn = NULL;
140#endif
141 pn = INET_nn;
142 while (pn != NULL) {
143 if (pn->addr.sin_addr.s_addr == ad && pn->host == host) {
144 safe_strncpy(name, pn->name, len);
145#ifdef DEBUG
146 bb_error_msg("rresolve: found %s %08lx in cache",
147 (host ? "host" : "net"), ad);
148#endif
149 return (0);
150 }
151 pn = pn->next;
152 }
153
154 host_ad = ntohl(ad);
155 np = NULL;
156 ent = NULL;
157 if (host) {
158#ifdef DEBUG
159 bb_error_msg("gethostbyaddr (%08lx)", ad);
160#endif
161 ent = gethostbyaddr((char *) &ad, 4, AF_INET);
162 if (ent != NULL) {
163 safe_strncpy(name, ent->h_name, len);
164 }
165 } else {
166#ifdef DEBUG
167 bb_error_msg("getnetbyaddr (%08lx)", host_ad);
168#endif
169 np = getnetbyaddr(host_ad, AF_INET);
170 if (np != NULL) {
171 safe_strncpy(name, np->n_name, len);
172 }
173 }
174 if ((ent == NULL) && (np == NULL)) {
175 safe_strncpy(name, inet_ntoa(s_in->sin_addr), len);
176 }
177 pn = (struct addr *) xmalloc(sizeof(struct addr));
178 pn->addr = *s_in;
179 pn->next = INET_nn;
180 pn->host = host;
181 pn->name = bb_xstrdup(name);
182 INET_nn = pn;
183
184 return (0);
185}
186
187#ifdef CONFIG_FEATURE_IPV6
188
189int INET6_resolve(const char *name, struct sockaddr_in6 *sin6)
190{
191 struct addrinfo req, *ai;
192 int s;
193
194 memset(&req, '\0', sizeof req);
195 req.ai_family = AF_INET6;
196 if ((s = getaddrinfo(name, NULL, &req, &ai))) {
197 bb_error_msg("getaddrinfo: %s: %d", name, s);
198 return -1;
199 }
200 memcpy(sin6, ai->ai_addr, sizeof(struct sockaddr_in6));
201
202 freeaddrinfo(ai);
203
204 return (0);
205}
206
207#ifndef IN6_IS_ADDR_UNSPECIFIED
208# define IN6_IS_ADDR_UNSPECIFIED(a) \
209 (((__u32 *) (a))[0] == 0 && ((__u32 *) (a))[1] == 0 && \
210 ((__u32 *) (a))[2] == 0 && ((__u32 *) (a))[3] == 0)
211#endif
212
213
214int INET6_rresolve(char *name, size_t len, struct sockaddr_in6 *sin6,
215 int numeric)
216{
217 int s;
218
219 /* Grmpf. -FvK */
220 if (sin6->sin6_family != AF_INET6) {
221#ifdef DEBUG
222 bb_error_msg(_("rresolve: unsupport address family %d !\n"),
223 sin6->sin6_family);
224#endif
225 errno = EAFNOSUPPORT;
226 return (-1);
227 }
228 if (numeric & 0x7FFF) {
229 inet_ntop(AF_INET6, &sin6->sin6_addr, name, len);
230 return (0);
231 }
232 if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
233 if (numeric & 0x8000) {
234 strcpy(name, "default");
235 } else {
236 strcpy(name, "*");
237 }
238 return (0);
239 }
240
241 s = getnameinfo((struct sockaddr *) sin6, sizeof(struct sockaddr_in6), name, len, NULL, 0, 0);
242 if (s) {
243 bb_error_msg("getnameinfo failed");
244 return -1;
245 }
246 return (0);
247}
248
249#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) many different people.
6 * If you wrote this, please acknowledge your work.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 * USA
22 */
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include "libbb.h"
28
29#define HASH_SIZE 311 /* Should be prime */
30#define hash_inode(i) ((i) % HASH_SIZE)
31
32typedef struct ino_dev_hash_bucket_struct {
33 struct ino_dev_hash_bucket_struct *next;
34 ino_t ino;
35 dev_t dev;
36 char name[1];
37} ino_dev_hashtable_bucket_t;
38
39static ino_dev_hashtable_bucket_t *ino_dev_hashtable[HASH_SIZE];
40
41/*
42 * Return 1 if statbuf->st_ino && statbuf->st_dev are recorded in
43 * `ino_dev_hashtable', else return 0
44 *
45 * If NAME is a non-NULL pointer to a character pointer, and there is
46 * a match, then set *NAME to the value of the name slot in that
47 * bucket.
48 */
49int is_in_ino_dev_hashtable(const struct stat *statbuf, char **name)
50{
51 ino_dev_hashtable_bucket_t *bucket;
52
53 bucket = ino_dev_hashtable[hash_inode(statbuf->st_ino)];
54 while (bucket != NULL) {
55 if ((bucket->ino == statbuf->st_ino) &&
56 (bucket->dev == statbuf->st_dev))
57 {
58 if (name) *name = bucket->name;
59 return 1;
60 }
61 bucket = bucket->next;
62 }
63 return 0;
64}
65
66/* Add statbuf to statbuf hash table */
67void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name)
68{
69 int i;
70 size_t s;
71 ino_dev_hashtable_bucket_t *bucket;
72
73 i = hash_inode(statbuf->st_ino);
74 s = name ? strlen(name) : 0;
75 bucket = xmalloc(sizeof(ino_dev_hashtable_bucket_t) + s);
76 bucket->ino = statbuf->st_ino;
77 bucket->dev = statbuf->st_dev;
78 if (name)
79 strcpy(bucket->name, name);
80 else
81 bucket->name[0] = '\0';
82 bucket->next = ino_dev_hashtable[i];
83 ino_dev_hashtable[i] = bucket;
84}
85
86#ifdef CONFIG_FEATURE_CLEAN_UP
87/* Clear statbuf hash table */
88void reset_ino_dev_hashtable(void)
89{
90 int i;
91 ino_dev_hashtable_bucket_t *bucket;
92
93 for (i = 0; i < HASH_SIZE; i++) {
94 while (ino_dev_hashtable[i] != NULL) {
95 bucket = ino_dev_hashtable[i]->next;
96 free(ino_dev_hashtable[i]);
97 ino_dev_hashtable[i] = bucket;
98 }
99 }
100}
101#endif
102
103
104/* END CODE */
105/*
106Local Variables:
107c-file-style: "linux"
108c-basic-offset: 4
109tab-width: 4
110End:
111*/
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 @@
1/*
2 * stolen from net-tools-1.59 and stripped down for busybox by
3 * Erik Andersen <andersen@codepoet.org>
4 *
5 * Heavily modified by Manuel Novoa III Mar 12, 2001
6 *
7 * Pruned unused code using KEEP_UNUSED define.
8 * Added print_bytes_scaled function to reduce code size.
9 * Added some (potentially) missing defines.
10 * Improved display support for -a and for a named interface.
11 *
12 * -----------------------------------------------------------
13 *
14 * ifconfig This file contains an implementation of the command
15 * that either displays or sets the characteristics of
16 * one or more of the system's networking interfaces.
17 *
18 * Version: $Id: interface.c,v 1.25 2004/08/26 21:45:21 andersen Exp $
19 *
20 * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
21 * and others. Copyright 1993 MicroWalt Corporation
22 *
23 * This program is free software; you can redistribute it
24 * and/or modify it under the terms of the GNU General
25 * Public License as published by the Free Software
26 * Foundation; either version 2 of the License, or (at
27 * your option) any later version.
28 *
29 * Patched to support 'add' and 'del' keywords for INET(4) addresses
30 * by Mrs. Brisby <mrs.brisby@nimh.org>
31 *
32 * {1.34} - 19980630 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
33 * - gettext instead of catgets for i18n
34 * 10/1998 - Andi Kleen. Use interface list primitives.
35 * 20001008 - Bernd Eckenfels, Patch from RH for setting mtu
36 * (default AF was wrong)
37 */
38
39/* #define KEEP_UNUSED */
40
41/*
42 *
43 * Protocol Families.
44 *
45 */
46#define HAVE_AFINET 1
47#undef HAVE_AFIPX
48#undef HAVE_AFATALK
49#undef HAVE_AFNETROM
50#undef HAVE_AFX25
51#undef HAVE_AFECONET
52#undef HAVE_AFASH
53
54/*
55 *
56 * Device Hardware types.
57 *
58 */
59#define HAVE_HWETHER 1
60#define HAVE_HWPPP 1
61#undef HAVE_HWSLIP
62
63
64#include "inet_common.h"
65#include <stdio.h>
66#include <errno.h>
67#include <stdlib.h>
68#include <string.h>
69#include <unistd.h>
70#include <fcntl.h>
71#include <ctype.h>
72#include <sys/ioctl.h>
73#include <sys/types.h>
74#include <net/if.h>
75#include <net/if_arp.h>
76#include "libbb.h"
77
78#ifdef CONFIG_FEATURE_IPV6
79# define HAVE_AFINET6 1
80#else
81# undef HAVE_AFINET6
82#endif
83
84#define _(x) x
85#define _PATH_PROCNET_DEV "/proc/net/dev"
86#define _PATH_PROCNET_IFINET6 "/proc/net/if_inet6"
87#define new(p) ((p) = xcalloc(1,sizeof(*(p))))
88#define KRELEASE(maj,min,patch) ((maj) * 65536 + (min)*256 + (patch))
89
90#ifdef HAVE_HWSLIP
91#include <net/if_slip.h>
92#endif
93
94#if HAVE_AFINET6
95
96#ifndef _LINUX_IN6_H
97/*
98 * This is in linux/include/net/ipv6.h.
99 */
100
101struct in6_ifreq {
102 struct in6_addr ifr6_addr;
103 uint32_t ifr6_prefixlen;
104 unsigned int ifr6_ifindex;
105};
106
107#endif
108
109#endif /* HAVE_AFINET6 */
110
111#if HAVE_AFIPX
112#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
113#include <netipx/ipx.h>
114#else
115#include "ipx.h"
116#endif
117#endif
118
119/* Defines for glibc2.0 users. */
120#ifndef SIOCSIFTXQLEN
121#define SIOCSIFTXQLEN 0x8943
122#define SIOCGIFTXQLEN 0x8942
123#endif
124
125/* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
126#ifndef ifr_qlen
127#define ifr_qlen ifr_ifru.ifru_mtu
128#endif
129
130#ifndef HAVE_TXQUEUELEN
131#define HAVE_TXQUEUELEN 1
132#endif
133
134#ifndef IFF_DYNAMIC
135#define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */
136#endif
137
138/* This structure defines protocol families and their handlers. */
139struct aftype {
140 const char *name;
141 const char *title;
142 int af;
143 int alen;
144 char *(*print) (unsigned char *);
145 char *(*sprint) (struct sockaddr *, int numeric);
146 int (*input) (int type, char *bufp, struct sockaddr *);
147 void (*herror) (char *text);
148 int (*rprint) (int options);
149 int (*rinput) (int typ, int ext, char **argv);
150
151 /* may modify src */
152 int (*getmask) (char *src, struct sockaddr * mask, char *name);
153
154 int fd;
155 char *flag_file;
156};
157
158#ifdef KEEP_UNUSED
159
160static int flag_unx;
161
162#ifdef HAVE_AFIPX
163static int flag_ipx;
164#endif
165#ifdef HAVE_AFX25
166static int flag_ax25;
167#endif
168#ifdef HAVE_AFATALK
169static int flag_ddp;
170#endif
171#ifdef HAVE_AFNETROM
172static int flag_netrom;
173#endif
174static int flag_inet;
175
176#ifdef HAVE_AFINET6
177static int flag_inet6;
178#endif
179#ifdef HAVE_AFECONET
180static int flag_econet;
181#endif
182#ifdef HAVE_AFX25
183static int flag_x25 = 0;
184#endif
185#ifdef HAVE_AFASH
186static int flag_ash;
187#endif
188
189
190static struct aftrans_t {
191 char *alias;
192 char *name;
193 int *flag;
194} aftrans[] = {
195
196#ifdef HAVE_AFX25
197 {
198 "ax25", "ax25", &flag_ax25},
199#endif
200 {
201 "ip", "inet", &flag_inet},
202#ifdef HAVE_AFINET6
203 {
204 "ip6", "inet6", &flag_inet6},
205#endif
206#ifdef HAVE_AFIPX
207 {
208 "ipx", "ipx", &flag_ipx},
209#endif
210#ifdef HAVE_AFATALK
211 {
212 "appletalk", "ddp", &flag_ddp},
213#endif
214#ifdef HAVE_AFNETROM
215 {
216 "netrom", "netrom", &flag_netrom},
217#endif
218 {
219 "inet", "inet", &flag_inet},
220#ifdef HAVE_AFINET6
221 {
222 "inet6", "inet6", &flag_inet6},
223#endif
224#ifdef HAVE_AFATALK
225 {
226 "ddp", "ddp", &flag_ddp},
227#endif
228 {
229 "unix", "unix", &flag_unx}, {
230 "tcpip", "inet", &flag_inet},
231#ifdef HAVE_AFECONET
232 {
233 "econet", "ec", &flag_econet},
234#endif
235#ifdef HAVE_AFX25
236 {
237 "x25", "x25", &flag_x25},
238#endif
239#ifdef HAVE_AFASH
240 {
241 "ash", "ash", &flag_ash},
242#endif
243 {
244 0, 0, 0}
245};
246
247static char afname[256] = "";
248#endif /* KEEP_UNUSED */
249
250#if HAVE_AFUNIX
251
252/* Display a UNIX domain address. */
253static char *UNIX_print(unsigned char *ptr)
254{
255 return (ptr);
256}
257
258
259/* Display a UNIX domain address. */
260static char *UNIX_sprint(struct sockaddr *sap, int numeric)
261{
262 static char buf[64];
263
264 if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
265 return safe_strncpy(buf, _("[NONE SET]"), sizeof(buf));
266 return (UNIX_print(sap->sa_data));
267}
268
269
270static struct aftype unix_aftype = {
271 "unix", "UNIX Domain", AF_UNIX, 0,
272 UNIX_print, UNIX_sprint, NULL, NULL,
273 NULL, NULL, NULL,
274 -1,
275 "/proc/net/unix"
276};
277#endif /* HAVE_AFUNIX */
278
279#if HAVE_AFINET
280
281#ifdef KEEP_UNUSED
282static void INET_reserror(char *text)
283{
284 herror(text);
285}
286
287/* Display an Internet socket address. */
288static char *INET_print(unsigned char *ptr)
289{
290 return (inet_ntoa((*(struct in_addr *) ptr)));
291}
292#endif /* KEEP_UNUSED */
293
294/* Display an Internet socket address. */
295static char *INET_sprint(struct sockaddr *sap, int numeric)
296{
297 static char buff[128];
298
299 if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
300 return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff));
301
302 if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap,
303 numeric, 0xffffff00) != 0)
304 return (NULL);
305
306 return (buff);
307}
308
309#ifdef KEEP_UNUSED
310static char *INET_sprintmask(struct sockaddr *sap, int numeric,
311 unsigned int netmask)
312{
313 static char buff[128];
314
315 if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
316 return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff));
317 if (INET_rresolve(buff, sizeof(buff), (struct sockaddr_in *) sap,
318 numeric, netmask) != 0)
319 return (NULL);
320 return (buff);
321}
322
323static int INET_getsock(char *bufp, struct sockaddr *sap)
324{
325 char *sp = bufp, *bp;
326 unsigned int i;
327 unsigned val;
328 struct sockaddr_in *sin;
329
330 sin = (struct sockaddr_in *) sap;
331 sin->sin_family = AF_INET;
332 sin->sin_port = 0;
333
334 val = 0;
335 bp = (char *) &val;
336 for (i = 0; i < sizeof(sin->sin_addr.s_addr); i++) {
337 *sp = toupper(*sp);
338
339 if ((*sp >= 'A') && (*sp <= 'F'))
340 bp[i] |= (int) (*sp - 'A') + 10;
341 else if ((*sp >= '0') && (*sp <= '9'))
342 bp[i] |= (int) (*sp - '0');
343 else
344 return (-1);
345
346 bp[i] <<= 4;
347 sp++;
348 *sp = toupper(*sp);
349
350 if ((*sp >= 'A') && (*sp <= 'F'))
351 bp[i] |= (int) (*sp - 'A') + 10;
352 else if ((*sp >= '0') && (*sp <= '9'))
353 bp[i] |= (int) (*sp - '0');
354 else
355 return (-1);
356
357 sp++;
358 }
359 sin->sin_addr.s_addr = htonl(val);
360
361 return (sp - bufp);
362}
363
364static int INET_input(int type, char *bufp, struct sockaddr *sap)
365{
366 switch (type) {
367 case 1:
368 return (INET_getsock(bufp, sap));
369 case 256:
370 return (INET_resolve(bufp, (struct sockaddr_in *) sap, 1));
371 default:
372 return (INET_resolve(bufp, (struct sockaddr_in *) sap, 0));
373 }
374}
375
376static int INET_getnetmask(char *adr, struct sockaddr *m, char *name)
377{
378 struct sockaddr_in *mask = (struct sockaddr_in *) m;
379 char *slash, *end;
380 int prefix;
381
382 if ((slash = strchr(adr, '/')) == NULL)
383 return 0;
384
385 *slash++ = '\0';
386 prefix = strtoul(slash, &end, 0);
387 if (*end != '\0')
388 return -1;
389
390 if (name) {
391 sprintf(name, "/%d", prefix);
392 }
393 mask->sin_family = AF_INET;
394 mask->sin_addr.s_addr = htonl(~(0xffffffffU >> prefix));
395 return 1;
396}
397#endif /* KEEP_UNUSED */
398
399static struct aftype inet_aftype = {
400 "inet", "DARPA Internet", AF_INET, sizeof(unsigned long),
401 NULL /* UNUSED INET_print */ , INET_sprint,
402 NULL /* UNUSED INET_input */ , NULL /* UNUSED INET_reserror */ ,
403 NULL /*INET_rprint */ , NULL /*INET_rinput */ ,
404 NULL /* UNUSED INET_getnetmask */ ,
405 -1,
406 NULL
407};
408
409#endif /* HAVE_AFINET */
410
411#if HAVE_AFINET6
412
413#ifdef KEEP_UNUSED
414static void INET6_reserror(char *text)
415{
416 herror(text);
417}
418
419/* Display an Internet socket address. */
420static char *INET6_print(unsigned char *ptr)
421{
422 static char name[80];
423
424 inet_ntop(AF_INET6, (struct in6_addr *) ptr, name, 80);
425 return name;
426}
427#endif /* KEEP_UNUSED */
428
429/* Display an Internet socket address. */
430/* dirty! struct sockaddr usually doesn't suffer for inet6 addresses, fst. */
431static char *INET6_sprint(struct sockaddr *sap, int numeric)
432{
433 static char buff[128];
434
435 if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
436 return safe_strncpy(buff, _("[NONE SET]"), sizeof(buff));
437 if (INET6_rresolve
438 (buff, sizeof(buff), (struct sockaddr_in6 *) sap, numeric) != 0)
439 return safe_strncpy(buff, _("[UNKNOWN]"), sizeof(buff));
440 return (buff);
441}
442
443#ifdef KEEP_UNUSED
444static int INET6_getsock(char *bufp, struct sockaddr *sap)
445{
446 struct sockaddr_in6 *sin6;
447
448 sin6 = (struct sockaddr_in6 *) sap;
449 sin6->sin6_family = AF_INET6;
450 sin6->sin6_port = 0;
451
452 if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0)
453 return (-1);
454
455 return 16; /* ?;) */
456}
457
458static int INET6_input(int type, char *bufp, struct sockaddr *sap)
459{
460 switch (type) {
461 case 1:
462 return (INET6_getsock(bufp, sap));
463 default:
464 return (INET6_resolve(bufp, (struct sockaddr_in6 *) sap));
465 }
466}
467#endif /* KEEP_UNUSED */
468
469static struct aftype inet6_aftype = {
470 "inet6", "IPv6", AF_INET6, sizeof(struct in6_addr),
471 NULL /* UNUSED INET6_print */ , INET6_sprint,
472 NULL /* UNUSED INET6_input */ , NULL /* UNUSED INET6_reserror */ ,
473 NULL /*INET6_rprint */ , NULL /*INET6_rinput */ ,
474 NULL /* UNUSED INET6_getnetmask */ ,
475 -1,
476 NULL
477};
478
479#endif /* HAVE_AFINET6 */
480
481/* Display an UNSPEC address. */
482static char *UNSPEC_print(unsigned char *ptr)
483{
484 static char buff[sizeof(struct sockaddr) * 3 + 1];
485 char *pos;
486 unsigned int i;
487
488 pos = buff;
489 for (i = 0; i < sizeof(struct sockaddr); i++) {
490 /* careful -- not every libc's sprintf returns # bytes written */
491 sprintf(pos, "%02X-", (*ptr++ & 0377));
492 pos += 3;
493 }
494 /* Erase trailing "-". Works as long as sizeof(struct sockaddr) != 0 */
495 *--pos = '\0';
496 return (buff);
497}
498
499/* Display an UNSPEC socket address. */
500static char *UNSPEC_sprint(struct sockaddr *sap, int numeric)
501{
502 static char buf[64];
503
504 if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
505 return safe_strncpy(buf, _("[NONE SET]"), sizeof(buf));
506 return (UNSPEC_print(sap->sa_data));
507}
508
509static struct aftype unspec_aftype = {
510 "unspec", "UNSPEC", AF_UNSPEC, 0,
511 UNSPEC_print, UNSPEC_sprint, NULL, NULL,
512 NULL,
513};
514
515static struct aftype *aftypes[] = {
516#if HAVE_AFUNIX
517 &unix_aftype,
518#endif
519#if HAVE_AFINET
520 &inet_aftype,
521#endif
522#if HAVE_AFINET6
523 &inet6_aftype,
524#endif
525#if HAVE_AFAX25
526 &ax25_aftype,
527#endif
528#if HAVE_AFNETROM
529 &netrom_aftype,
530#endif
531#if HAVE_AFROSE
532 &rose_aftype,
533#endif
534#if HAVE_AFIPX
535 &ipx_aftype,
536#endif
537#if HAVE_AFATALK
538 &ddp_aftype,
539#endif
540#if HAVE_AFECONET
541 &ec_aftype,
542#endif
543#if HAVE_AFASH
544 &ash_aftype,
545#endif
546#if HAVE_AFX25
547 &x25_aftype,
548#endif
549 &unspec_aftype,
550 NULL
551};
552
553#ifdef KEEP_UNUSED
554static short sVafinit = 0;
555
556static void afinit()
557{
558 unspec_aftype.title = _("UNSPEC");
559#if HAVE_AFUNIX
560 unix_aftype.title = _("UNIX Domain");
561#endif
562#if HAVE_AFINET
563 inet_aftype.title = _("DARPA Internet");
564#endif
565#if HAVE_AFINET6
566 inet6_aftype.title = _("IPv6");
567#endif
568#if HAVE_AFAX25
569 ax25_aftype.title = _("AMPR AX.25");
570#endif
571#if HAVE_AFNETROM
572 netrom_aftype.title = _("AMPR NET/ROM");
573#endif
574#if HAVE_AFIPX
575 ipx_aftype.title = _("Novell IPX");
576#endif
577#if HAVE_AFATALK
578 ddp_aftype.title = _("Appletalk DDP");
579#endif
580#if HAVE_AFECONET
581 ec_aftype.title = _("Econet");
582#endif
583#if HAVE_AFX25
584 x25_aftype.title = _("CCITT X.25");
585#endif
586#if HAVE_AFROSE
587 rose_aftype.title = _("AMPR ROSE");
588#endif
589#if HAVE_AFASH
590 ash_aftype.title = _("Ash");
591#endif
592 sVafinit = 1;
593}
594
595static int aftrans_opt(const char *arg)
596{
597 struct aftrans_t *paft;
598 char *tmp1, *tmp2;
599 char buf[256];
600
601 safe_strncpy(buf, arg, sizeof(buf));
602
603 tmp1 = buf;
604
605 while (tmp1) {
606
607 tmp2 = strchr(tmp1, ',');
608
609 if (tmp2)
610 *(tmp2++) = '\0';
611
612 paft = aftrans;
613 for (paft = aftrans; paft->alias; paft++) {
614 if (strcmp(tmp1, paft->alias))
615 continue;
616 if (strlen(paft->name) + strlen(afname) + 1 >= sizeof(afname)) {
617 bb_error_msg(_("Too many address family arguments."));
618 return (0);
619 }
620 if (paft->flag)
621 (*paft->flag)++;
622 if (afname[0])
623 strcat(afname, ",");
624 strcat(afname, paft->name);
625 break;
626 }
627 if (!paft->alias) {
628 bb_error_msg(_("Unknown address family `%s'."), tmp1);
629 return (1);
630 }
631 tmp1 = tmp2;
632 }
633
634 return (0);
635}
636
637/* set the default AF list from the program name or a constant value */
638static void aftrans_def(char *tool, char *argv0, char *dflt)
639{
640 char *tmp;
641 char *buf;
642
643 strcpy(afname, dflt);
644
645 if (!(tmp = strrchr(argv0, '/')))
646 tmp = argv0; /* no slash?! */
647 else
648 tmp++;
649
650 if (!(buf = strdup(tmp)))
651 return;
652
653 if (strlen(tool) >= strlen(tmp)) {
654 free(buf);
655 return;
656 }
657 tmp = buf + (strlen(tmp) - strlen(tool));
658
659 if (strcmp(tmp, tool) != 0) {
660 free(buf);
661 return;
662 }
663 *tmp = '\0';
664 if ((tmp = strchr(buf, '_')))
665 *tmp = '\0';
666
667 afname[0] = '\0';
668 if (aftrans_opt(buf))
669 strcpy(afname, buf);
670
671 free(buf);
672}
673
674/* Check our protocol family table for this family. */
675static struct aftype *get_aftype(const char *name)
676{
677 struct aftype **afp;
678
679#ifdef KEEP_UNUSED
680 if (!sVafinit)
681 afinit();
682#endif /* KEEP_UNUSED */
683
684 afp = aftypes;
685 while (*afp != NULL) {
686 if (!strcmp((*afp)->name, name))
687 return (*afp);
688 afp++;
689 }
690 if (strchr(name, ','))
691 bb_error_msg(_("Please don't supply more than one address family."));
692 return (NULL);
693}
694#endif /* KEEP_UNUSED */
695
696/* Check our protocol family table for this family. */
697static struct aftype *get_afntype(int af)
698{
699 struct aftype **afp;
700
701#ifdef KEEP_UNUSED
702 if (!sVafinit)
703 afinit();
704#endif /* KEEP_UNUSED */
705
706 afp = aftypes;
707 while (*afp != NULL) {
708 if ((*afp)->af == af)
709 return (*afp);
710 afp++;
711 }
712 return (NULL);
713}
714
715/* Check our protocol family table for this family and return its socket */
716static int get_socket_for_af(int af)
717{
718 struct aftype **afp;
719
720#ifdef KEEP_UNUSED
721 if (!sVafinit)
722 afinit();
723#endif /* KEEP_UNUSED */
724
725 afp = aftypes;
726 while (*afp != NULL) {
727 if ((*afp)->af == af)
728 return (*afp)->fd;
729 afp++;
730 }
731 return -1;
732}
733
734#ifdef KEEP_UNUSED
735/* type: 0=all, 1=getroute */
736static void print_aflist(int type)
737{
738 int count = 0;
739 char *txt;
740 struct aftype **afp;
741
742#ifdef KEEP_UNUSED
743 if (!sVafinit)
744 afinit();
745#endif /* KEEP_UNUSED */
746
747 afp = aftypes;
748 while (*afp != NULL) {
749 if ((type == 1 && ((*afp)->rprint == NULL)) || ((*afp)->af == 0)) {
750 afp++;
751 continue;
752 }
753 if ((count % 3) == 0)
754 fprintf(stderr, count ? "\n " : " ");
755 txt = (*afp)->name;
756 if (!txt)
757 txt = "..";
758 fprintf(stderr, "%s (%s) ", txt, _((*afp)->title));
759 count++;
760 afp++;
761 }
762 fprintf(stderr, "\n");
763}
764#endif /* KEEP_UNUSED */
765
766struct user_net_device_stats {
767 unsigned long long rx_packets; /* total packets received */
768 unsigned long long tx_packets; /* total packets transmitted */
769 unsigned long long rx_bytes; /* total bytes received */
770 unsigned long long tx_bytes; /* total bytes transmitted */
771 unsigned long rx_errors; /* bad packets received */
772 unsigned long tx_errors; /* packet transmit problems */
773 unsigned long rx_dropped; /* no space in linux buffers */
774 unsigned long tx_dropped; /* no space available in linux */
775 unsigned long rx_multicast; /* multicast packets received */
776 unsigned long rx_compressed;
777 unsigned long tx_compressed;
778 unsigned long collisions;
779
780 /* detailed rx_errors: */
781 unsigned long rx_length_errors;
782 unsigned long rx_over_errors; /* receiver ring buff overflow */
783 unsigned long rx_crc_errors; /* recved pkt with crc error */
784 unsigned long rx_frame_errors; /* recv'd frame alignment error */
785 unsigned long rx_fifo_errors; /* recv'r fifo overrun */
786 unsigned long rx_missed_errors; /* receiver missed packet */
787 /* detailed tx_errors */
788 unsigned long tx_aborted_errors;
789 unsigned long tx_carrier_errors;
790 unsigned long tx_fifo_errors;
791 unsigned long tx_heartbeat_errors;
792 unsigned long tx_window_errors;
793};
794
795struct interface {
796 struct interface *next, *prev;
797 char name[IFNAMSIZ]; /* interface name */
798 short type; /* if type */
799 short flags; /* various flags */
800 int metric; /* routing metric */
801 int mtu; /* MTU value */
802 int tx_queue_len; /* transmit queue length */
803 struct ifmap map; /* hardware setup */
804 struct sockaddr addr; /* IP address */
805 struct sockaddr dstaddr; /* P-P IP address */
806 struct sockaddr broadaddr; /* IP broadcast address */
807 struct sockaddr netmask; /* IP network mask */
808 struct sockaddr ipxaddr_bb; /* IPX network address */
809 struct sockaddr ipxaddr_sn; /* IPX network address */
810 struct sockaddr ipxaddr_e3; /* IPX network address */
811 struct sockaddr ipxaddr_e2; /* IPX network address */
812 struct sockaddr ddpaddr; /* Appletalk DDP address */
813 struct sockaddr ecaddr; /* Econet address */
814 int has_ip;
815 int has_ipx_bb;
816 int has_ipx_sn;
817 int has_ipx_e3;
818 int has_ipx_e2;
819 int has_ax25;
820 int has_ddp;
821 int has_econet;
822 char hwaddr[32]; /* HW address */
823 int statistics_valid;
824 struct user_net_device_stats stats; /* statistics */
825 int keepalive; /* keepalive value for SLIP */
826 int outfill; /* outfill value for SLIP */
827};
828
829
830int interface_opt_a = 0; /* show all interfaces */
831
832#ifdef KEEP_UNUSED
833static int opt_i = 0; /* show the statistics */
834static int opt_v = 0; /* debugging output flag */
835
836static int addr_family = 0; /* currently selected AF */
837#endif /* KEEP_UNUSED */
838
839static struct interface *int_list, *int_last;
840static int skfd = -1; /* generic raw socket desc. */
841
842
843static int sockets_open(int family)
844{
845 struct aftype **aft;
846 int sfd = -1;
847 static int force = -1;
848
849 if (force < 0) {
850 force = 0;
851 if (get_kernel_revision() < KRELEASE(2, 1, 0))
852 force = 1;
853 if (access("/proc/net", R_OK))
854 force = 1;
855 }
856 for (aft = aftypes; *aft; aft++) {
857 struct aftype *af = *aft;
858 int type = SOCK_DGRAM;
859
860 if (af->af == AF_UNSPEC)
861 continue;
862 if (family && family != af->af)
863 continue;
864 if (af->fd != -1) {
865 sfd = af->fd;
866 continue;
867 }
868 /* Check some /proc file first to not stress kmod */
869 if (!family && !force && af->flag_file) {
870 if (access(af->flag_file, R_OK))
871 continue;
872 }
873#if HAVE_AFNETROM
874 if (af->af == AF_NETROM)
875 type = SOCK_SEQPACKET;
876#endif
877#if HAVE_AFX25
878 if (af->af == AF_X25)
879 type = SOCK_SEQPACKET;
880#endif
881 af->fd = socket(af->af, type, 0);
882 if (af->fd >= 0)
883 sfd = af->fd;
884 }
885 if (sfd < 0) {
886 bb_error_msg(_("No usable address families found."));
887 }
888 return sfd;
889}
890
891/* like strcmp(), but knows about numbers */
892static int nstrcmp(const char *a, const char *b)
893{
894 const char *a_ptr = a;
895 const char *b_ptr = b;
896
897 while (*a == *b) {
898 if (*a == '\0') {
899 return 0;
900 }
901 if (!isdigit(*a) && isdigit(*(a+1))) {
902 a_ptr = a+1;
903 b_ptr = b+1;
904 }
905 a++;
906 b++;
907 }
908
909 if (isdigit(*a) && isdigit(*b)) {
910 return atoi(a_ptr) > atoi(b_ptr) ? 1 : -1;
911 }
912 return *a - *b;
913}
914
915static struct interface *add_interface(char *name)
916{
917 struct interface *ife, **nextp, *new;
918
919 for (ife = int_last; ife; ife = ife->prev) {
920 int n = nstrcmp(ife->name, name);
921
922 if (n == 0)
923 return ife;
924 if (n < 0)
925 break;
926 }
927 new(new);
928 safe_strncpy(new->name, name, IFNAMSIZ);
929 nextp = ife ? &ife->next : &int_list;
930 new->prev = ife;
931 new->next = *nextp;
932 if (new->next)
933 new->next->prev = new;
934 else
935 int_last = new;
936 *nextp = new;
937 return new;
938}
939
940
941static int if_readconf(void)
942{
943 int numreqs = 30;
944 struct ifconf ifc;
945 struct ifreq *ifr;
946 int n, err = -1;
947 int skfd2;
948
949 /* SIOCGIFCONF currently seems to only work properly on AF_INET sockets
950 (as of 2.1.128) */
951 skfd2 = get_socket_for_af(AF_INET);
952 if (skfd2 < 0) {
953 bb_perror_msg(("warning: no inet socket available"));
954 /* Try to soldier on with whatever socket we can get hold of. */
955 skfd2 = sockets_open(0);
956 if (skfd2 < 0)
957 return -1;
958 }
959
960 ifc.ifc_buf = NULL;
961 for (;;) {
962 ifc.ifc_len = sizeof(struct ifreq) * numreqs;
963 ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len);
964
965 if (ioctl(skfd2, SIOCGIFCONF, &ifc) < 0) {
966 perror("SIOCGIFCONF");
967 goto out;
968 }
969 if (ifc.ifc_len == sizeof(struct ifreq) * numreqs) {
970 /* assume it overflowed and try again */
971 numreqs += 10;
972 continue;
973 }
974 break;
975 }
976
977 ifr = ifc.ifc_req;
978 for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) {
979 add_interface(ifr->ifr_name);
980 ifr++;
981 }
982 err = 0;
983
984 out:
985 free(ifc.ifc_buf);
986 return err;
987}
988
989char *get_name(char *name, char *p)
990{
991 /* Extract <name>[:<alias>] from nul-terminated p where p matches
992 <name>[:<alias>]: after leading whitespace.
993 If match is not made, set name empty and return unchanged p */
994 int namestart=0, nameend=0, aliasend;
995 while (isspace(p[namestart]))
996 namestart++;
997 nameend=namestart;
998 while (p[nameend] && p[nameend]!=':' && !isspace(p[nameend]))
999 nameend++;
1000 if (p[nameend]==':') {
1001 aliasend=nameend+1;
1002 while (p[aliasend] && isdigit(p[aliasend]))
1003 aliasend++;
1004 if (p[aliasend]==':') {
1005 nameend=aliasend;
1006 }
1007 if ((nameend-namestart)<IFNAMSIZ) {
1008 memcpy(name,&p[namestart],nameend-namestart);
1009 name[nameend-namestart]='\0';
1010 p=&p[nameend];
1011 } else {
1012 /* Interface name too large */
1013 name[0]='\0';
1014 }
1015 } else {
1016 /* first ':' not found - return empty */
1017 name[0]='\0';
1018 }
1019 return p + 1;
1020}
1021
1022/* If scanf supports size qualifiers for %n conversions, then we can
1023 * use a modified fmt that simply stores the position in the fields
1024 * having no associated fields in the proc string. Of course, we need
1025 * to zero them again when we're done. But that is smaller than the
1026 * old approach of multiple scanf occurrences with large numbers of
1027 * args. */
1028
1029/* static const char *ss_fmt[] = { */
1030/* "%Ln%Lu%lu%lu%lu%lu%ln%ln%Ln%Lu%lu%lu%lu%lu%lu", */
1031/* "%Lu%Lu%lu%lu%lu%lu%ln%ln%Lu%Lu%lu%lu%lu%lu%lu", */
1032/* "%Lu%Lu%lu%lu%lu%lu%lu%lu%Lu%Lu%lu%lu%lu%lu%lu%lu" */
1033/* }; */
1034
1035 /* Lie about the size of the int pointed to for %n. */
1036#if INT_MAX == LONG_MAX
1037static const char *ss_fmt[] = {
1038 "%n%Lu%u%u%u%u%n%n%n%Lu%u%u%u%u%u",
1039 "%Lu%Lu%u%u%u%u%n%n%Lu%Lu%u%u%u%u%u",
1040 "%Lu%Lu%u%u%u%u%u%u%Lu%Lu%u%u%u%u%u%u"
1041};
1042#else
1043static const char *ss_fmt[] = {
1044 "%n%Lu%lu%lu%lu%lu%n%n%n%Lu%lu%lu%lu%lu%lu",
1045 "%Lu%Lu%lu%lu%lu%lu%n%n%Lu%Lu%lu%lu%lu%lu%lu",
1046 "%Lu%Lu%lu%lu%lu%lu%lu%lu%Lu%Lu%lu%lu%lu%lu%lu%lu"
1047};
1048
1049#endif
1050
1051static void get_dev_fields(char *bp, struct interface *ife, int procnetdev_vsn)
1052{
1053 memset(&ife->stats, 0, sizeof(struct user_net_device_stats));
1054
1055 sscanf(bp, ss_fmt[procnetdev_vsn],
1056 &ife->stats.rx_bytes, /* missing for 0 */
1057 &ife->stats.rx_packets,
1058 &ife->stats.rx_errors,
1059 &ife->stats.rx_dropped,
1060 &ife->stats.rx_fifo_errors,
1061 &ife->stats.rx_frame_errors,
1062 &ife->stats.rx_compressed, /* missing for <= 1 */
1063 &ife->stats.rx_multicast, /* missing for <= 1 */
1064 &ife->stats.tx_bytes, /* missing for 0 */
1065 &ife->stats.tx_packets,
1066 &ife->stats.tx_errors,
1067 &ife->stats.tx_dropped,
1068 &ife->stats.tx_fifo_errors,
1069 &ife->stats.collisions,
1070 &ife->stats.tx_carrier_errors,
1071 &ife->stats.tx_compressed /* missing for <= 1 */
1072 );
1073
1074 if (procnetdev_vsn <= 1) {
1075 if (procnetdev_vsn == 0) {
1076 ife->stats.rx_bytes = 0;
1077 ife->stats.tx_bytes = 0;
1078 }
1079 ife->stats.rx_multicast = 0;
1080 ife->stats.rx_compressed = 0;
1081 ife->stats.tx_compressed = 0;
1082 }
1083}
1084
1085static inline int procnetdev_version(char *buf)
1086{
1087 if (strstr(buf, "compressed"))
1088 return 2;
1089 if (strstr(buf, "bytes"))
1090 return 1;
1091 return 0;
1092}
1093
1094static int if_readlist_proc(char *target)
1095{
1096 static int proc_read;
1097 FILE *fh;
1098 char buf[512];
1099 struct interface *ife;
1100 int err, procnetdev_vsn;
1101
1102 if (proc_read)
1103 return 0;
1104 if (!target)
1105 proc_read = 1;
1106
1107 fh = fopen(_PATH_PROCNET_DEV, "r");
1108 if (!fh) {
1109 bb_perror_msg(_("Warning: cannot open %s. Limited output."), _PATH_PROCNET_DEV);
1110 return if_readconf();
1111 }
1112 fgets(buf, sizeof buf, fh); /* eat line */
1113 fgets(buf, sizeof buf, fh);
1114
1115 procnetdev_vsn = procnetdev_version(buf);
1116
1117 err = 0;
1118 while (fgets(buf, sizeof buf, fh)) {
1119 char *s, name[128];
1120
1121 s = get_name(name, buf);
1122 ife = add_interface(name);
1123 get_dev_fields(s, ife, procnetdev_vsn);
1124 ife->statistics_valid = 1;
1125 if (target && !strcmp(target, name))
1126 break;
1127 }
1128 if (ferror(fh)) {
1129 perror(_PATH_PROCNET_DEV);
1130 err = -1;
1131 proc_read = 0;
1132 }
1133 fclose(fh);
1134 return err;
1135}
1136
1137static int if_readlist(void)
1138{
1139 int err = if_readlist_proc(NULL);
1140
1141 if (!err)
1142 err = if_readconf();
1143 return err;
1144}
1145
1146static int for_all_interfaces(int (*doit) (struct interface *, void *),
1147 void *cookie)
1148{
1149 struct interface *ife;
1150
1151 if (!int_list && (if_readlist() < 0))
1152 return -1;
1153 for (ife = int_list; ife; ife = ife->next) {
1154 int err = doit(ife, cookie);
1155
1156 if (err)
1157 return err;
1158 }
1159 return 0;
1160}
1161
1162/* Support for fetching an IPX address */
1163
1164#if HAVE_AFIPX
1165static int ipx_getaddr(int sock, int ft, struct ifreq *ifr)
1166{
1167 ((struct sockaddr_ipx *) &ifr->ifr_addr)->sipx_type = ft;
1168 return ioctl(sock, SIOCGIFADDR, ifr);
1169}
1170#endif
1171
1172
1173/* Fetch the interface configuration from the kernel. */
1174static int if_fetch(struct interface *ife)
1175{
1176 struct ifreq ifr;
1177 int fd;
1178 char *ifname = ife->name;
1179
1180 strcpy(ifr.ifr_name, ifname);
1181 if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0)
1182 return (-1);
1183 ife->flags = ifr.ifr_flags;
1184
1185 strcpy(ifr.ifr_name, ifname);
1186 if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0)
1187 memset(ife->hwaddr, 0, 32);
1188 else
1189 memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8);
1190
1191 ife->type = ifr.ifr_hwaddr.sa_family;
1192
1193 strcpy(ifr.ifr_name, ifname);
1194 if (ioctl(skfd, SIOCGIFMETRIC, &ifr) < 0)
1195 ife->metric = 0;
1196 else
1197 ife->metric = ifr.ifr_metric;
1198
1199 strcpy(ifr.ifr_name, ifname);
1200 if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0)
1201 ife->mtu = 0;
1202 else
1203 ife->mtu = ifr.ifr_mtu;
1204
1205#ifdef HAVE_HWSLIP
1206 if (ife->type == ARPHRD_SLIP || ife->type == ARPHRD_CSLIP ||
1207 ife->type == ARPHRD_SLIP6 || ife->type == ARPHRD_CSLIP6 ||
1208 ife->type == ARPHRD_ADAPT) {
1209#ifdef SIOCGOUTFILL
1210 strcpy(ifr.ifr_name, ifname);
1211 if (ioctl(skfd, SIOCGOUTFILL, &ifr) < 0)
1212 ife->outfill = 0;
1213 else
1214 ife->outfill = (unsigned int) ifr.ifr_data;
1215#endif
1216#ifdef SIOCGKEEPALIVE
1217 strcpy(ifr.ifr_name, ifname);
1218 if (ioctl(skfd, SIOCGKEEPALIVE, &ifr) < 0)
1219 ife->keepalive = 0;
1220 else
1221 ife->keepalive = (unsigned int) ifr.ifr_data;
1222#endif
1223 }
1224#endif
1225
1226 strcpy(ifr.ifr_name, ifname);
1227 if (ioctl(skfd, SIOCGIFMAP, &ifr) < 0)
1228 memset(&ife->map, 0, sizeof(struct ifmap));
1229 else
1230 memcpy(&ife->map, &ifr.ifr_map, sizeof(struct ifmap));
1231
1232 strcpy(ifr.ifr_name, ifname);
1233 if (ioctl(skfd, SIOCGIFMAP, &ifr) < 0)
1234 memset(&ife->map, 0, sizeof(struct ifmap));
1235 else
1236 ife->map = ifr.ifr_map;
1237
1238#ifdef HAVE_TXQUEUELEN
1239 strcpy(ifr.ifr_name, ifname);
1240 if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) < 0)
1241 ife->tx_queue_len = -1; /* unknown value */
1242 else
1243 ife->tx_queue_len = ifr.ifr_qlen;
1244#else
1245 ife->tx_queue_len = -1; /* unknown value */
1246#endif
1247
1248#if HAVE_AFINET
1249 /* IPv4 address? */
1250 fd = get_socket_for_af(AF_INET);
1251 if (fd >= 0) {
1252 strcpy(ifr.ifr_name, ifname);
1253 ifr.ifr_addr.sa_family = AF_INET;
1254 if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
1255 ife->has_ip = 1;
1256 ife->addr = ifr.ifr_addr;
1257 strcpy(ifr.ifr_name, ifname);
1258 if (ioctl(fd, SIOCGIFDSTADDR, &ifr) < 0)
1259 memset(&ife->dstaddr, 0, sizeof(struct sockaddr));
1260 else
1261 ife->dstaddr = ifr.ifr_dstaddr;
1262
1263 strcpy(ifr.ifr_name, ifname);
1264 if (ioctl(fd, SIOCGIFBRDADDR, &ifr) < 0)
1265 memset(&ife->broadaddr, 0, sizeof(struct sockaddr));
1266 else
1267 ife->broadaddr = ifr.ifr_broadaddr;
1268
1269 strcpy(ifr.ifr_name, ifname);
1270 if (ioctl(fd, SIOCGIFNETMASK, &ifr) < 0)
1271 memset(&ife->netmask, 0, sizeof(struct sockaddr));
1272 else
1273 ife->netmask = ifr.ifr_netmask;
1274 } else
1275 memset(&ife->addr, 0, sizeof(struct sockaddr));
1276 }
1277#endif
1278
1279#if HAVE_AFATALK
1280 /* DDP address maybe ? */
1281 fd = get_socket_for_af(AF_APPLETALK);
1282 if (fd >= 0) {
1283 strcpy(ifr.ifr_name, ifname);
1284 if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
1285 ife->ddpaddr = ifr.ifr_addr;
1286 ife->has_ddp = 1;
1287 }
1288 }
1289#endif
1290
1291#if HAVE_AFIPX
1292 /* Look for IPX addresses with all framing types */
1293 fd = get_socket_for_af(AF_IPX);
1294 if (fd >= 0) {
1295 strcpy(ifr.ifr_name, ifname);
1296 if (!ipx_getaddr(fd, IPX_FRAME_ETHERII, &ifr)) {
1297 ife->has_ipx_bb = 1;
1298 ife->ipxaddr_bb = ifr.ifr_addr;
1299 }
1300 strcpy(ifr.ifr_name, ifname);
1301 if (!ipx_getaddr(fd, IPX_FRAME_SNAP, &ifr)) {
1302 ife->has_ipx_sn = 1;
1303 ife->ipxaddr_sn = ifr.ifr_addr;
1304 }
1305 strcpy(ifr.ifr_name, ifname);
1306 if (!ipx_getaddr(fd, IPX_FRAME_8023, &ifr)) {
1307 ife->has_ipx_e3 = 1;
1308 ife->ipxaddr_e3 = ifr.ifr_addr;
1309 }
1310 strcpy(ifr.ifr_name, ifname);
1311 if (!ipx_getaddr(fd, IPX_FRAME_8022, &ifr)) {
1312 ife->has_ipx_e2 = 1;
1313 ife->ipxaddr_e2 = ifr.ifr_addr;
1314 }
1315 }
1316#endif
1317
1318#if HAVE_AFECONET
1319 /* Econet address maybe? */
1320 fd = get_socket_for_af(AF_ECONET);
1321 if (fd >= 0) {
1322 strcpy(ifr.ifr_name, ifname);
1323 if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
1324 ife->ecaddr = ifr.ifr_addr;
1325 ife->has_econet = 1;
1326 }
1327 }
1328#endif
1329
1330 return 0;
1331}
1332
1333
1334static int do_if_fetch(struct interface *ife)
1335{
1336 if (if_fetch(ife) < 0) {
1337 char *errmsg;
1338
1339 if (errno == ENODEV) {
1340 /* Give better error message for this case. */
1341 errmsg = _("Device not found");
1342 } else {
1343 errmsg = strerror(errno);
1344 }
1345 bb_error_msg(_("%s: error fetching interface information: %s\n"),
1346 ife->name, errmsg);
1347 return -1;
1348 }
1349 return 0;
1350}
1351
1352/* This structure defines hardware protocols and their handlers. */
1353struct hwtype {
1354 const char *name;
1355 const char *title;
1356 int type;
1357 int alen;
1358 char *(*print) (unsigned char *);
1359 int (*input) (char *, struct sockaddr *);
1360 int (*activate) (int fd);
1361 int suppress_null_addr;
1362};
1363
1364static struct hwtype unspec_hwtype = {
1365 "unspec", "UNSPEC", -1, 0,
1366 UNSPEC_print, NULL, NULL
1367};
1368
1369static struct hwtype loop_hwtype = {
1370 "loop", "Local Loopback", ARPHRD_LOOPBACK, 0,
1371 NULL, NULL, NULL
1372};
1373
1374#if HAVE_HWETHER
1375#include <net/if_arp.h>
1376
1377#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
1378#include <net/ethernet.h>
1379#else
1380#include <linux/if_ether.h>
1381#endif
1382
1383/* Display an Ethernet address in readable format. */
1384static char *pr_ether(unsigned char *ptr)
1385{
1386 static char buff[64];
1387
1388 snprintf(buff, sizeof(buff), "%02X:%02X:%02X:%02X:%02X:%02X",
1389 (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
1390 (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
1391 );
1392 return (buff);
1393}
1394
1395#ifdef KEEP_UNUSED
1396/* Input an Ethernet address and convert to binary. */
1397static int in_ether(char *bufp, struct sockaddr *sap)
1398{
1399 unsigned char *ptr;
1400 char c, *orig;
1401 int i;
1402 unsigned val;
1403
1404 sap->sa_family = ether_hwtype.type;
1405 ptr = sap->sa_data;
1406
1407 i = 0;
1408 orig = bufp;
1409 while ((*bufp != '\0') && (i < ETH_ALEN)) {
1410 val = 0;
1411 c = *bufp++;
1412 if (isdigit(c))
1413 val = c - '0';
1414 else if (c >= 'a' && c <= 'f')
1415 val = c - 'a' + 10;
1416 else if (c >= 'A' && c <= 'F')
1417 val = c - 'A' + 10;
1418 else {
1419#ifdef DEBUG
1420 bb_error_msg(_("in_ether(%s): invalid ether address!\n"), orig);
1421#endif
1422 errno = EINVAL;
1423 return (-1);
1424 }
1425 val <<= 4;
1426 c = *bufp;
1427 if (isdigit(c))
1428 val |= c - '0';
1429 else if (c >= 'a' && c <= 'f')
1430 val |= c - 'a' + 10;
1431 else if (c >= 'A' && c <= 'F')
1432 val |= c - 'A' + 10;
1433 else if (c == ':' || c == 0)
1434 val >>= 4;
1435 else {
1436#ifdef DEBUG
1437 bb_error_msg(_("in_ether(%s): invalid ether address!"), orig);
1438#endif
1439 errno = EINVAL;
1440 return (-1);
1441 }
1442 if (c != 0)
1443 bufp++;
1444 *ptr++ = (unsigned char) (val & 0377);
1445 i++;
1446
1447 /* We might get a semicolon here - not required. */
1448 if (*bufp == ':') {
1449#ifdef DEBUG
1450 if (i == ETH_ALEN) {
1451 bb_error_msg(_("in_ether(%s): trailing : ignored!"), orig);
1452 }
1453#endif
1454 bufp++;
1455 }
1456 }
1457
1458#ifdef DEBUG
1459 /* That's it. Any trailing junk? */
1460 if ((i == ETH_ALEN) && (*bufp != '\0')) {
1461 bb_error_msg(_("in_ether(%s): trailing junk!"), orig);
1462 errno = EINVAL;
1463 return (-1);
1464 }
1465 bb_error_msg("in_ether(%s): %s", orig, pr_ether(sap->sa_data));
1466#endif
1467
1468 return (0);
1469}
1470#endif /* KEEP_UNUSED */
1471
1472
1473static struct hwtype ether_hwtype = {
1474 "ether", "Ethernet", ARPHRD_ETHER, ETH_ALEN,
1475 pr_ether, NULL /* UNUSED in_ether */ , NULL
1476};
1477
1478
1479#endif /* HAVE_HWETHER */
1480
1481
1482#if HAVE_HWPPP
1483
1484#include <net/if_arp.h>
1485
1486#ifdef KEEP_UNUSED
1487/* Start the PPP encapsulation on the file descriptor. */
1488static int do_ppp(int fd)
1489{
1490 bb_error_msg(_("You cannot start PPP with this program."));
1491 return -1;
1492}
1493#endif /* KEEP_UNUSED */
1494
1495static struct hwtype ppp_hwtype = {
1496 "ppp", "Point-Point Protocol", ARPHRD_PPP, 0,
1497 NULL, NULL, NULL /* UNUSED do_ppp */ , 0
1498};
1499
1500
1501#endif /* HAVE_PPP */
1502
1503static struct hwtype *hwtypes[] = {
1504
1505 &loop_hwtype,
1506
1507#if HAVE_HWSLIP
1508 &slip_hwtype,
1509 &cslip_hwtype,
1510 &slip6_hwtype,
1511 &cslip6_hwtype,
1512 &adaptive_hwtype,
1513#endif
1514#if HAVE_HWSTRIP
1515 &strip_hwtype,
1516#endif
1517#if HAVE_HWASH
1518 &ash_hwtype,
1519#endif
1520#if HAVE_HWETHER
1521 &ether_hwtype,
1522#endif
1523#if HAVE_HWTR
1524 &tr_hwtype,
1525#ifdef ARPHRD_IEEE802_TR
1526 &tr_hwtype1,
1527#endif
1528#endif
1529#if HAVE_HWAX25
1530 &ax25_hwtype,
1531#endif
1532#if HAVE_HWNETROM
1533 &netrom_hwtype,
1534#endif
1535#if HAVE_HWROSE
1536 &rose_hwtype,
1537#endif
1538#if HAVE_HWTUNNEL
1539 &tunnel_hwtype,
1540#endif
1541#if HAVE_HWPPP
1542 &ppp_hwtype,
1543#endif
1544#if HAVE_HWHDLCLAPB
1545 &hdlc_hwtype,
1546 &lapb_hwtype,
1547#endif
1548#if HAVE_HWARC
1549 &arcnet_hwtype,
1550#endif
1551#if HAVE_HWFR
1552 &dlci_hwtype,
1553 &frad_hwtype,
1554#endif
1555#if HAVE_HWSIT
1556 &sit_hwtype,
1557#endif
1558#if HAVE_HWFDDI
1559 &fddi_hwtype,
1560#endif
1561#if HAVE_HWHIPPI
1562 &hippi_hwtype,
1563#endif
1564#if HAVE_HWIRDA
1565 &irda_hwtype,
1566#endif
1567#if HAVE_HWEC
1568 &ec_hwtype,
1569#endif
1570#if HAVE_HWX25
1571 &x25_hwtype,
1572#endif
1573 &unspec_hwtype,
1574 NULL
1575};
1576
1577#ifdef KEEP_UNUSED
1578static short sVhwinit = 0;
1579
1580static void hwinit()
1581{
1582 loop_hwtype.title = _("Local Loopback");
1583 unspec_hwtype.title = _("UNSPEC");
1584#if HAVE_HWSLIP
1585 slip_hwtype.title = _("Serial Line IP");
1586 cslip_hwtype.title = _("VJ Serial Line IP");
1587 slip6_hwtype.title = _("6-bit Serial Line IP");
1588 cslip6_hwtype.title = _("VJ 6-bit Serial Line IP");
1589 adaptive_hwtype.title = _("Adaptive Serial Line IP");
1590#endif
1591#if HAVE_HWETHER
1592 ether_hwtype.title = _("Ethernet");
1593#endif
1594#if HAVE_HWASH
1595 ash_hwtype.title = _("Ash");
1596#endif
1597#if HAVE_HWFDDI
1598 fddi_hwtype.title = _("Fiber Distributed Data Interface");
1599#endif
1600#if HAVE_HWHIPPI
1601 hippi_hwtype.title = _("HIPPI");
1602#endif
1603#if HAVE_HWAX25
1604 ax25_hwtype.title = _("AMPR AX.25");
1605#endif
1606#if HAVE_HWROSE
1607 rose_hwtype.title = _("AMPR ROSE");
1608#endif
1609#if HAVE_HWNETROM
1610 netrom_hwtype.title = _("AMPR NET/ROM");
1611#endif
1612#if HAVE_HWX25
1613 x25_hwtype.title = _("generic X.25");
1614#endif
1615#if HAVE_HWTUNNEL
1616 tunnel_hwtype.title = _("IPIP Tunnel");
1617#endif
1618#if HAVE_HWPPP
1619 ppp_hwtype.title = _("Point-to-Point Protocol");
1620#endif
1621#if HAVE_HWHDLCLAPB
1622 hdlc_hwtype.title = _("(Cisco)-HDLC");
1623 lapb_hwtype.title = _("LAPB");
1624#endif
1625#if HAVE_HWARC
1626 arcnet_hwtype.title = _("ARCnet");
1627#endif
1628#if HAVE_HWFR
1629 dlci_hwtype.title = _("Frame Relay DLCI");
1630 frad_hwtype.title = _("Frame Relay Access Device");
1631#endif
1632#if HAVE_HWSIT
1633 sit_hwtype.title = _("IPv6-in-IPv4");
1634#endif
1635#if HAVE_HWIRDA
1636 irda_hwtype.title = _("IrLAP");
1637#endif
1638#if HAVE_HWTR
1639 tr_hwtype.title = _("16/4 Mbps Token Ring");
1640#ifdef ARPHRD_IEEE802_TR
1641 tr_hwtype1.title = _("16/4 Mbps Token Ring (New)");
1642#endif
1643#endif
1644#if HAVE_HWEC
1645 ec_hwtype.title = _("Econet");
1646#endif
1647 sVhwinit = 1;
1648}
1649#endif /* KEEP_UNUSED */
1650
1651#ifdef IFF_PORTSEL
1652#if 0
1653static const char * const if_port_text[][4] = {
1654 /* Keep in step with <linux/netdevice.h> */
1655 {"unknown", NULL, NULL, NULL},
1656 {"10base2", "bnc", "coax", NULL},
1657 {"10baseT", "utp", "tpe", NULL},
1658 {"AUI", "thick", "db15", NULL},
1659 {"100baseT", NULL, NULL, NULL},
1660 {"100baseTX", NULL, NULL, NULL},
1661 {"100baseFX", NULL, NULL, NULL},
1662 {NULL, NULL, NULL, NULL},
1663};
1664#else
1665static const char * const if_port_text[] = {
1666 /* Keep in step with <linux/netdevice.h> */
1667 "unknown",
1668 "10base2",
1669 "10baseT",
1670 "AUI",
1671 "100baseT",
1672 "100baseTX",
1673 "100baseFX",
1674 NULL
1675};
1676#endif
1677#endif
1678
1679/* Check our hardware type table for this type. */
1680static struct hwtype *get_hwntype(int type)
1681{
1682 struct hwtype **hwp;
1683
1684#ifdef KEEP_UNUSED
1685 if (!sVhwinit)
1686 hwinit();
1687#endif /* KEEP_UNUSED */
1688
1689 hwp = hwtypes;
1690 while (*hwp != NULL) {
1691 if ((*hwp)->type == type)
1692 return (*hwp);
1693 hwp++;
1694 }
1695 return (NULL);
1696}
1697
1698/* return 1 if address is all zeros */
1699static int hw_null_address(struct hwtype *hw, void *ap)
1700{
1701 unsigned int i;
1702 unsigned char *address = (unsigned char *) ap;
1703
1704 for (i = 0; i < hw->alen; i++)
1705 if (address[i])
1706 return 0;
1707 return 1;
1708}
1709
1710static const char TRext[] = "\0\0\0Ki\0Mi\0Gi\0Ti";
1711
1712static void print_bytes_scaled(unsigned long long ull, const char *end)
1713{
1714 unsigned long long int_part;
1715 const char *ext;
1716 unsigned int frac_part;
1717 int i;
1718
1719 frac_part = 0;
1720 ext = TRext;
1721 int_part = ull;
1722 i = 4;
1723 do {
1724#if 0
1725 /* This does correct rounding and is a little larger. But it
1726 * uses KiB as the smallest displayed unit. */
1727 if ((int_part < (1024*1024 - 51)) || !--i) {
1728 i = 0;
1729 int_part += 51; /* 1024*.05 = 51.2 */
1730 frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
1731 }
1732 int_part /= 1024;
1733 ext += 3; /* KiB, MiB, GiB, TiB */
1734#else
1735 if (int_part >= 1024) {
1736 frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
1737 int_part /= 1024;
1738 ext += 3; /* KiB, MiB, GiB, TiB */
1739 }
1740 --i;
1741#endif
1742 } while (i);
1743
1744 printf("X bytes:%Lu (%Lu.%u %sB)%s", ull, int_part, frac_part, ext, end);
1745}
1746
1747static const char * const ife_print_flags_strs[] = {
1748 "UP ",
1749 "BROADCAST ",
1750 "DEBUG ",
1751 "LOOPBACK ",
1752 "POINTOPOINT ",
1753 "NOTRAILERS ",
1754 "RUNNING ",
1755 "NOARP ",
1756 "PROMISC ",
1757 "ALLMULTI ",
1758 "SLAVE ",
1759 "MASTER ",
1760 "MULTICAST ",
1761#ifdef HAVE_DYNAMIC
1762 "DYNAMIC "
1763#endif
1764};
1765
1766static const unsigned short ife_print_flags_mask[] = {
1767 IFF_UP,
1768 IFF_BROADCAST,
1769 IFF_DEBUG,
1770 IFF_LOOPBACK,
1771 IFF_POINTOPOINT,
1772 IFF_NOTRAILERS,
1773 IFF_RUNNING,
1774 IFF_NOARP,
1775 IFF_PROMISC,
1776 IFF_ALLMULTI,
1777 IFF_SLAVE,
1778 IFF_MASTER,
1779 IFF_MULTICAST,
1780#ifdef HAVE_DYNAMIC
1781 IFF_DYNAMIC
1782#endif
1783 0
1784};
1785
1786static void ife_print(struct interface *ptr)
1787{
1788 struct aftype *ap;
1789 struct hwtype *hw;
1790 int hf;
1791 int can_compress = 0;
1792
1793#if HAVE_AFIPX
1794 static struct aftype *ipxtype = NULL;
1795#endif
1796#if HAVE_AFECONET
1797 static struct aftype *ectype = NULL;
1798#endif
1799#if HAVE_AFATALK
1800 static struct aftype *ddptype = NULL;
1801#endif
1802#if HAVE_AFINET6
1803 FILE *f;
1804 char addr6[40], devname[20];
1805 struct sockaddr_in6 sap;
1806 int plen, scope, dad_status, if_idx;
1807 char addr6p[8][5];
1808#endif
1809
1810 ap = get_afntype(ptr->addr.sa_family);
1811 if (ap == NULL)
1812 ap = get_afntype(0);
1813
1814 hf = ptr->type;
1815
1816 if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6)
1817 can_compress = 1;
1818
1819 hw = get_hwntype(hf);
1820 if (hw == NULL)
1821 hw = get_hwntype(-1);
1822
1823 printf(_("%-9.9s Link encap:%s "), ptr->name, _(hw->title));
1824 /* For some hardware types (eg Ash, ATM) we don't print the
1825 hardware address if it's null. */
1826 if (hw->print != NULL && (!(hw_null_address(hw, ptr->hwaddr) &&
1827 hw->suppress_null_addr)))
1828 printf(_("HWaddr %s "), hw->print(ptr->hwaddr));
1829#ifdef IFF_PORTSEL
1830 if (ptr->flags & IFF_PORTSEL) {
1831 printf(_("Media:%s"), if_port_text[ptr->map.port] /* [0] */);
1832 if (ptr->flags & IFF_AUTOMEDIA)
1833 printf(_("(auto)"));
1834 }
1835#endif
1836 printf("\n");
1837
1838#if HAVE_AFINET
1839 if (ptr->has_ip) {
1840 printf(_(" %s addr:%s "), ap->name,
1841 ap->sprint(&ptr->addr, 1));
1842 if (ptr->flags & IFF_POINTOPOINT) {
1843 printf(_(" P-t-P:%s "), ap->sprint(&ptr->dstaddr, 1));
1844 }
1845 if (ptr->flags & IFF_BROADCAST) {
1846 printf(_(" Bcast:%s "), ap->sprint(&ptr->broadaddr, 1));
1847 }
1848 printf(_(" Mask:%s\n"), ap->sprint(&ptr->netmask, 1));
1849 }
1850#endif
1851
1852#if HAVE_AFINET6
1853
1854#define IPV6_ADDR_ANY 0x0000U
1855
1856#define IPV6_ADDR_UNICAST 0x0001U
1857#define IPV6_ADDR_MULTICAST 0x0002U
1858#define IPV6_ADDR_ANYCAST 0x0004U
1859
1860#define IPV6_ADDR_LOOPBACK 0x0010U
1861#define IPV6_ADDR_LINKLOCAL 0x0020U
1862#define IPV6_ADDR_SITELOCAL 0x0040U
1863
1864#define IPV6_ADDR_COMPATv4 0x0080U
1865
1866#define IPV6_ADDR_SCOPE_MASK 0x00f0U
1867
1868#define IPV6_ADDR_MAPPED 0x1000U
1869#define IPV6_ADDR_RESERVED 0x2000U /* reserved address space */
1870
1871 if ((f = fopen(_PATH_PROCNET_IFINET6, "r")) != NULL) {
1872 while (fscanf
1873 (f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %20s\n",
1874 addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4],
1875 addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
1876 &dad_status, devname) != EOF) {
1877 if (!strcmp(devname, ptr->name)) {
1878 sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
1879 addr6p[0], addr6p[1], addr6p[2], addr6p[3],
1880 addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
1881 inet_pton(AF_INET6, addr6,
1882 (struct sockaddr *) &sap.sin6_addr);
1883 sap.sin6_family = AF_INET6;
1884 printf(_(" inet6 addr: %s/%d"),
1885 inet6_aftype.sprint((struct sockaddr *) &sap, 1),
1886 plen);
1887 printf(_(" Scope:"));
1888 switch (scope & IPV6_ADDR_SCOPE_MASK) {
1889 case 0:
1890 printf(_("Global"));
1891 break;
1892 case IPV6_ADDR_LINKLOCAL:
1893 printf(_("Link"));
1894 break;
1895 case IPV6_ADDR_SITELOCAL:
1896 printf(_("Site"));
1897 break;
1898 case IPV6_ADDR_COMPATv4:
1899 printf(_("Compat"));
1900 break;
1901 case IPV6_ADDR_LOOPBACK:
1902 printf(_("Host"));
1903 break;
1904 default:
1905 printf(_("Unknown"));
1906 }
1907 printf("\n");
1908 }
1909 }
1910 fclose(f);
1911 }
1912#endif
1913
1914#if HAVE_AFIPX
1915 if (ipxtype == NULL)
1916 ipxtype = get_afntype(AF_IPX);
1917
1918 if (ipxtype != NULL) {
1919 if (ptr->has_ipx_bb)
1920 printf(_(" IPX/Ethernet II addr:%s\n"),
1921 ipxtype->sprint(&ptr->ipxaddr_bb, 1));
1922 if (ptr->has_ipx_sn)
1923 printf(_(" IPX/Ethernet SNAP addr:%s\n"),
1924 ipxtype->sprint(&ptr->ipxaddr_sn, 1));
1925 if (ptr->has_ipx_e2)
1926 printf(_(" IPX/Ethernet 802.2 addr:%s\n"),
1927 ipxtype->sprint(&ptr->ipxaddr_e2, 1));
1928 if (ptr->has_ipx_e3)
1929 printf(_(" IPX/Ethernet 802.3 addr:%s\n"),
1930 ipxtype->sprint(&ptr->ipxaddr_e3, 1));
1931 }
1932#endif
1933
1934#if HAVE_AFATALK
1935 if (ddptype == NULL)
1936 ddptype = get_afntype(AF_APPLETALK);
1937 if (ddptype != NULL) {
1938 if (ptr->has_ddp)
1939 printf(_(" EtherTalk Phase 2 addr:%s\n"),
1940 ddptype->sprint(&ptr->ddpaddr, 1));
1941 }
1942#endif
1943
1944#if HAVE_AFECONET
1945 if (ectype == NULL)
1946 ectype = get_afntype(AF_ECONET);
1947 if (ectype != NULL) {
1948 if (ptr->has_econet)
1949 printf(_(" econet addr:%s\n"),
1950 ectype->sprint(&ptr->ecaddr, 1));
1951 }
1952#endif
1953
1954 printf(" ");
1955 /* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */
1956
1957 if (ptr->flags == 0) {
1958 printf(_("[NO FLAGS] "));
1959 } else {
1960 int i = 0;
1961 do {
1962 if (ptr->flags & ife_print_flags_mask[i]) {
1963 printf(_(ife_print_flags_strs[i]));
1964 }
1965 } while (ife_print_flags_mask[++i]);
1966 }
1967
1968 /* DONT FORGET TO ADD THE FLAGS IN ife_print_short */
1969 printf(_(" MTU:%d Metric:%d"), ptr->mtu, ptr->metric ? ptr->metric : 1);
1970#ifdef SIOCSKEEPALIVE
1971 if (ptr->outfill || ptr->keepalive)
1972 printf(_(" Outfill:%d Keepalive:%d"), ptr->outfill, ptr->keepalive);
1973#endif
1974 printf("\n");
1975
1976 /* If needed, display the interface statistics. */
1977
1978 if (ptr->statistics_valid) {
1979 /* XXX: statistics are currently only printed for the primary address,
1980 * not for the aliases, although strictly speaking they're shared
1981 * by all addresses.
1982 */
1983 printf(" ");
1984
1985 printf(_("RX packets:%Lu errors:%lu dropped:%lu overruns:%lu frame:%lu\n"),
1986 ptr->stats.rx_packets, ptr->stats.rx_errors,
1987 ptr->stats.rx_dropped, ptr->stats.rx_fifo_errors,
1988 ptr->stats.rx_frame_errors);
1989 if (can_compress)
1990 printf(_(" compressed:%lu\n"),
1991 ptr->stats.rx_compressed);
1992 printf(" ");
1993 printf(_("TX packets:%Lu errors:%lu dropped:%lu overruns:%lu carrier:%lu\n"),
1994 ptr->stats.tx_packets, ptr->stats.tx_errors,
1995 ptr->stats.tx_dropped, ptr->stats.tx_fifo_errors,
1996 ptr->stats.tx_carrier_errors);
1997 printf(_(" collisions:%lu "), ptr->stats.collisions);
1998 if (can_compress)
1999 printf(_("compressed:%lu "), ptr->stats.tx_compressed);
2000 if (ptr->tx_queue_len != -1)
2001 printf(_("txqueuelen:%d "), ptr->tx_queue_len);
2002 printf("\n R");
2003 print_bytes_scaled(ptr->stats.rx_bytes, " T");
2004 print_bytes_scaled(ptr->stats.tx_bytes, "\n");
2005
2006 }
2007
2008 if ((ptr->map.irq || ptr->map.mem_start || ptr->map.dma ||
2009 ptr->map.base_addr)) {
2010 printf(" ");
2011 if (ptr->map.irq)
2012 printf(_("Interrupt:%d "), ptr->map.irq);
2013 if (ptr->map.base_addr >= 0x100) /* Only print devices using it for
2014 I/O maps */
2015 printf(_("Base address:0x%lx "),
2016 (unsigned long) ptr->map.base_addr);
2017 if (ptr->map.mem_start) {
2018 printf(_("Memory:%lx-%lx "), ptr->map.mem_start,
2019 ptr->map.mem_end);
2020 }
2021 if (ptr->map.dma)
2022 printf(_("DMA chan:%x "), ptr->map.dma);
2023 printf("\n");
2024 }
2025 printf("\n");
2026}
2027
2028
2029static int do_if_print(struct interface *ife, void *cookie)
2030{
2031 int *opt_a = (int *) cookie;
2032 int res;
2033
2034 res = do_if_fetch(ife);
2035 if (res >= 0) {
2036 if ((ife->flags & IFF_UP) || *opt_a)
2037 ife_print(ife);
2038 }
2039 return res;
2040}
2041
2042static struct interface *lookup_interface(char *name)
2043{
2044 struct interface *ife = NULL;
2045
2046 if (if_readlist_proc(name) < 0)
2047 return NULL;
2048 ife = add_interface(name);
2049 return ife;
2050}
2051
2052/* for ipv4 add/del modes */
2053static int if_print(char *ifname)
2054{
2055 int res;
2056
2057 if (!ifname) {
2058 res = for_all_interfaces(do_if_print, &interface_opt_a);
2059 } else {
2060 struct interface *ife;
2061
2062 ife = lookup_interface(ifname);
2063 res = do_if_fetch(ife);
2064 if (res >= 0)
2065 ife_print(ife);
2066 }
2067 return res;
2068}
2069
2070int display_interfaces(char *ifname)
2071{
2072 int status;
2073
2074 /* Create a channel to the NET kernel. */
2075 if ((skfd = sockets_open(0)) < 0) {
2076 bb_perror_msg_and_die("socket");
2077 }
2078
2079 /* Do we have to show the current setup? */
2080 status = if_print(ifname);
2081 close(skfd);
2082 exit(status < 0);
2083}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Based in part on code from sash, Copyright (c) 1999 by David I. Bell
6 * Permission has been granted to redistribute this code under the GPL.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include <sys/stat.h>
24#include "libbb.h"
25
26/*
27 * Return TRUE if a fileName is a directory.
28 * Nonexistent files return FALSE.
29 */
30int is_directory(const char *fileName, const int followLinks, struct stat *statBuf)
31{
32 int status;
33 struct stat astatBuf;
34
35 if (statBuf == NULL) {
36 /* set from auto stack buffer */
37 statBuf = &astatBuf;
38 }
39
40 if (followLinks)
41 status = stat(fileName, statBuf);
42 else
43 status = lstat(fileName, statBuf);
44
45 if (status < 0 || !(S_ISDIR(statBuf->st_mode))) {
46 status = FALSE;
47 }
48 else status = TRUE;
49
50 return status;
51}
52
53/* END CODE */
54/*
55Local Variables:
56c-file-style: "linux"
57c-basic-offset: 4
58tab-width: 4
59End:
60*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <string.h>
24#include <stdlib.h>
25#include <sys/utsname.h> /* for uname(2) */
26
27#include "libbb.h"
28
29/* Returns kernel version encoded as major*65536 + minor*256 + patch,
30 * so, for example, to check if the kernel is greater than 2.2.11:
31 * if (get_kernel_revision() <= 2*65536+2*256+11) { <stuff> }
32 */
33extern int get_kernel_revision(void)
34{
35 struct utsname name;
36 char *s;
37 int i, r;
38
39 if (uname(&name) == -1) {
40 bb_perror_msg("cannot get system information");
41 return (0);
42 }
43
44 s = name.release;
45 r = 0;
46 for (i=0 ; i<3 ; i++) {
47 r = r * 256 + atoi(strtok(s, "."));
48 s = NULL;
49 }
50 return r;
51}
52
53/* END CODE */
54/*
55Local Variables:
56c-file-style: "linux"
57c-basic-offset: 4
58tab-width: 4
59End:
60*/
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 @@
1/*
2 * busybox library eXtended function
3 *
4 * Copyright (C) 2001 Larry Doolittle, <ldoolitt@recycle.lbl.gov>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include <string.h>
23#include "libbb.h"
24
25/* Find out if the last character of a string matches the one given Don't
26 * underrun the buffer if the string length is 0. Also avoids a possible
27 * space-hogging inline of strlen() per usage.
28 */
29char * last_char_is(const char *s, int c)
30{
31 char *sret = (char *)s;
32 if (sret) {
33 sret = strrchr(sret, c);
34 if(sret != NULL && *(sret+1) != 0)
35 sret = NULL;
36 }
37 return sret;
38}
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 @@
1#include <stdlib.h>
2#include <string.h>
3#include "unarchive.h"
4#include "libbb.h"
5
6extern llist_t *llist_add_to(llist_t *old_head, char *new_item)
7{
8 llist_t *new_head;
9
10 new_head = xmalloc(sizeof(llist_t));
11 new_head->data = new_item;
12 new_head->link = old_head;
13
14 return(new_head);
15}
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 @@
1/*
2 * issue.c: issue printing code
3 *
4 * Copyright (C) 2003 Bastian Blank <waldi@tuxbox.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 *
20 * Optimize and correcting OCRNL by Vladimir Oleynik <dzo@simtreas.ru>
21 */
22
23#include <sys/param.h> /* MAXHOSTNAMELEN */
24#include <stdio.h>
25#include <unistd.h>
26#include "libbb.h"
27
28#include <sys/utsname.h>
29#include <time.h>
30
31#define LOGIN " login: "
32
33static const char fmtstr_d[] = "%A, %d %B %Y";
34static const char fmtstr_t[] = "%H:%M:%S";
35
36void print_login_issue(const char *issue_file, const char *tty)
37{
38 FILE *fd;
39 int c;
40 char buf[256];
41 const char *outbuf;
42 time_t t;
43 struct utsname uts;
44
45 time(&t);
46 uname(&uts);
47
48 puts("\r"); /* start a new line */
49
50 if ((fd = fopen(issue_file, "r"))) {
51 while ((c = fgetc(fd)) != EOF) {
52 outbuf = buf;
53 buf[0] = c;
54 if(c == '\n') {
55 buf[1] = '\r';
56 buf[2] = 0;
57 } else {
58 buf[1] = 0;
59 }
60 if (c == '\\' || c == '%') {
61 c = fgetc(fd);
62 switch (c) {
63 case 's':
64 outbuf = uts.sysname;
65 break;
66
67 case 'n':
68 outbuf = uts.nodename;
69 break;
70
71 case 'r':
72 outbuf = uts.release;
73 break;
74
75 case 'v':
76 outbuf = uts.version;
77 break;
78
79 case 'm':
80 outbuf = uts.machine;
81 break;
82
83 case 'D':
84 case 'o':
85 getdomainname(buf, sizeof(buf));
86 buf[sizeof(buf) - 1] = '\0';
87 break;
88
89 case 'd':
90 strftime(buf, sizeof(buf), fmtstr_d, localtime(&t));
91 break;
92
93 case 't':
94 strftime(buf, sizeof(buf), fmtstr_t, localtime(&t));
95 break;
96
97 case 'h':
98 gethostname(buf, sizeof(buf) - 1);
99 break;
100
101 case 'l':
102 outbuf = tty;
103 break;
104
105 default:
106 buf[0] = c;
107 }
108 }
109 fputs(outbuf, stdout);
110 }
111
112 fclose(fd);
113
114 fflush(stdout);
115 }
116}
117
118void print_login_prompt(void)
119{
120 char buf[MAXHOSTNAMELEN+1];
121
122 gethostname(buf, MAXHOSTNAMELEN);
123 fputs(buf, stdout);
124
125 fputs(LOGIN, stdout);
126 fflush(stdout);
127}
128
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <errno.h>
24#include <fcntl.h>
25#include <string.h>
26#include <unistd.h>
27#include <sys/ioctl.h>
28#include "libbb.h"
29
30/* Grumble... The 2.6.x kernel breaks asm/posix_types.h
31 * so we get to try and cope as best we can... */
32#include <linux/version.h>
33#include <asm/posix_types.h>
34
35#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
36#define __bb_kernel_dev_t __kernel_old_dev_t
37#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
38#define __bb_kernel_dev_t __kernel_dev_t
39#else
40#define __bb_kernel_dev_t unsigned short
41#endif
42
43/* Stuff stolen from linux/loop.h */
44#define LO_NAME_SIZE 64
45#define LO_KEY_SIZE 32
46#define LOOP_SET_FD 0x4C00
47#define LOOP_CLR_FD 0x4C01
48#define LOOP_SET_STATUS 0x4C02
49#define LOOP_GET_STATUS 0x4C03
50struct loop_info {
51 int lo_number;
52 __bb_kernel_dev_t lo_device;
53 unsigned long lo_inode;
54 __bb_kernel_dev_t lo_rdevice;
55 int lo_offset;
56 int lo_encrypt_type;
57 int lo_encrypt_key_size;
58 int lo_flags;
59 char lo_name[LO_NAME_SIZE];
60 unsigned char lo_encrypt_key[LO_KEY_SIZE];
61 unsigned long lo_init[2];
62 char reserved[4];
63};
64
65extern int del_loop(const char *device)
66{
67 int fd;
68
69 if ((fd = open(device, O_RDONLY)) < 0) {
70 bb_perror_msg("%s", device);
71 return (FALSE);
72 }
73 if (ioctl(fd, LOOP_CLR_FD, 0) < 0) {
74 close(fd);
75 bb_perror_msg("ioctl: LOOP_CLR_FD");
76 return (FALSE);
77 }
78 close(fd);
79 return (TRUE);
80}
81
82extern int set_loop(const char *device, const char *file, int offset,
83 int *loopro)
84{
85 struct loop_info loopinfo;
86 int fd, ffd, mode;
87
88 mode = *loopro ? O_RDONLY : O_RDWR;
89 if ((ffd = open(file, mode)) < 0 && !*loopro
90 && (errno != EROFS || (ffd = open(file, mode = O_RDONLY)) < 0)) {
91 bb_perror_msg("%s", file);
92 return 1;
93 }
94 if ((fd = open(device, mode)) < 0) {
95 close(ffd);
96 bb_perror_msg("%s", device);
97 return 1;
98 }
99 *loopro = (mode == O_RDONLY);
100
101 memset(&loopinfo, 0, sizeof(loopinfo));
102 safe_strncpy(loopinfo.lo_name, file, LO_NAME_SIZE);
103
104 loopinfo.lo_offset = offset;
105
106 loopinfo.lo_encrypt_key_size = 0;
107 if (ioctl(fd, LOOP_SET_FD, ffd) < 0) {
108 bb_perror_msg("ioctl: LOOP_SET_FD");
109 close(fd);
110 close(ffd);
111 return 1;
112 }
113 if (ioctl(fd, LOOP_SET_STATUS, &loopinfo) < 0) {
114 (void) ioctl(fd, LOOP_CLR_FD, 0);
115 bb_perror_msg("ioctl: LOOP_SET_STATUS");
116 close(fd);
117 close(ffd);
118 return 1;
119 }
120 close(fd);
121 close(ffd);
122 return 0;
123}
124
125extern char *find_unused_loop_device(void)
126{
127 char dev[20];
128 int i, fd;
129 struct stat statbuf;
130 struct loop_info loopinfo;
131
132 for (i = 0; i <= 7; i++) {
133 sprintf(dev, LOOP_FORMAT, i);
134 if (stat(dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) {
135 if ((fd = open(dev, O_RDONLY)) >= 0) {
136 if (ioctl(fd, LOOP_GET_STATUS, &loopinfo) != 0) {
137 if (errno == ENXIO) { /* probably free */
138 close(fd);
139 return strdup(dev);
140 }
141 }
142 close(fd);
143 }
144 }
145 }
146 return NULL;
147}
148
149
150/* END CODE */
151/*
152Local Variables:
153c-file-style: "linux"
154c-basic-offset: 4
155tab-width: 4
156End:
157*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * parse_mode implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* Mar 5, 2003 Manuel Novoa III
24 *
25 * This is the main work function for the 'mkdir' applet. As such, it
26 * strives to be SUSv3 compliant in it's behaviour when recursively
27 * making missing parent dirs, and in it's mode setting of the final
28 * directory 'path'.
29 *
30 * To recursively build all missing intermediate directories, make
31 * sure that (flags & FILEUTILS_RECUR) is non-zero. Newly created
32 * intermediate directories will have at least u+wx perms.
33 *
34 * To set specific permissions on 'path', pass the appropriate 'mode'
35 * val. Otherwise, pass -1 to get default permissions.
36 */
37
38#include <errno.h>
39#include <unistd.h>
40#include <sys/stat.h>
41#include "libbb.h"
42
43int bb_make_directory (char *path, long mode, int flags)
44{
45 mode_t mask;
46 const char *fail_msg;
47 char *s = path;
48 char c;
49 struct stat st;
50
51 mask = umask(0);
52 if (mode == -1) {
53 umask(mask);
54 mode = (S_IXUSR | S_IXGRP | S_IXOTH |
55 S_IWUSR | S_IWGRP | S_IWOTH |
56 S_IRUSR | S_IRGRP | S_IROTH) & ~mask;
57 } else {
58 umask(mask & ~0300);
59 }
60
61 do {
62 c = 0;
63
64 if (flags & FILEUTILS_RECUR) { /* Get the parent. */
65 /* Bypass leading non-'/'s and then subsequent '/'s. */
66 while (*s) {
67 if (*s == '/') {
68 do {
69 ++s;
70 } while (*s == '/');
71 c = *s; /* Save the current char */
72 *s = 0; /* and replace it with nul. */
73 break;
74 }
75 ++s;
76 }
77 }
78
79 if (mkdir(path, 0777) < 0) {
80 /* If we failed for any other reason than the directory
81 * already exists, output a diagnostic and return -1.*/
82 if (errno != EEXIST
83 || !(flags & FILEUTILS_RECUR)
84 || (stat(path, &st) < 0 || !S_ISDIR(st.st_mode))) {
85 fail_msg = "create";
86 umask(mask);
87 break;
88 }
89 /* Since the directory exists, don't attempt to change
90 * permissions if it was the full target. Note that
91 * this is not an error conditon. */
92 if (!c) {
93 umask(mask);
94 return 0;
95 }
96 }
97
98 if (!c) {
99 /* Done. If necessary, updated perms on the newly
100 * created directory. Failure to update here _is_
101 * an error.*/
102 umask(mask);
103 if ((mode != -1) && (chmod(path, mode) < 0)){
104 fail_msg = "set permissions of";
105 break;
106 }
107 return 0;
108 }
109
110 /* Remove any inserted nul from the path (recursive mode). */
111 *s = c;
112
113 } while (1);
114
115 bb_perror_msg ("Cannot %s directory `%s'", fail_msg, path);
116 return -1;
117}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include "busybox.h"
22#include "libbb.h"
23
24#ifdef L_full_version
25 const char * const bb_msg_full_version = BB_BANNER " multi-call binary";
26#endif
27#ifdef L_memory_exhausted
28 const char * const bb_msg_memory_exhausted = "memory exhausted";
29#endif
30#ifdef L_invalid_date
31 const char * const bb_msg_invalid_date = "invalid date `%s'";
32#endif
33#ifdef L_io_error
34 const char * const bb_msg_io_error = "%s: input/output error -- %m";
35#endif
36#ifdef L_write_error
37 const char * const bb_msg_write_error = "Write Error";
38#endif
39#ifdef L_name_longer_than_foo
40 const char * const bb_msg_name_longer_than_foo = "Names longer than %d chars not supported.";
41#endif
42#ifdef L_unknown
43 const char * const bb_msg_unknown = "(unknown)";
44#endif
45#ifdef L_can_not_create_raw_socket
46 const char * const bb_msg_can_not_create_raw_socket = "can`t create raw socket";
47#endif
48#ifdef L_perm_denied_are_you_root
49 const char * const bb_msg_perm_denied_are_you_root = "permission denied. (are you root?)";
50#endif
51#ifdef L_msg_standard_input
52 const char * const bb_msg_standard_input = "standard input";
53#endif
54#ifdef L_msg_standard_output
55 const char * const bb_msg_standard_output = "standard output";
56#endif
57
58#ifdef L_passwd_file
59#define PASSWD_FILE "/etc/passwd"
60const char * const bb_path_passwd_file = PASSWD_FILE;
61#endif
62
63#ifdef L_shadow_file
64#define SHADOW_FILE "/etc/shadow"
65const char * const bb_path_shadow_file = SHADOW_FILE;
66#endif
67
68#ifdef L_group_file
69#define GROUP_FILE "/etc/group"
70const char * const bb_path_group_file = GROUP_FILE;
71#endif
72
73#ifdef L_gshadow_file
74#define GSHADOW_FILE "/etc/gshadow"
75const char * const bb_path_gshadow_file = GSHADOW_FILE;
76#endif
77
78#ifdef L_nologin_file
79#define NOLOGIN_FILE "/etc/nologin"
80const char * const bb_path_nologin_file = NOLOGIN_FILE;
81#endif
82
83#ifdef L_securetty_file
84#define SECURETTY_FILE "/etc/securetty"
85const char * const bb_path_securetty_file = SECURETTY_FILE;
86#endif
87
88#ifdef L_motd_file
89#define MOTD_FILE "/etc/motd"
90const char * const bb_path_motd_file = MOTD_FILE;
91#endif
92
93#ifdef L_shell_file
94const char * const bb_default_login_shell = LIBBB_DEFAULT_LOGIN_SHELL;
95#endif
96
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * mode_string implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* Aug 13, 2003
24 * Fix a bug reported by junkio@cox.net involving the mode_chars index.
25 */
26
27
28#include <assert.h>
29#include <sys/stat.h>
30
31#if ( S_ISUID != 04000 ) || ( S_ISGID != 02000 ) || ( S_ISVTX != 01000 ) \
32 || ( S_IRUSR != 00400 ) || ( S_IWUSR != 00200 ) || ( S_IXUSR != 00100 ) \
33 || ( S_IRGRP != 00040 ) || ( S_IWGRP != 00020 ) || ( S_IXGRP != 00010 ) \
34 || ( S_IROTH != 00004 ) || ( S_IWOTH != 00002 ) || ( S_IXOTH != 00001 )
35#error permission bitflag value assumption(s) violated!
36#endif
37
38#if ( S_IFSOCK!= 0140000 ) || ( S_IFLNK != 0120000 ) \
39 || ( S_IFREG != 0100000 ) || ( S_IFBLK != 0060000 ) \
40 || ( S_IFDIR != 0040000 ) || ( S_IFCHR != 0020000 ) \
41 || ( S_IFIFO != 0010000 )
42#warning mode type bitflag value assumption(s) violated! falling back to larger version
43
44#if (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX) == 07777
45#undef mode_t
46#define mode_t unsigned short
47#endif
48
49static const mode_t mode_flags[] = {
50 S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID,
51 S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID,
52 S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX
53};
54
55/* The static const char arrays below are duplicated for the two cases
56 * because moving them ahead of the mode_flags declaration cause a text
57 * size increase with the gcc version I'm using. */
58
59/* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C',
60 * and 'B' types don't appear to be available on linux. So I removed them. */
61static const char type_chars[16] = "?pc?d?b?-?l?s???";
62/* 0123456789abcdef */
63static const char mode_chars[7] = "rwxSTst";
64
65const char *bb_mode_string(int mode)
66{
67 static char buf[12];
68 char *p = buf;
69
70 int i, j, k;
71
72 *p = type_chars[ (mode >> 12) & 0xf ];
73 i = 0;
74 do {
75 j = k = 0;
76 do {
77 *++p = '-';
78 if (mode & mode_flags[i+j]) {
79 *p = mode_chars[j];
80 k = j;
81 }
82 } while (++j < 3);
83 if (mode & mode_flags[i+j]) {
84 *p = mode_chars[3 + (k & 2) + ((i&8) >> 3)];
85 }
86 i += 4;
87 } while (i < 12);
88
89 /* Note: We don't bother with nul termination because bss initialization
90 * should have taken care of that for us. If the user scribbled in buf
91 * memory, they deserve whatever happens. But we'll at least assert. */
92 assert(buf[10] == 0);
93
94 return buf;
95}
96
97#else
98
99/* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C',
100 * and 'B' types don't appear to be available on linux. So I removed them. */
101static const char type_chars[16] = "?pc?d?b?-?l?s???";
102/* 0123456789abcdef */
103static const char mode_chars[7] = "rwxSTst";
104
105const char *bb_mode_string(int mode)
106{
107 static char buf[12];
108 char *p = buf;
109
110 int i, j, k, m;
111
112 *p = type_chars[ (mode >> 12) & 0xf ];
113 i = 0;
114 m = 0400;
115 do {
116 j = k = 0;
117 do {
118 *++p = '-';
119 if (mode & m) {
120 *p = mode_chars[j];
121 k = j;
122 }
123 m >>= 1;
124 } while (++j < 3);
125 ++i;
126 if (mode & (010000 >> i)) {
127 *p = mode_chars[3 + (k & 2) + (i == 3)];
128 }
129 } while (i < 3);
130
131 /* Note: We don't bother with nul termination because bss initialization
132 * should have taken care of that for us. If the user scribbled in buf
133 * memory, they deserve whatever happens. But we'll at least assert. */
134 assert(buf[10] == 0);
135
136 return buf;
137}
138
139#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * some system calls possibly missing from libc
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <stdio.h>
24#include <errno.h>
25#include <unistd.h>
26#include <sys/syscall.h>
27#include "libbb.h"
28
29/* uClibc always supplies (possibly ENOSYS) versions of these functions. */
30#ifndef __UCLIBC__
31
32/* These syscalls are not included in very old glibc versions */
33int delete_module(const char *name)
34{
35#ifndef __NR_delete_module
36#warning This kernel does not support the delete_module syscall
37#warning -> The delete_module system call is being stubbed out...
38 errno=ENOSYS;
39 return -1;
40#else
41 return(syscall(__NR_delete_module, name));
42#endif
43}
44
45int get_kernel_syms(__ptr_t ks)
46{
47#ifndef __NR_get_kernel_syms
48#warning This kernel does not support the get_kernel_syms syscall
49#warning -> The get_kernel_syms system call is being stubbed out...
50 errno=ENOSYS;
51 return -1;
52#else
53 return(syscall(__NR_get_kernel_syms, ks));
54#endif
55}
56
57/* This may have 5 arguments (for old 2.0 kernels) or 2 arguments
58 * (for 2.2 and 2.4 kernels). Use the greatest common denominator,
59 * and let the kernel cope with whatever it gets. Its good at that. */
60int init_module(void *first, void *second, void *third, void *fourth, void *fifth)
61{
62#ifndef __NR_init_module
63#warning This kernel does not support the init_module syscall
64#warning -> The init_module system call is being stubbed out...
65 errno=ENOSYS;
66 return -1;
67#else
68 return(syscall(__NR_init_module, first, second, third, fourth, fifth));
69#endif
70}
71
72int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret)
73{
74#ifndef __NR_query_module
75#warning This kernel does not support the query_module syscall
76#warning -> The query_module system call is being stubbed out...
77 bb_error_msg("\n\nTo make this application work, you will need to recompile\n"
78 "BusyBox with a kernel supporting the query_module system call.\n");
79 errno=ENOSYS;
80 return -1;
81#else
82 return(syscall(__NR_query_module, name, which, buf, bufsize, ret));
83#endif
84}
85
86/* Jump through hoops to fixup error return codes */
87unsigned long create_module(const char *name, size_t size)
88{
89#ifndef __NR_create_module
90#warning This kernel does not support the create_module syscall
91#warning -> The create_module system call is being stubbed out...
92 errno=ENOSYS;
93 return -1;
94#else
95 long ret = syscall(__NR_create_module, name, size);
96
97 if (ret == -1 && errno > 125) {
98 ret = -errno;
99 errno = 0;
100 }
101 return ret;
102#endif
103}
104
105
106#endif /* __UCLIBC__ */
107
108/* END CODE */
109/*
110Local Variables:
111c-file-style: "linux"
112c-basic-offset: 4
113tab-width: 4
114End:
115*/
116
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdlib.h>
23#include <unistd.h>
24#include <errno.h>
25#include <string.h>
26#include <stdio.h>
27#include <mntent.h>
28#include "libbb.h"
29
30#define MTAB_MAX_ENTRIES 40
31static const int MS_RDONLY = 1; /* Mount read-only. */
32
33void erase_mtab(const char *name)
34{
35 struct mntent entries[MTAB_MAX_ENTRIES];
36 int count = 0;
37 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
38 struct mntent *m;
39
40 /* Check if reading the mtab file failed */
41 if (mountTable == 0
42 /* Bummer. fall back on trying the /proc filesystem */
43 && (mountTable = setmntent("/proc/mounts", "r")) == 0) {
44 bb_perror_msg(bb_path_mtab_file);
45 return;
46 }
47
48 while (((m = getmntent(mountTable)) != 0) && (count < MTAB_MAX_ENTRIES))
49 {
50 entries[count].mnt_fsname = strdup(m->mnt_fsname);
51 entries[count].mnt_dir = strdup(m->mnt_dir);
52 entries[count].mnt_type = strdup(m->mnt_type);
53 entries[count].mnt_opts = strdup(m->mnt_opts);
54 entries[count].mnt_freq = m->mnt_freq;
55 entries[count].mnt_passno = m->mnt_passno;
56 count++;
57 }
58 endmntent(mountTable);
59 if ((mountTable = setmntent(bb_path_mtab_file, "w"))) {
60 int i;
61
62 for (i = 0; i < count; i++) {
63 int result = (strcmp(entries[i].mnt_fsname, name) == 0
64 || strcmp(entries[i].mnt_dir, name) == 0);
65
66 if (result)
67 continue;
68 else
69 addmntent(mountTable, &entries[i]);
70 }
71 endmntent(mountTable);
72 } else if (errno != EROFS)
73 bb_perror_msg(bb_path_mtab_file);
74}
75
76void write_mtab(char *blockDevice, char *directory,
77 char *filesystemType, long flags, char *string_flags)
78{
79 FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
80 struct mntent m;
81
82 if (mountTable == 0) {
83 bb_perror_msg(bb_path_mtab_file);
84 return;
85 }
86 if (mountTable) {
87 int length = strlen(directory);
88
89 if (length > 1 && directory[length - 1] == '/')
90 directory[length - 1] = '\0';
91
92 if (filesystemType == 0) {
93 struct mntent *p = find_mount_point(blockDevice, "/proc/mounts");
94
95 if (p && p->mnt_type)
96 filesystemType = p->mnt_type;
97 }
98 m.mnt_fsname = blockDevice;
99 m.mnt_dir = directory;
100 m.mnt_type = filesystemType ? filesystemType : "default";
101
102 if (*string_flags) {
103 m.mnt_opts = string_flags;
104 } else {
105 if ((flags | MS_RDONLY) == flags)
106 m.mnt_opts = "ro";
107 else
108 m.mnt_opts = "rw";
109 }
110
111 m.mnt_freq = 0;
112 m.mnt_passno = 0;
113 addmntent(mountTable, &m);
114 endmntent(mountTable);
115 }
116}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include "libbb.h"
24
25
26/* Busybox mount uses either /proc/mounts or /etc/mtab to
27 * get the list of currently mounted filesystems */
28#if defined CONFIG_FEATURE_MTAB_SUPPORT
29const char bb_path_mtab_file[] = CONFIG_FEATURE_MTAB_FILENAME;
30#else
31const char bb_path_mtab_file[] = "/proc/mounts";
32#endif
33
34
35/* END CODE */
36/*
37Local Variables:
38c-file-style: "linux"
39c-basic-offset: 4
40tab-width: 4
41End:
42*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 /* Hacked by Tito Ragusa (c) 2004 <farmatito@tiscali.it> to make it more
23 * flexible :
24 *
25 * if bufsize is > 0 char *group cannot be set to NULL.
26 * On success groupname is written on static allocated buffer group
27 * (and a pointer to it is returned).
28 * On failure gid as string is written to static allocated buffer
29 * group and NULL is returned.
30 * if bufsize is = 0 char *group can be set to NULL.
31 * On success groupname is returned.
32 * On failure NULL is returned.
33 * if bufsize is < 0 char *group can be set to NULL.
34 * On success groupname is returned.
35 * On failure an error message is printed and the program exits.
36 */
37
38#include "libbb.h"
39#include "grp_.h"
40
41/* gets a groupname given a gid */
42char * my_getgrgid(char *group, long gid, int bufsize)
43{
44 struct group *mygroup = getgrgid(gid);
45
46 return my_getug(group, (mygroup) ? mygroup->gr_name : (char *)mygroup, gid, bufsize, 'g');
47}
48
49
50/* END CODE */
51/*
52Local Variables:
53c-file-style: "linux"
54c-basic-offset: 4
55tab-width: 4
56End:
57*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <string.h>
24#include "libbb.h"
25#include "pwd_.h"
26#include "grp_.h"
27
28
29/* returns a gid given a group name */
30long my_getgrnam(const char *name)
31{
32 struct group *mygroup;
33
34 mygroup = getgrnam(name);
35 if (mygroup==NULL)
36 bb_error_msg_and_die("unknown group name: %s", name);
37
38 return (mygroup->gr_gid);
39}
40
41
42/* END CODE */
43/*
44Local Variables:
45c-file-style: "linux"
46c-basic-offset: 4
47tab-width: 4
48End:
49*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <string.h>
24#include "libbb.h"
25#include "pwd_.h"
26#include "grp_.h"
27
28
29/* returns a uid given a username */
30long my_getpwnam(const char *name)
31{
32 struct passwd *myuser;
33
34 myuser = getpwnam(name);
35 if (myuser==NULL)
36 bb_error_msg_and_die("unknown user name: %s", name);
37
38 return myuser->pw_uid;
39}
40
41
42/* END CODE */
43/*
44Local Variables:
45c-file-style: "linux"
46c-basic-offset: 4
47tab-width: 4
48End:
49*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 /* Hacked by Tito Ragusa (c) 2004 <farmatito@tiscali.it> to make it more
23 * flexible :
24 *
25 * if bufsize is > 0 char *name can not be set to NULL.
26 * On success username is written on the static allocated buffer name
27 * (and a pointer to it is returned).
28 * On failure uid as string is written to the static allocated buffer name
29 * and NULL is returned.
30 * if bufsize is = 0 char *name can be set to NULL.
31 * On success username is returned.
32 * On failure NULL is returned.
33 * if bufsize is < 0 char *name can be set to NULL
34 * On success username is returned.
35 * On failure an error message is printed and the program exits.
36 */
37
38#include "libbb.h"
39#include "pwd_.h"
40
41/* gets a username given a uid */
42char * my_getpwuid(char *name, long uid, int bufsize)
43{
44 struct passwd *myuser = getpwuid(uid);
45
46 return my_getug(name, (myuser) ? myuser->pw_name : (char *)myuser , uid, bufsize, 'u');
47}
48
49/* END CODE */
50/*
51Local Variables:
52c-file-style: "linux"
53c-basic-offset: 4
54tab-width: 4
55End:
56*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 2004 by Tito Ragusa <farmatito@tiscali.it>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 /*
23 *
24 * if bufsize is > 0 char *buffer can not be set to NULL.
25 * If idname is not NULL it is written on the static allocated buffer
26 * (and a pointer to it is returned).
27 * if idname is NULL, id as string is written to the static allocated buffer
28 * and NULL is returned.
29 * if bufsize is = 0 char *buffer can be set to NULL.
30 * If idname exists a pointer to it is returned,
31 * else NULL is returned.
32 * if bufsize is < 0 char *buffer can be set to NULL.
33 * If idname exists a pointer to it is returned,
34 * else an error message is printed and the program exits.
35 */
36
37#include <stdio.h>
38#include <assert.h>
39#include "libbb.h"
40
41
42/* internal function for my_getpwuid and my_getgrgid */
43char * my_getug(char *buffer, char *idname, long id, int bufsize, char prefix)
44{
45 if(bufsize > 0 ) {
46 assert(buffer!=NULL);
47 if(idname) {
48 return safe_strncpy(buffer, idname, bufsize);
49 }
50 snprintf(buffer, bufsize, "%ld", id);
51 } else if(bufsize < 0 && !idname) {
52 bb_error_msg_and_die("unknown %cid %ld", prefix, id);
53 }
54 return idname;
55}
56
57/* END CODE */
58/*
59Local Variables:
60c-file-style: "linux"
61c-basic-offset: 4
62tab-width: 4
63End:
64*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright 1989 - 1994, Julianne Frances Haugh <jockgrrl@austin.rr.com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31/*
32 * This version of obscure.c contains modifications to support "cracklib"
33 * by Alec Muffet (alec.muffett@uk.sun.com). You must obtain the Cracklib
34 * library source code for this function to operate.
35 */
36
37#include <stdlib.h>
38#include <stdio.h>
39#include <string.h>
40#include <ctype.h>
41#include "libbb.h"
42
43/*
44 * can't be a palindrome - like `R A D A R' or `M A D A M'
45 */
46
47static int palindrome(const char *newval)
48{
49 int i, j;
50
51 i = strlen(newval);
52
53 for (j = 0; j < i; j++)
54 if (newval[i - j - 1] != newval[j])
55 return 0;
56
57 return 1;
58}
59
60/*
61 * more than half of the characters are different ones.
62 */
63
64static int similiar(const char *old, const char *newval)
65{
66 int i, j;
67
68 for (i = j = 0; newval[i] && old[i]; i++)
69 if (strchr(newval, old[i]))
70 j++;
71
72 if (i >= j * 2)
73 return 0;
74
75 return 1;
76}
77
78/*
79 * a nice mix of characters.
80 */
81
82static int simple(const char *newval)
83{
84 int digits = 0;
85 int uppers = 0;
86 int lowers = 0;
87 int others = 0;
88 int c;
89 int size;
90 int i;
91
92 for (i = 0; (c = *newval++) != 0; i++) {
93 if (isdigit(c))
94 digits = c;
95 else if (isupper(c))
96 uppers = c;
97 else if (islower(c))
98 lowers = c;
99 else
100 others = c;
101 }
102
103 /*
104 * The scam is this - a password of only one character type
105 * must be 8 letters long. Two types, 7, and so on.
106 */
107
108 size = 9;
109 if (digits)
110 size--;
111 if (uppers)
112 size--;
113 if (lowers)
114 size--;
115 if (others)
116 size--;
117
118 if (size <= i)
119 return 0;
120
121 return 1;
122}
123
124static char *str_lower(char *string)
125{
126 char *cp;
127
128 for (cp = string; *cp; cp++)
129 *cp = tolower(*cp);
130 return string;
131}
132
133static const char *
134password_check(const char *old, const char *newval, const struct passwd *pwdp)
135{
136 const char *msg;
137 char *newmono, *wrapped;
138 int lenwrap;
139
140 if (strcmp(newval, old) == 0)
141 return "no change";
142 if (simple(newval))
143 return "too simple";
144
145 msg = NULL;
146 newmono = str_lower(bb_xstrdup(newval));
147 lenwrap = strlen(old);
148 wrapped = (char *) xmalloc(lenwrap * 2 + 1);
149 str_lower(strcpy(wrapped, old));
150
151 if (palindrome(newmono))
152 msg = "a palindrome";
153
154 else if (strcmp(wrapped, newmono) == 0)
155 msg = "case changes only";
156
157 else if (similiar(wrapped, newmono))
158 msg = "too similiar";
159
160 else {
161 safe_strncpy(wrapped + lenwrap, wrapped, lenwrap + 1);
162 if (strstr(wrapped, newmono))
163 msg = "rotated";
164 }
165
166 bzero(newmono, strlen(newmono));
167 bzero(wrapped, lenwrap * 2);
168 free(newmono);
169 free(wrapped);
170
171 return msg;
172}
173
174static const char *
175obscure_msg(const char *old, const char *newval, const struct passwd *pwdp)
176{
177 int maxlen, oldlen, newlen;
178 char *new1, *old1;
179 const char *msg;
180
181 oldlen = strlen(old);
182 newlen = strlen(newval);
183
184#if 0 /* why not check the password when set for the first time? --marekm */
185 if (old[0] == '\0')
186 /* return (1); */
187 return NULL;
188#endif
189
190 if (newlen < 5)
191 return "too short";
192
193 /*
194 * Remaining checks are optional.
195 */
196 /* Not for us -- Sean
197 *if (!getdef_bool("OBSCURE_CHECKS_ENAB"))
198 * return NULL;
199 */
200 msg = password_check(old, newval, pwdp);
201 if (msg)
202 return msg;
203
204 /* The traditional crypt() truncates passwords to 8 chars. It is
205 possible to circumvent the above checks by choosing an easy
206 8-char password and adding some random characters to it...
207 Example: "password$%^&*123". So check it again, this time
208 truncated to the maximum length. Idea from npasswd. --marekm */
209
210 maxlen = 8;
211 if (oldlen <= maxlen && newlen <= maxlen)
212 return NULL;
213
214 new1 = (char *) bb_xstrdup(newval);
215 old1 = (char *) bb_xstrdup(old);
216 if (newlen > maxlen)
217 new1[maxlen] = '\0';
218 if (oldlen > maxlen)
219 old1[maxlen] = '\0';
220
221 msg = password_check(old1, new1, pwdp);
222
223 bzero(new1, newlen);
224 bzero(old1, oldlen);
225 free(new1);
226 free(old1);
227
228 return msg;
229}
230
231/*
232 * Obscure - see if password is obscure enough.
233 *
234 * The programmer is encouraged to add as much complexity to this
235 * routine as desired. Included are some of my favorite ways to
236 * check passwords.
237 */
238
239extern int obscure(const char *old, const char *newval, const struct passwd *pwdp)
240{
241 const char *msg = obscure_msg(old, newval, pwdp);
242
243 /* if (msg) { */
244 if (msg != NULL) {
245 printf("Bad password: %s.\n", msg);
246 /* return 0; */
247 return 1;
248 }
249 /* return 1; */
250 return 0;
251}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * parse_mode implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
24
25#include <stdlib.h>
26#include <assert.h>
27#include <sys/stat.h>
28#include "libbb.h"
29
30#define FILEMODEBITS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
31
32extern int bb_parse_mode(const char *s, mode_t *current_mode)
33{
34 static const mode_t who_mask[] = {
35 S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */
36 S_ISUID | S_IRWXU, /* u */
37 S_ISGID | S_IRWXG, /* g */
38 S_IRWXO /* o */
39 };
40
41 static const mode_t perm_mask[] = {
42 S_IRUSR | S_IRGRP | S_IROTH, /* r */
43 S_IWUSR | S_IWGRP | S_IWOTH, /* w */
44 S_IXUSR | S_IXGRP | S_IXOTH, /* x */
45 S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */
46 S_ISUID | S_ISGID, /* s */
47 S_ISVTX /* t */
48 };
49
50 static const char who_chars[] = "augo";
51 static const char perm_chars[] = "rwxXst";
52
53 const char *p;
54
55 mode_t wholist;
56 mode_t permlist;
57 mode_t mask;
58 mode_t new_mode;
59 char op;
60
61 assert(s);
62
63 if (((unsigned int)(*s - '0')) < 8) {
64 unsigned long tmp;
65 char *e;
66
67 tmp = strtol(s, &e, 8);
68 if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */
69 return 0;
70 }
71 *current_mode = tmp;
72 return 1;
73 }
74
75 mask = umask(0);
76 umask(mask);
77
78 new_mode = *current_mode;
79
80 /* Note: We allow empty clauses, and hence empty modes.
81 * We treat an empty mode as no change to perms. */
82
83 while (*s) { /* Process clauses. */
84
85 if (*s == ',') { /* We allow empty clauses. */
86 ++s;
87 continue;
88 }
89
90 /* Get a wholist. */
91 wholist = 0;
92
93 WHO_LIST:
94 p = who_chars;
95 do {
96 if (*p == *s) {
97 wholist |= who_mask[(int)(p-who_chars)];
98 if (!*++s) {
99 return 0;
100 }
101 goto WHO_LIST;
102 }
103 } while (*++p);
104
105 do { /* Process action list. */
106 if ((*s != '+') && (*s != '-')) {
107 if (*s != '=') {
108 return 0;
109 }
110 /* Since op is '=', clear all bits corresponding to the
111 * wholist, of all file bits if wholist is empty. */
112 permlist = ~FILEMODEBITS;
113 if (wholist) {
114 permlist = ~wholist;
115 }
116 new_mode &= permlist;
117 }
118 op = *s++;
119
120 /* Check for permcopy. */
121 p = who_chars + 1; /* Skip 'a' entry. */
122 do {
123 if (*p == *s) {
124 int i = 0;
125 permlist = who_mask[(int)(p-who_chars)]
126 & (S_IRWXU | S_IRWXG | S_IRWXO)
127 & new_mode;
128 do {
129 if (permlist & perm_mask[i]) {
130 permlist |= perm_mask[i];
131 }
132 } while (++i < 3);
133 ++s;
134 goto GOT_ACTION;
135 }
136 } while (*++p);
137
138 /* It was not a permcopy, so get a permlist. */
139 permlist = 0;
140
141 PERM_LIST:
142 p = perm_chars;
143 do {
144 if (*p == *s) {
145 if ((*p != 'X')
146 || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH))
147 ) {
148 permlist |= perm_mask[(int)(p-perm_chars)];
149 }
150 if (!*++s) {
151 break;
152 }
153 goto PERM_LIST;
154 }
155 } while (*++p);
156
157 GOT_ACTION:
158 if (permlist) { /* The permlist was nonempty. */
159 mode_t tmp = ~mask;
160 if (wholist) {
161 tmp = wholist;
162 }
163 permlist &= tmp;
164
165 if (op == '-') {
166 new_mode &= ~permlist;
167 } else {
168 new_mode |= permlist;
169 }
170 }
171 } while (*s && (*s != ','));
172 }
173
174 *current_mode = new_mode;
175
176 return 1;
177}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * bb_xparse_number implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <stdlib.h>
24#include <string.h>
25#include <limits.h>
26#include <errno.h>
27#include <assert.h>
28#include "libbb.h"
29
30extern
31unsigned long bb_xparse_number(const char *numstr,
32 const struct suffix_mult *suffixes)
33{
34 unsigned long int r;
35 char *e;
36 int old_errno;
37
38 /* Since this is a lib function, we're not allowed to reset errno to 0.
39 * Doing so could break an app that is deferring checking of errno.
40 * So, save the old value so that we can restore it if successful. */
41 old_errno = errno;
42 errno = 0;
43 r = strtoul(numstr, &e, 10);
44
45 if ((numstr != e) && !errno) {
46 errno = old_errno; /* Ok. So restore errno. */
47 if (!*e) {
48 return r;
49 }
50 if (suffixes) {
51 assert(suffixes->suffix); /* No nul suffixes. */
52 do {
53 if (strcmp(suffixes->suffix, e) == 0) {
54 if (ULONG_MAX / suffixes->mult < r) { /* Overflow! */
55 break;
56 }
57 return r * suffixes->mult;
58 }
59 ++suffixes;
60 } while (suffixes->suffix);
61 }
62 }
63 bb_error_msg_and_die("invalid number `%s'", numstr);
64}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <errno.h>
24#include <string.h>
25#include <stdlib.h>
26#include "libbb.h"
27
28extern void bb_perror_msg(const char *s, ...)
29{
30 va_list p;
31
32 va_start(p, s);
33 bb_vperror_msg(s, p);
34 va_end(p);
35}
36
37
38/* END CODE */
39/*
40Local Variables:
41c-file-style: "linux"
42c-basic-offset: 4
43tab-width: 4
44End:
45*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <errno.h>
24#include <string.h>
25#include <stdlib.h>
26#include "libbb.h"
27
28extern void bb_perror_msg_and_die(const char *s, ...)
29{
30 va_list p;
31
32 va_start(p, s);
33 bb_vperror_msg(s, p);
34 va_end(p);
35 exit(bb_default_error_retval);
36}
37
38
39/* END CODE */
40/*
41Local Variables:
42c-file-style: "linux"
43c-basic-offset: 4
44tab-width: 4
45End:
46*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * bb_perror_nomsg implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <stddef.h>
24#include <libbb.h>
25
26extern void bb_perror_nomsg(void)
27{
28 /* Ignore the gcc warning about a null format string. */
29 bb_perror_msg(NULL);
30}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * bb_perror_nomsg_and_die implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <stddef.h>
24#include "libbb.h"
25
26extern void bb_perror_nomsg_and_die(void)
27{
28 /* Ignore the gcc warning about a null format string. */
29 bb_perror_msg_and_die(NULL);
30}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <unistd.h>
25#include "libbb.h"
26
27extern void bb_xprint_and_close_file(FILE *file)
28{
29 bb_xfflush_stdout();
30 /* Note: Do not use STDOUT_FILENO here, as this is a lib routine
31 * and the calling code may have reassigned stdout. */
32 if (bb_copyfd_eof(fileno(file), STDOUT_FILENO) == -1) {
33 /* bb_copyfd outputs any needed messages, so just die. */
34 exit(bb_default_error_retval);
35 }
36 /* Note: Since we're reading, don't bother checking the return value
37 * of fclose(). The only possible failure is EINTR which
38 * should already have been taken care of. */
39 fclose(file);
40}
41
42/* Returns:
43 * 0 if successful
44 * -1 if 'filename' does not exist or is a directory
45 * exits with default error code if an error occurs
46 */
47
48extern int bb_xprint_file_by_name(const char *filename)
49{
50 FILE *f;
51
52#if 0
53 /* This check shouldn't be necessary for linux, but is left
54 * here disabled just in case. */
55 struct stat statBuf;
56
57 if(is_directory(filename, TRUE, &statBuf)) {
58 bb_error_msg("%s: Is directory", filename);
59 } else
60#endif
61 if ((f = bb_wfopen(filename, "r")) != NULL) {
62 bb_xprint_and_close_file(f);
63 return 0;
64 }
65
66 return -1;
67}
68
69/* END CODE */
70/*
71Local Variables:
72c-file-style: "linux"
73c-basic-offset: 4
74tab-width: 4
75End:
76*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * *printf implementations for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* Mar 12, 2003 Manuel Novoa III
24 *
25 * While fwrite(), fputc(), fputs(), etc. all set the stream error flag
26 * on failure, the *printf functions are unique in that they can fail
27 * for reasons not related to the actual output itself. Among the possible
28 * reasons for failure which don't set the streams error indicator,
29 * SUSv3 lists EILSEQ, EINVAL, and ENOMEM.
30 *
31 * In some cases, it would be desirable to have a group of *printf()
32 * functions available that _always_ set the stream error indicator on
33 * failure. That would allow us to defer error checking until applet
34 * exit. Unfortunately, there is no standard way of setting a streams
35 * error indicator... even though we can clear it with clearerr().
36 *
37 * Therefore, we have to resort to implementation dependent code. Feel
38 * free to send patches for stdio implementations where the following
39 * fails.
40 *
41 * NOTE: None of this is thread safe. As busybox is a non-threaded app,
42 * that isn't currently an issue.
43 */
44
45#include <stdio.h>
46#include <stdarg.h>
47#include "libbb.h"
48
49#if defined(__UCLIBC__)
50
51# if defined(__FLAG_ERROR)
52/* Using my newer stdio implementation. Unlocked macros are:
53 * #define __CLEARERR(stream) \
54 ((stream)->modeflags &= ~(__FLAG_EOF|__FLAG_ERROR), (void)0)
55 * #define __FEOF(stream) ((stream)->modeflags & __FLAG_EOF)
56 * #define __FERROR(stream) ((stream)->modeflags & __FLAG_ERROR)
57 */
58# if defined(__MASK_READING)
59# define SET_FERROR_UNLOCKED(S) ((S)->__modeflags |= __FLAG_ERROR)
60# else
61# define SET_FERROR_UNLOCKED(S) ((S)->modeflags |= __FLAG_ERROR)
62# endif
63
64# elif defined(__MODE_ERR)
65/* Using either the original stdio implementation (from dev86) or
66 * my original stdio rewrite. Macros were:
67 * #define ferror(fp) (((fp)->mode&__MODE_ERR) != 0)
68 * #define feof(fp) (((fp)->mode&__MODE_EOF) != 0)
69 * #define clearerr(fp) ((fp)->mode &= ~(__MODE_EOF|__MODE_ERR),0)
70 */
71#define SET_FERROR_UNLOCKED(S) ((S)->mode |= __MODE_ERR)
72
73# else
74#error unknown uClibc stdio implemenation!
75# endif
76
77#elif defined(__GLIBC__)
78
79# if defined(_STDIO_USES_IOSTREAM)
80/* Apparently using the newer libio implementation, with associated defines:
81 * #define _IO_feof_unlocked(__fp) (((__fp)->_flags & _IO_EOF_SEEN) != 0)
82 * #define _IO_ferror_unlocked(__fp) (((__fp)->_flags & _IO_ERR_SEEN) != 0)
83 */
84#define SET_FERROR_UNLOCKED(S) ((S)->_flags |= _IO_ERR_SEEN)
85
86# else
87/* Assume the older version of glibc which used a bitfield entry
88 * as a stream error flag. The associated defines were:
89 * #define __clearerr(stream) ((stream)->__error = (stream)->__eof = 0)
90 * #define feof_unlocked(stream) ((stream)->__eof != 0)
91 * #define ferror_unlocked(stream) ((stream)->__error != 0)
92 */
93#define SET_FERROR_UNLOCKED(S) ((S)->__error = 1)
94
95# endif
96
97#elif defined(__NEWLIB_H__)
98/* I honestly don't know if there are different versions of stdio in
99 * newlibs history. Anyway, here's what's current.
100 * #define __sfeof(p) (((p)->_flags & __SEOF) != 0)
101 * #define __sferror(p) (((p)->_flags & __SERR) != 0)
102 * #define __sclearerr(p) ((void)((p)->_flags &= ~(__SERR|__SEOF)))
103 */
104#define SET_FERROR_UNLOCKED(S) ((S)->_flags |= __SERR)
105
106#elif defined(__dietlibc__)
107/*
108 * WARNING!!! dietlibc is quite buggy. WARNING!!!
109 *
110 * Some example bugs as of March 12, 2003...
111 * 1) fputc() doesn't set the error indicator on failure.
112 * 2) freopen() doesn't maintain the same stream object, contrary to
113 * standards. This makes it useless in its primary role of
114 * reassociating stdin/stdout/stderr.
115 * 3) printf() often fails to correctly format output when conversions
116 * involve padding. It is also practically useless for floating
117 * point output.
118 *
119 * But, if you're determined to use it anyway, (as of the current version)
120 * you can extract the information you need from dietstdio.h. See the
121 * other library implementations for examples.
122 */
123#error dietlibc is currently not supported. Please see the commented source.
124
125#else /* some other lib */
126/* Please see the comments for the above supported libraries for examples
127 * of what is required to support your stdio implementation.
128 */
129#error Your stdio library is currently not supported. Please see the commented source.
130#endif
131
132#ifdef L_bb_vfprintf
133extern int bb_vfprintf(FILE * __restrict stream,
134 const char * __restrict format,
135 va_list arg)
136{
137 int rv;
138
139 if ((rv = vfprintf(stream, format, arg)) < 0) {
140 SET_FERROR_UNLOCKED(stream);
141 }
142
143 return rv;
144}
145#endif
146
147#ifdef L_bb_vprintf
148extern int bb_vprintf(const char * __restrict format, va_list arg)
149{
150 return bb_vfprintf(stdout, format, arg);
151}
152#endif
153
154#ifdef L_bb_fprintf
155extern int bb_fprintf(FILE * __restrict stream,
156 const char * __restrict format, ...)
157{
158 va_list arg;
159 int rv;
160
161 va_start(arg, format);
162 rv = bb_vfprintf(stream, format, arg);
163 va_end(arg);
164
165 return rv;
166}
167#endif
168
169#ifdef L_bb_printf
170extern int bb_printf(const char * __restrict format, ...)
171{
172 va_list arg;
173 int rv;
174
175 va_start(arg, format);
176 rv = bb_vfprintf(stdout, format, arg);
177 va_end(arg);
178
179 return rv;
180}
181#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) Manuel Novoa III <mjn3@codepoet.org>
6 * and Vladimir Oleynik <dzo@simtreas.ru>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 *
23 */
24
25#include <stdio.h>
26#include <limits.h>
27#include <ctype.h>
28#include "libbb.h"
29
30#define WANT_HEX_ESCAPES 1
31
32/* Usual "this only works for ascii compatible encodings" disclaimer. */
33#undef _tolower
34#define _tolower(X) ((X)|((char) 0x20))
35
36char bb_process_escape_sequence(const char **ptr)
37{
38 static const char charmap[] = {
39 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', 0,
40 '\a', '\b', '\f', '\n', '\r', '\t', '\v', '\\', '\\' };
41
42 const char *p;
43 const char *q;
44 unsigned int num_digits;
45 unsigned int r;
46 unsigned int n;
47 unsigned int d;
48 unsigned int base;
49
50 num_digits = n = 0;
51 base = 8;
52 q = *ptr;
53
54#ifdef WANT_HEX_ESCAPES
55 if (*q == 'x') {
56 ++q;
57 base = 16;
58 ++num_digits;
59 }
60#endif
61
62 do {
63 d = (unsigned int)(*q - '0');
64#ifdef WANT_HEX_ESCAPES
65 if (d >= 10) {
66 d = ((unsigned int)(_tolower(*q) - 'a')) + 10;
67 }
68#endif
69
70 if (d >= base) {
71#ifdef WANT_HEX_ESCAPES
72 if ((base == 16) && (!--num_digits)) {
73/* return '\\'; */
74 --q;
75 }
76#endif
77 break;
78 }
79
80 r = n * base + d;
81 if (r > UCHAR_MAX) {
82 break;
83 }
84
85 n = r;
86 ++q;
87 } while (++num_digits < 3);
88
89 if (num_digits == 0) { /* mnemonic escape sequence? */
90 p = charmap;
91 do {
92 if (*p == *q) {
93 q++;
94 break;
95 }
96 } while (*++p);
97 n = *(p+(sizeof(charmap)/2));
98 }
99
100 *ptr = q;
101
102 return (char) n;
103}
104
105/* END CODE */
106/*
107Local Variables:
108c-file-style: "linux"
109c-basic-offset: 4
110tab-width: 4
111End:
112*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright 1998 by Albert Cahalan; all rights reserved.
6 * Copyright (C) 2002 by Vladimir Oleynik <dzo@simtreas.ru>
7 * GNU Library General Public License Version 2, or any later version
8 *
9 */
10
11#include <dirent.h>
12#include <string.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <asm/page.h>
16
17#include "libbb.h"
18
19extern procps_status_t * procps_scan(int save_user_arg0
20#ifdef CONFIG_SELINUX
21 , int use_selinux , security_id_t *sid
22#endif
23 )
24{
25 static DIR *dir;
26 struct dirent *entry;
27 static procps_status_t ret_status;
28 char *name;
29 int n;
30 char status[32];
31 char buf[1024];
32 FILE *fp;
33 procps_status_t curstatus;
34 int pid;
35 long tasknice;
36 struct stat sb;
37
38 if (!dir) {
39 dir = opendir("/proc");
40 if(!dir)
41 bb_error_msg_and_die("Can't open /proc");
42 }
43 for(;;) {
44 if((entry = readdir(dir)) == NULL) {
45 closedir(dir);
46 dir = 0;
47 return 0;
48 }
49 name = entry->d_name;
50 if (!(*name >= '0' && *name <= '9'))
51 continue;
52
53 memset(&curstatus, 0, sizeof(procps_status_t));
54 pid = atoi(name);
55 curstatus.pid = pid;
56
57 sprintf(status, "/proc/%d", pid);
58 if(stat(status, &sb))
59 continue;
60 my_getpwuid(curstatus.user, sb.st_uid, sizeof(curstatus.user));
61
62 sprintf(status, "/proc/%d/stat", pid);
63 if((fp = fopen(status, "r")) == NULL)
64 continue;
65#ifdef CONFIG_SELINUX
66 if(use_selinux)
67 {
68 if(fstat_secure(fileno(fp), &sb, sid))
69 continue;
70 }
71 else
72#endif
73 name = fgets(buf, sizeof(buf), fp);
74 fclose(fp);
75 if(name == NULL)
76 continue;
77 name = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
78 if(name == 0 || name[1] != ' ')
79 continue;
80 *name = 0;
81 sscanf(buf, "%*s (%15c", curstatus.short_cmd);
82 n = sscanf(name+2,
83 "%c %d "
84 "%*s %*s %*s %*s " /* pgrp, session, tty, tpgid */
85 "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
86#ifdef FEATURE_CPU_USAGE_PERCENTAGE
87 "%lu %lu "
88#else
89 "%*s %*s "
90#endif
91 "%*s %*s %*s " /* cutime, cstime, priority */
92 "%ld "
93 "%*s %*s %*s " /* timeout, it_real_value, start_time */
94 "%*s " /* vsize */
95 "%ld",
96 curstatus.state, &curstatus.ppid,
97#ifdef FEATURE_CPU_USAGE_PERCENTAGE
98 &curstatus.utime, &curstatus.stime,
99#endif
100 &tasknice,
101 &curstatus.rss);
102#ifdef FEATURE_CPU_USAGE_PERCENTAGE
103 if(n != 6)
104#else
105 if(n != 4)
106#endif
107 continue;
108
109 if (curstatus.rss == 0 && curstatus.state[0] != 'Z')
110 curstatus.state[1] = 'W';
111 else
112 curstatus.state[1] = ' ';
113 if (tasknice < 0)
114 curstatus.state[2] = '<';
115 else if (tasknice > 0)
116 curstatus.state[2] = 'N';
117 else
118 curstatus.state[2] = ' ';
119
120#ifdef PAGE_SHIFT
121 curstatus.rss <<= (PAGE_SHIFT - 10); /* 2**10 = 1kb */
122#else
123 curstatus.rss *= (getpagesize() >> 10); /* 2**10 = 1kb */
124#endif
125
126 if(save_user_arg0) {
127 sprintf(status, "/proc/%d/cmdline", pid);
128 if((fp = fopen(status, "r")) == NULL)
129 continue;
130 if((n=fread(buf, 1, sizeof(buf)-1, fp)) > 0) {
131 if(buf[n-1]=='\n')
132 buf[--n] = 0;
133 name = buf;
134 while(n) {
135 if(((unsigned char)*name) < ' ')
136 *name = ' ';
137 name++;
138 n--;
139 }
140 *name = 0;
141 if(buf[0])
142 curstatus.cmd = strdup(buf);
143 /* if NULL it work true also */
144 }
145 fclose(fp);
146 }
147 return memcpy(&ret_status, &curstatus, sizeof(procps_status_t));
148 }
149}
150
151/* END CODE */
152/*
153Local Variables:
154c-file-style: "linux"
155c-basic-offset: 4
156tab-width: 4
157End:
158*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routine.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <string.h>
24#include <crypt.h>
25#include "libbb.h"
26
27
28extern char *pw_encrypt(const char *clear, const char *salt)
29{
30 static char cipher[128];
31 char *cp;
32
33#ifdef CONFIG_FEATURE_SHA1_PASSWORDS
34 if (strncmp(salt, "$2$", 3) == 0) {
35 return sha1_crypt(clear);
36 }
37#endif
38 cp = (char *) crypt(clear, salt);
39 /* if crypt (a nonstandard crypt) returns a string too large,
40 truncate it so we don't overrun buffers and hope there is
41 enough security in what's left */
42 safe_strncpy(cipher, cp, sizeof(cipher));
43 return cipher;
44}
45
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright 1989 - 1994, Julianne Frances Haugh <jockgrrl@austin.rr.com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <time.h>
32#include <sys/types.h>
33#include "libbb.h"
34#include "shadow_.h"
35
36/*
37 * pwd_to_spwd - create entries for new spwd structure
38 *
39 * pwd_to_spwd() creates a new (struct spwd) containing the
40 * information in the pointed-to (struct passwd).
41 */
42#define DAY (24L*3600L)
43#define WEEK (7*DAY)
44#define SCALE DAY
45struct spwd *pwd_to_spwd(const struct passwd *pw)
46{
47 static struct spwd sp;
48
49 /*
50 * Nice, easy parts first. The name and passwd map directly
51 * from the old password structure to the new one.
52 */
53 sp.sp_namp = pw->pw_name;
54 sp.sp_pwdp = pw->pw_passwd;
55
56 /*
57 * Defaults used if there is no pw_age information.
58 */
59 sp.sp_min = 0;
60 sp.sp_max = (10000L * DAY) / SCALE;
61 sp.sp_lstchg = time((time_t *) 0) / SCALE;
62
63 /*
64 * These fields have no corresponding information in the password
65 * file. They are set to uninitialized values.
66 */
67 sp.sp_warn = -1;
68 sp.sp_expire = -1;
69 sp.sp_inact = -1;
70 sp.sp_flag = -1;
71
72 return &sp;
73}
74
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 @@
1/*
2 Copyright (C) 2002 Tim Riker <Tim@Rikers.org>
3 everyone seems to claim it someplace. ;-)
4*/
5
6#include <errno.h>
7
8#include "libbb.h"
9
10int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret);
11
12int my_query_module(const char *name, int which, void **buf,
13 size_t *bufsize, size_t *ret)
14{
15 int my_ret;
16
17 my_ret = query_module(name, which, *buf, *bufsize, ret);
18
19 if (my_ret == -1 && errno == ENOSPC) {
20 *buf = xrealloc(*buf, *ret);
21 *bufsize = *ret;
22
23 my_ret = query_module(name, which, *buf, *bufsize, ret);
24 }
25
26 return my_ret;
27}
28
29
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) many different people.
6 * If you wrote this, please acknowledge your work.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 * USA
22 */
23
24#include <stdlib.h>
25#include <string.h>
26#include "libbb.h"
27
28/*
29 * Gets the next package field from package_buffer, seperated into the field name
30 * and field value, it returns the int offset to the first character of the next field
31 */
32int read_package_field(const char *package_buffer, char **field_name, char **field_value)
33{
34 int offset_name_start = 0;
35 int offset_name_end = 0;
36 int offset_value_start = 0;
37 int offset_value_end = 0;
38 int offset = 0;
39 int next_offset;
40 int name_length;
41 int value_length;
42 int exit_flag = FALSE;
43
44 if (package_buffer == NULL) {
45 *field_name = NULL;
46 *field_value = NULL;
47 return(-1);
48 }
49 while (1) {
50 next_offset = offset + 1;
51 switch (package_buffer[offset]) {
52 case('\0'):
53 exit_flag = TRUE;
54 break;
55 case(':'):
56 if (offset_name_end == 0) {
57 offset_name_end = offset;
58 offset_value_start = next_offset;
59 }
60 /* TODO: Name might still have trailing spaces if ':' isnt
61 * immediately after name */
62 break;
63 case('\n'):
64 /* TODO: The char next_offset may be out of bounds */
65 if (package_buffer[next_offset] != ' ') {
66 exit_flag = TRUE;
67 break;
68 }
69 case('\t'):
70 case(' '):
71 /* increment the value start point if its a just filler */
72 if (offset_name_start == offset) {
73 offset_name_start++;
74 }
75 if (offset_value_start == offset) {
76 offset_value_start++;
77 }
78 break;
79 }
80 if (exit_flag) {
81 /* Check that the names are valid */
82 offset_value_end = offset;
83 name_length = offset_name_end - offset_name_start;
84 value_length = offset_value_end - offset_value_start;
85 if (name_length == 0) {
86 break;
87 }
88 if ((name_length > 0) && (value_length > 0)) {
89 break;
90 }
91
92 /* If not valid, start fresh with next field */
93 exit_flag = FALSE;
94 offset_name_start = offset + 1;
95 offset_name_end = 0;
96 offset_value_start = offset + 1;
97 offset_value_end = offset + 1;
98 offset++;
99 }
100 offset++;
101 }
102 if (name_length == 0) {
103 *field_name = NULL;
104 } else {
105 *field_name = bb_xstrndup(&package_buffer[offset_name_start], name_length);
106 }
107 if (value_length > 0) {
108 *field_value = bb_xstrndup(&package_buffer[offset_value_start], value_length);
109 } else {
110 *field_value = NULL;
111 }
112 return(next_offset);
113}
114
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <string.h>
24#include <dirent.h>
25#include <sys/stat.h>
26#include <stdlib.h> /* free() */
27#include "libbb.h"
28
29#undef DEBUG_RECURS_ACTION
30
31
32/*
33 * Walk down all the directories under the specified
34 * location, and do something (something specified
35 * by the fileAction and dirAction function pointers).
36 *
37 * Unfortunately, while nftw(3) could replace this and reduce
38 * code size a bit, nftw() wasn't supported before GNU libc 2.1,
39 * and so isn't sufficiently portable to take over since glibc2.1
40 * is so stinking huge.
41 */
42int recursive_action(const char *fileName,
43 int recurse, int followLinks, int depthFirst,
44 int (*fileAction) (const char *fileName,
45 struct stat * statbuf,
46 void* userData),
47 int (*dirAction) (const char *fileName,
48 struct stat * statbuf,
49 void* userData),
50 void* userData)
51{
52 int status;
53 struct stat statbuf;
54 struct dirent *next;
55
56 if (followLinks)
57 status = stat(fileName, &statbuf);
58 else
59 status = lstat(fileName, &statbuf);
60
61 if (status < 0) {
62#ifdef DEBUG_RECURS_ACTION
63 bb_error_msg("status=%d followLinks=%d TRUE=%d",
64 status, followLinks, TRUE);
65#endif
66 bb_perror_msg("%s", fileName);
67 return FALSE;
68 }
69
70 if (! followLinks && (S_ISLNK(statbuf.st_mode))) {
71 if (fileAction == NULL)
72 return TRUE;
73 else
74 return fileAction(fileName, &statbuf, userData);
75 }
76
77 if (! recurse) {
78 if (S_ISDIR(statbuf.st_mode)) {
79 if (dirAction != NULL)
80 return (dirAction(fileName, &statbuf, userData));
81 else
82 return TRUE;
83 }
84 }
85
86 if (S_ISDIR(statbuf.st_mode)) {
87 DIR *dir;
88
89 if (dirAction != NULL && ! depthFirst) {
90 status = dirAction(fileName, &statbuf, userData);
91 if (! status) {
92 bb_perror_msg("%s", fileName);
93 return FALSE;
94 } else if (status == SKIP)
95 return TRUE;
96 }
97 dir = opendir(fileName);
98 if (!dir) {
99 bb_perror_msg("%s", fileName);
100 return FALSE;
101 }
102 status = TRUE;
103 while ((next = readdir(dir)) != NULL) {
104 char *nextFile;
105
106 nextFile = concat_subpath_file(fileName, next->d_name);
107 if(nextFile == NULL)
108 continue;
109 if (! recursive_action(nextFile, TRUE, followLinks, depthFirst,
110 fileAction, dirAction, userData)) {
111 status = FALSE;
112 }
113 free(nextFile);
114 }
115 closedir(dir);
116 if (dirAction != NULL && depthFirst) {
117 if (! dirAction(fileName, &statbuf, userData)) {
118 bb_perror_msg("%s", fileName);
119 return FALSE;
120 }
121 }
122 if (! status)
123 return FALSE;
124 } else {
125 if (fileAction == NULL)
126 return TRUE;
127 else
128 return fileAction(fileName, &statbuf, userData);
129 }
130 return TRUE;
131}
132
133
134/* END CODE */
135/*
136Local Variables:
137c-file-style: "linux"
138c-basic-offset: 4
139tab-width: 4
140End:
141*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini remove_file implementation for busybox
4 *
5 * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <time.h>
24#include <utime.h>
25#include <dirent.h>
26#include <errno.h>
27#include <unistd.h>
28#include <stdlib.h>
29#include <string.h>
30#include <getopt.h>
31#include "libbb.h"
32
33extern int remove_file(const char *path, int flags)
34{
35 struct stat path_stat;
36 int path_exists = 1;
37
38 if (lstat(path, &path_stat) < 0) {
39 if (errno != ENOENT) {
40 bb_perror_msg("unable to stat `%s'", path);
41 return -1;
42 }
43
44 path_exists = 0;
45 }
46
47 if (!path_exists) {
48 if (!(flags & FILEUTILS_FORCE)) {
49 bb_perror_msg("cannot remove `%s'", path);
50 return -1;
51 }
52 return 0;
53 }
54
55 if (S_ISDIR(path_stat.st_mode)) {
56 DIR *dp;
57 struct dirent *d;
58 int status = 0;
59
60 if (!(flags & FILEUTILS_RECUR)) {
61 bb_error_msg("%s: is a directory", path);
62 return -1;
63 }
64
65 if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 &&
66 isatty(0)) ||
67 (flags & FILEUTILS_INTERACTIVE)) {
68 fprintf(stderr, "%s: descend into directory `%s'? ", bb_applet_name,
69 path);
70 if (!bb_ask_confirmation())
71 return 0;
72 }
73
74 if ((dp = opendir(path)) == NULL) {
75 bb_perror_msg("unable to open `%s'", path);
76 return -1;
77 }
78
79 while ((d = readdir(dp)) != NULL) {
80 char *new_path;
81
82 new_path = concat_subpath_file(path, d->d_name);
83 if(new_path == NULL)
84 continue;
85 if (remove_file(new_path, flags) < 0)
86 status = -1;
87 free(new_path);
88 }
89
90 if (closedir(dp) < 0) {
91 bb_perror_msg("unable to close `%s'", path);
92 return -1;
93 }
94
95 if (flags & FILEUTILS_INTERACTIVE) {
96 fprintf(stderr, "%s: remove directory `%s'? ", bb_applet_name, path);
97 if (!bb_ask_confirmation())
98 return status;
99 }
100
101 if (rmdir(path) < 0) {
102 bb_perror_msg("unable to remove `%s'", path);
103 return -1;
104 }
105
106 return status;
107 } else {
108 if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 &&
109 !S_ISLNK(path_stat.st_mode) &&
110 isatty(0)) ||
111 (flags & FILEUTILS_INTERACTIVE)) {
112 fprintf(stderr, "%s: remove `%s'? ", bb_applet_name, path);
113 if (!bb_ask_confirmation())
114 return 0;
115 }
116
117 if (unlink(path) < 0) {
118 bb_perror_msg("unable to remove `%s'", path);
119 return -1;
120 }
121
122 return 0;
123 }
124}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <stdio.h>
32#include <errno.h>
33#include <unistd.h>
34#include <string.h>
35#include <stdlib.h>
36#include <syslog.h>
37#include <ctype.h>
38#include "libbb.h"
39
40
41
42/* Return 1 if SHELL is a restricted shell (one not returned by
43 getusershell), else 0, meaning it is a standard shell. */
44
45int restricted_shell ( const char *shell )
46{
47 char *line;
48
49 setusershell ( );
50 while (( line = getusershell ( ))) {
51 if (( *line != '#' ) && ( strcmp ( line, shell ) == 0 ))
52 break;
53 }
54 endusershell ( );
55 return line ? 0 : 1;
56}
57
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * run command from specified directory
4 *
5 *
6 * Copyright (C) 2001 by Emanuele Aina <emanuele.aina@tiscali.it>
7 * rewrite to vfork usage by
8 * Copyright (C) 2002 by Vladimir Oleynik <dzo@simtreas.ru>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 *
16 */
17
18
19#include <sys/types.h>
20#include <sys/wait.h>
21#include <stdlib.h>
22#include <dirent.h>
23#include <unistd.h>
24#include <ctype.h>
25#include <errno.h>
26
27#include "libbb.h"
28
29/* valid_name */
30/* True or false? Is this a valid filename (upper/lower alpha, digits,
31 * underscores, and hyphens only?)
32 */
33static int valid_name(const struct dirent *d)
34{
35 char *c = d->d_name;
36
37 while (*c) {
38 if (!isalnum(*c) && (*c != '_') && (*c != '-')) {
39 return 0;
40 }
41 ++c;
42 }
43 return 1;
44}
45
46/* test mode = 1 is the same as official run_parts
47 * test_mode = 2 means to fail silently on missing directories
48 */
49
50extern int run_parts(char **args, const unsigned char test_mode, char **env)
51{
52 struct dirent **namelist = 0;
53 struct stat st;
54 char *filename;
55 char *arg0 = args[0];
56 int entries;
57 int i;
58 int exitstatus = 0;
59
60#if __GNUC__
61 /* Avoid longjmp clobbering */
62 (void) &i;
63 (void) &exitstatus;
64#endif
65 /* scandir() isn't POSIX, but it makes things easy. */
66 entries = scandir(arg0, &namelist, valid_name, alphasort);
67
68 if (entries == -1) {
69 if (test_mode & 2) {
70 return(2);
71 }
72 bb_perror_msg_and_die("failed to open directory %s", arg0);
73 }
74
75 for (i = 0; i < entries; i++) {
76
77 filename = concat_path_file(arg0, namelist[i]->d_name);
78
79 if (stat(filename, &st) < 0) {
80 bb_perror_msg_and_die("failed to stat component %s", filename);
81 }
82 if (S_ISREG(st.st_mode) && !access(filename, X_OK)) {
83 if (test_mode) {
84 puts(filename);
85 } else {
86 /* exec_errno is common vfork variable */
87 volatile int exec_errno = 0;
88 int result;
89 int pid;
90
91 if ((pid = vfork()) < 0) {
92 bb_perror_msg_and_die("failed to fork");
93 } else if (!pid) {
94 args[0] = filename;
95 execve(filename, args, env);
96 exec_errno = errno;
97 _exit(1);
98 }
99
100 waitpid(pid, &result, 0);
101 if(exec_errno) {
102 errno = exec_errno;
103 bb_perror_msg("failed to exec %s", filename);
104 exitstatus = 1;
105 }
106 if (WIFEXITED(result) && WEXITSTATUS(result)) {
107 bb_perror_msg("%s exited with return code %d", filename, WEXITSTATUS(result));
108 exitstatus = 1;
109 } else if (WIFSIGNALED(result)) {
110 bb_perror_msg("%s exited because of uncaught signal %d", filename, WTERMSIG(result));
111 exitstatus = 1;
112 }
113 }
114 }
115 else if (!S_ISDIR(st.st_mode)) {
116 bb_error_msg("component %s is not an executable plain file", filename);
117 exitstatus = 1;
118 }
119
120 free(namelist[i]);
121 free(filename);
122 }
123 free(namelist);
124
125 return(exitstatus);
126}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <stdio.h>
32#include <errno.h>
33#include <unistd.h>
34#include <string.h>
35#include <stdlib.h>
36#include <syslog.h>
37#include <ctype.h>
38#include "libbb.h"
39#ifdef CONFIG_SELINUX
40#include <proc_secure.h>
41#endif
42
43/* Run SHELL, or DEFAULT_SHELL if SHELL is empty.
44 If COMMAND is nonzero, pass it to the shell with the -c option.
45 If ADDITIONAL_ARGS is nonzero, pass it to the shell as more
46 arguments. */
47
48void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args
49#ifdef CONFIG_SELINUX
50 , security_id_t sid
51#endif
52)
53{
54 const char **args;
55 int argno = 1;
56 int additional_args_cnt = 0;
57
58 for ( args = additional_args; args && *args; args++ )
59 additional_args_cnt++;
60
61 args = (const char **) xmalloc (sizeof (char *) * ( 4 + additional_args_cnt ));
62
63 args [0] = bb_get_last_path_component ( bb_xstrdup ( shell ));
64
65 if ( loginshell ) {
66 char *args0;
67 bb_xasprintf ( &args0, "-%s", args [0] );
68 args [0] = args0;
69 }
70
71 if ( command ) {
72 args [argno++] = "-c";
73 args [argno++] = command;
74 }
75 if ( additional_args ) {
76 for ( ; *additional_args; ++additional_args )
77 args [argno++] = *additional_args;
78 }
79 args [argno] = 0;
80#ifdef CONFIG_SELINUX
81 if(sid)
82 execve_secure(shell, (char **) args, environ, sid);
83 else
84#endif
85 execv ( shell, (char **) args );
86 bb_perror_msg_and_die ( "cannot run %s", shell );
87}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <errno.h>
24#include <unistd.h>
25#include "libbb.h"
26
27
28
29ssize_t safe_read(int fd, void *buf, size_t count)
30{
31 ssize_t n;
32
33 do {
34 n = read(fd, buf, count);
35 } while (n < 0 && errno == EINTR);
36
37 return n;
38}
39
40
41/* END CODE */
42/*
43Local Variables:
44c-file-style: "linux"
45c-basic-offset: 4
46tab-width: 4
47End:
48*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <string.h>
23#include "libbb.h"
24
25
26
27/* Like strncpy but make sure the resulting string is always 0 terminated. */
28extern char * safe_strncpy(char *dst, const char *src, size_t size)
29{
30 dst[size-1] = '\0';
31 return strncpy(dst, src, size-1);
32}
33
34
35/* END CODE */
36/*
37Local Variables:
38c-file-style: "linux"
39c-basic-offset: 4
40tab-width: 4
41End:
42*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdlib.h>
23#include <errno.h>
24#include <assert.h>
25#include "libbb.h"
26
27#ifdef L_safe_strtoi
28extern
29int safe_strtoi(char *arg, int* value)
30{
31 int error;
32 long lvalue = *value;
33 error = safe_strtol(arg, &lvalue);
34 *value = (int) lvalue;
35 return error;
36}
37#endif
38
39#ifdef L_safe_strtod
40extern
41int safe_strtod(char *arg, double* value)
42{
43 char *endptr;
44 int errno_save = errno;
45
46 assert(arg!=NULL);
47 errno = 0;
48 *value = strtod(arg, &endptr);
49 if (errno != 0 || *endptr!='\0' || endptr==arg) {
50 return 1;
51 }
52 errno = errno_save;
53 return 0;
54}
55#endif
56
57#ifdef L_safe_strtol
58extern
59int safe_strtol(char *arg, long* value)
60{
61 char *endptr;
62 int errno_save = errno;
63
64 assert(arg!=NULL);
65 errno = 0;
66 *value = strtol(arg, &endptr, 0);
67 if (errno != 0 || *endptr!='\0' || endptr==arg) {
68 return 1;
69 }
70 errno = errno_save;
71 return 0;
72}
73#endif
74
75#ifdef L_safe_strtoul
76extern
77int safe_strtoul(char *arg, unsigned long* value)
78{
79 char *endptr;
80 int errno_save = errno;
81
82 assert(arg!=NULL);
83 errno = 0;
84 *value = strtoul(arg, &endptr, 0);
85 if (errno != 0 || *endptr!='\0' || endptr==arg) {
86 return 1;
87 }
88 errno = errno_save;
89 return 0;
90}
91#endif
92
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <errno.h>
24#include <unistd.h>
25#include "libbb.h"
26
27
28
29ssize_t safe_write(int fd, const void *buf, size_t count)
30{
31 ssize_t n;
32
33 do {
34 n = write(fd, buf, count);
35 } while (n < 0 && errno == EINTR);
36
37 return n;
38}
39
40
41/* END CODE */
42/*
43Local Variables:
44c-file-style: "linux"
45c-basic-offset: 4
46tab-width: 4
47End:
48*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <stdio.h>
32#include <errno.h>
33#include <unistd.h>
34#include <string.h>
35#include <stdlib.h>
36#include <syslog.h>
37#include <ctype.h>
38#include "libbb.h"
39
40
41
42#define DEFAULT_LOGIN_PATH "/bin:/usr/bin"
43#define DEFAULT_ROOT_LOGIN_PATH "/usr/sbin:/bin:/usr/bin:/sbin"
44
45static void xsetenv ( const char *key, const char *value )
46{
47 if ( setenv ( key, value, 1 ))
48 bb_error_msg_and_die (bb_msg_memory_exhausted);
49}
50
51void setup_environment ( const char *shell, int loginshell, int changeenv, const struct passwd *pw )
52{
53 if ( loginshell ) {
54 const char *term;
55
56 /* Change the current working directory to be the home directory
57 * of the user. It is a fatal error for this process to be unable
58 * to change to that directory. There is no "default" home
59 * directory.
60 * Some systems default to HOME=/
61 */
62 if ( chdir ( pw-> pw_dir )) {
63 if ( chdir ( "/" )) {
64 syslog ( LOG_WARNING, "unable to cd to %s' for user %s'\n", pw-> pw_dir, pw-> pw_name );
65 bb_error_msg_and_die ( "cannot cd to home directory or /" );
66 }
67 fputs ( "warning: cannot change to home directory\n", stderr );
68 }
69
70 /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH.
71 Unset all other environment variables. */
72 term = getenv ("TERM");
73 clearenv ( );
74 if ( term )
75 xsetenv ( "TERM", term );
76 xsetenv ( "HOME", pw-> pw_dir );
77 xsetenv ( "SHELL", shell );
78 xsetenv ( "USER", pw-> pw_name );
79 xsetenv ( "LOGNAME", pw-> pw_name );
80 xsetenv ( "PATH", ( pw-> pw_uid ? DEFAULT_LOGIN_PATH : DEFAULT_ROOT_LOGIN_PATH ));
81 }
82 else if ( changeenv ) {
83 /* Set HOME, SHELL, and if not becoming a super-user,
84 USER and LOGNAME. */
85 xsetenv ( "HOME", pw-> pw_dir );
86 xsetenv ( "SHELL", shell );
87 if ( pw-> pw_uid ) {
88 xsetenv ( "USER", pw-> pw_name );
89 xsetenv ( "LOGNAME", pw-> pw_name );
90 }
91 }
92}
93
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * bb_simplify_path implementation for busybox
4 *
5 * Copyright (C) 2001 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <stdlib.h>
24#include "libbb.h"
25
26char *bb_simplify_path(const char *path)
27{
28 char *s, *start, *p;
29
30 if (path[0] == '/')
31 start = bb_xstrdup(path);
32 else {
33 s = xgetcwd(NULL);
34 start = concat_path_file(s, path);
35 free(s);
36 }
37 p = s = start;
38
39 do {
40 if (*p == '/') {
41 if (*s == '/') { /* skip duplicate (or initial) slash */
42 continue;
43 } else if (*s == '.') {
44 if (s[1] == '/' || s[1] == 0) { /* remove extra '.' */
45 continue;
46 } else if ((s[1] == '.') && (s[2] == '/' || s[2] == 0)) {
47 ++s;
48 if (p > start) {
49 while (*--p != '/'); /* omit previous dir */
50 }
51 continue;
52 }
53 }
54 }
55 *++p = *s;
56 } while (*++s);
57
58 if ((p == start) || (*p != '/')) { /* not a trailing slash */
59 ++p; /* so keep last character */
60 }
61 *p = 0;
62
63 return start;
64}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * skip_whitespace implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <ctype.h>
24#include "libbb.h"
25
26extern const char *bb_skip_whitespace(const char *s)
27{
28 while (isspace(*s)) {
29 ++s;
30 }
31
32 return s;
33}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * compact speed_t <-> speed functions for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <termios.h>
24#include "libbb.h"
25
26struct speed_map {
27 unsigned short speed;
28 unsigned short value;
29};
30
31static const struct speed_map speeds[] = {
32 {B0, 0},
33 {B50, 50},
34 {B75, 75},
35 {B110, 110},
36 {B134, 134},
37 {B150, 150},
38 {B200, 200},
39 {B300, 300},
40 {B600, 600},
41 {B1200, 1200},
42 {B1800, 1800},
43 {B2400, 2400},
44 {B4800, 4800},
45 {B9600, 9600},
46#ifdef B19200
47 {B19200, 19200},
48#elif defined(EXTA)
49 {EXTA, 19200},
50#endif
51#ifdef B38400
52 {B38400, 38400/256 + 0x8000U},
53#elif defined(EXTB)
54 {EXTB, 38400/256 + 0x8000U},
55#endif
56#ifdef B57600
57 {B57600, 57600/256 + 0x8000U},
58#endif
59#ifdef B115200
60 {B115200, 115200/256 + 0x8000U},
61#endif
62#ifdef B230400
63 {B230400, 230400/256 + 0x8000U},
64#endif
65#ifdef B460800
66 {B460800, 460800/256 + 0x8000U},
67#endif
68};
69
70static const int NUM_SPEEDS = (sizeof(speeds) / sizeof(struct speed_map));
71
72unsigned long bb_baud_to_value(speed_t speed)
73{
74 int i = 0;
75
76 do {
77 if (speed == speeds[i].speed) {
78 if (speeds[i].value & 0x8000U) {
79 return ((unsigned long) (speeds[i].value) & 0x7fffU) * 256;
80 }
81 return speeds[i].value;
82 }
83 } while (++i < NUM_SPEEDS);
84
85 return 0;
86}
87
88speed_t bb_value_to_baud(unsigned long value)
89{
90 int i = 0;
91
92 do {
93 if (value == bb_baud_to_value(speeds[i].speed)) {
94 return speeds[i].speed;
95 }
96 } while (++i < NUM_SPEEDS);
97
98 return (speed_t) - 1;
99}
100
101#if 0
102/* testing code */
103#include <stdio.h>
104
105int main(void)
106{
107 unsigned long v;
108 speed_t s;
109
110 for (v = 0 ; v < 500000 ; v++) {
111 s = bb_value_to_baud(v);
112 if (s == (speed_t) -1) {
113 continue;
114 }
115 printf("v = %lu -- s = %0lo\n", v, (unsigned long) s);
116 }
117
118 printf("-------------------------------\n");
119
120 for (s = 0 ; s < 010017+1 ; s++) {
121 v = bb_baud_to_value(s);
122 if (!v) {
123 continue;
124 }
125 printf("v = %lu -- s = %0lo\n", v, (unsigned long) s);
126 }
127
128 return 0;
129}
130#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * some system calls possibly missing from libc
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <stdio.h>
24#include <errno.h>
25#include <unistd.h>
26/* Kernel headers before 2.1.mumble need this on the Alpha to get
27 _syscall* defined. */
28#define __LIBRARY__
29#include <sys/syscall.h>
30#include "libbb.h"
31
32int sysfs( int option, unsigned int fs_index, char * buf)
33{
34 return(syscall(__NR_sysfs, option, fs_index, buf));
35}
36
37int pivot_root(const char * new_root,const char * put_old)
38{
39#ifndef __NR_pivot_root
40#warning This kernel does not support the pivot_root syscall
41#warning -> The pivot_root system call is being stubbed out...
42 /* BusyBox was compiled against a kernel that did not support
43 * the pivot_root system call. To make this application work,
44 * you will need to recompile with a kernel supporting the
45 * pivot_root system call.
46 */
47 bb_error_msg("\n\nTo make this application work, you will need to recompile\n"
48 "BusyBox with a kernel supporting the pivot_root system call.\n");
49 errno=ENOSYS;
50 return -1;
51#else
52 return(syscall(__NR_pivot_root, new_root, put_old));
53#endif
54}
55
56
57
58/* These syscalls are not included in ancient glibc versions */
59#if ((__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1))
60
61int bdflush(int func, int data)
62{
63 return(syscall(__NR_bdflush, func, data));
64}
65
66#ifndef __alpha__
67# define __NR_klogctl __NR_syslog
68int klogctl(int type, char *b, int len)
69{
70 return(syscall(__NR_klogctl, type, b, len));
71}
72#endif
73
74
75int umount2(const char * special_file, int flags)
76{
77#ifndef __NR_pivot_root
78#warning This kernel does not support the umount2 syscall
79#warning -> The umount2 system call is being stubbed out...
80 /* BusyBox was compiled against a kernel that did not support
81 * the umount2 system call. To make this application work,
82 * you will need to recompile with a kernel supporting the
83 * umount2 system call.
84 */
85 bb_error_msg("\n\nTo make this application work, you will need to recompile\n"
86 "BusyBox with a kernel supporting the umount2 system call.\n");
87 errno=ENOSYS;
88 return -1;
89#else
90 return(syscall(__NR_umount2, special_file, flags));
91#endif
92}
93
94
95#endif
96
97
98/* END CODE */
99/*
100Local Variables:
101c-file-style: "linux"
102c-basic-offset: 4
103tab-width: 4
104End:
105*/
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
--- /dev/null
+++ b/busybox/libbb/syslog_msg_with_name.c
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) many different people.
6 * If you wrote this, please acknowledge your work.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 * USA
22 */
23
24#include <stdio.h>
25#include <string.h>
26#include <ctype.h>
27#include "libbb.h"
28
29
30void trim(char *s)
31{
32 int len = strlen(s);
33
34 /* trim trailing whitespace */
35 while ( len > 0 && isspace(s[len-1]))
36 s[--len]='\0';
37
38 /* trim leading whitespace */
39 memmove(s, &s[strspn(s, " \n\r\t\v")], len);
40}
41
42/* END CODE */
43/*
44Local Variables:
45c-file-style: "linux"
46c-basic-offset: 4
47tab-width: 4
48End:
49*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) many different people.
6 * If you wrote this, please acknowledge your work.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 * USA
22 */
23
24#include <signal.h>
25#include <ctype.h>
26#include <string.h>
27#include <stdlib.h>
28#include <stdio.h>
29
30struct signal_name {
31 const char *name;
32 int number;
33};
34
35static const struct signal_name signames[] = {
36 /* POSIX signals */
37 { "EXIT", 0 }, /* 0 */
38 { "HUP", SIGHUP }, /* 1 */
39 { "INT", SIGINT }, /* 2 */
40 { "QUIT", SIGQUIT }, /* 3 */
41 { "ILL", SIGILL }, /* 4 */
42 { "ABRT", SIGABRT }, /* 6 */
43 { "FPE", SIGFPE }, /* 8 */
44 { "KILL", SIGKILL }, /* 9 */
45 { "SEGV", SIGSEGV }, /* 11 */
46 { "PIPE", SIGPIPE }, /* 13 */
47 { "ALRM", SIGALRM }, /* 14 */
48 { "TERM", SIGTERM }, /* 15 */
49 { "USR1", SIGUSR1 }, /* 10 (arm,i386,m68k,ppc), 30 (alpha,sparc*), 16 (mips) */
50 { "USR2", SIGUSR2 }, /* 12 (arm,i386,m68k,ppc), 31 (alpha,sparc*), 17 (mips) */
51 { "CHLD", SIGCHLD }, /* 17 (arm,i386,m68k,ppc), 20 (alpha,sparc*), 18 (mips) */
52 { "CONT", SIGCONT }, /* 18 (arm,i386,m68k,ppc), 19 (alpha,sparc*), 25 (mips) */
53 { "STOP", SIGSTOP }, /* 19 (arm,i386,m68k,ppc), 17 (alpha,sparc*), 23 (mips) */
54 { "TSTP", SIGTSTP }, /* 20 (arm,i386,m68k,ppc), 18 (alpha,sparc*), 24 (mips) */
55 { "TTIN", SIGTTIN }, /* 21 (arm,i386,m68k,ppc,alpha,sparc*), 26 (mips) */
56 { "TTOU", SIGTTOU }, /* 22 (arm,i386,m68k,ppc,alpha,sparc*), 27 (mips) */
57 /* Miscellaneous other signals */
58#ifdef SIGTRAP
59 { "TRAP", SIGTRAP }, /* 5 */
60#endif
61#ifdef SIGIOT
62 { "IOT", SIGIOT }, /* 6, same as SIGABRT */
63#endif
64#ifdef SIGEMT
65 { "EMT", SIGEMT }, /* 7 (mips,alpha,sparc*) */
66#endif
67#ifdef SIGBUS
68 { "BUS", SIGBUS }, /* 7 (arm,i386,m68k,ppc), 10 (mips,alpha,sparc*) */
69#endif
70#ifdef SIGSYS
71 { "SYS", SIGSYS }, /* 12 (mips,alpha,sparc*) */
72#endif
73#ifdef SIGSTKFLT
74 { "STKFLT", SIGSTKFLT }, /* 16 (arm,i386,m68k,ppc) */
75#endif
76#ifdef SIGURG
77 { "URG", SIGURG }, /* 23 (arm,i386,m68k,ppc), 16 (alpha,sparc*), 21 (mips) */
78#endif
79#ifdef SIGIO
80 { "IO", SIGIO }, /* 29 (arm,i386,m68k,ppc), 23 (alpha,sparc*), 22 (mips) */
81#endif
82#ifdef SIGPOLL
83 { "POLL", SIGPOLL }, /* same as SIGIO */
84#endif
85#ifdef SIGCLD
86 { "CLD", SIGCLD }, /* same as SIGCHLD (mips) */
87#endif
88#ifdef SIGXCPU
89 { "XCPU", SIGXCPU }, /* 24 (arm,i386,m68k,ppc,alpha,sparc*), 30 (mips) */
90#endif
91#ifdef SIGXFSZ
92 { "XFSZ", SIGXFSZ }, /* 25 (arm,i386,m68k,ppc,alpha,sparc*), 31 (mips) */
93#endif
94#ifdef SIGVTALRM
95 { "VTALRM", SIGVTALRM }, /* 26 (arm,i386,m68k,ppc,alpha,sparc*), 28 (mips) */
96#endif
97#ifdef SIGPROF
98 { "PROF", SIGPROF }, /* 27 (arm,i386,m68k,ppc,alpha,sparc*), 29 (mips) */
99#endif
100#ifdef SIGPWR
101 { "PWR", SIGPWR }, /* 30 (arm,i386,m68k,ppc), 29 (alpha,sparc*), 19 (mips) */
102#endif
103#ifdef SIGINFO
104 { "INFO", SIGINFO }, /* 29 (alpha) */
105#endif
106#ifdef SIGLOST
107 { "LOST", SIGLOST }, /* 29 (arm,i386,m68k,ppc,sparc*) */
108#endif
109#ifdef SIGWINCH
110 { "WINCH", SIGWINCH }, /* 28 (arm,i386,m68k,ppc,alpha,sparc*), 20 (mips) */
111#endif
112#ifdef SIGUNUSED
113 { "UNUSED", SIGUNUSED }, /* 31 (arm,i386,m68k,ppc) */
114#endif
115 {0, 0}
116};
117
118/*
119 if str_sig == NULL returned signal name [*signo],
120 if str_sig != NULL - set *signo from signal_name,
121 findings with digit number or with or without SIG-prefix name
122
123 if startnum=0 flag for support finding zero signal,
124 but str_sig="0" always found, (hmm - standart or realize?)
125 if startnum<0 returned reverse signal_number <-> signal_name
126 if found error - returned NULL
127
128*/
129
130const char *
131u_signal_names(const char *str_sig, int *signo, int startnum)
132{
133 static char retstr[16];
134 const struct signal_name *s = signames;
135 static const char prefix[] = "SIG";
136 const char *sptr;
137
138 if(startnum)
139 s++;
140 if(str_sig==NULL) {
141 while (s->name != 0) {
142 if(s->number == *signo)
143 break;
144 s++;
145 }
146 } else {
147 if (isdigit(((unsigned char)*str_sig))) {
148 char *endp;
149 long int sn = strtol(str_sig, &endp, 10);
150 /* test correct and overflow */
151 if(*endp == 0 && sn >= 0 && sn < NSIG) {
152 *signo = (int)sn;
153 /* test for unnamed */
154 sptr = u_signal_names(0, signo, 0);
155 if(sptr==NULL)
156 return NULL;
157 if(sn!=0)
158 sptr += 3;
159 return sptr;
160 }
161 } else {
162 sptr = str_sig;
163 while (s->name != 0) {
164 if (strcasecmp(s->name, sptr) == 0) {
165 *signo = s->number;
166 if(startnum<0) {
167 sprintf(retstr, "%d", *signo);
168 return retstr;
169 }
170 break;
171 }
172 if(s!=signames && sptr == str_sig &&
173 strncasecmp(sptr, prefix, 3) == 0) {
174 sptr += 3; /* strlen(prefix) */
175 continue;
176 }
177 sptr = str_sig;
178 s++;
179 }
180 }
181 }
182 if(s->name==0)
183 return NULL;
184 if(s!=signames)
185 strcpy(retstr, prefix);
186 else
187 retstr[0] = 0;
188 return strcat(retstr, s->name);
189}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <unistd.h>
24#include "libbb.h"
25
26
27
28#if (__GLIBC__ < 2)
29extern int vdprintf(int d, const char *format, va_list ap)
30{
31 char buf[BUF_SIZE];
32 int len;
33
34 len = vsprintf(buf, format, ap);
35 return write(d, buf, len);
36}
37#endif
38
39
40/* END CODE */
41/*
42Local Variables:
43c-file-style: "linux"
44c-basic-offset: 4
45tab-width: 4
46End:
47*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <errno.h>
24#include <string.h>
25#include <stdlib.h>
26#include "libbb.h"
27
28extern void bb_verror_msg(const char *s, va_list p)
29{
30 fflush(stdout);
31 fprintf(stderr, "%s: ", bb_applet_name);
32 vfprintf(stderr, s, p);
33}
34
35
36/* END CODE */
37/*
38Local Variables:
39c-file-style: "linux"
40c-basic-offset: 4
41tab-width: 4
42End:
43*/
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 @@
1/*
2 * Rexec program for system have fork() as vfork() with foreground option
3 *
4 * Copyright (C) Vladimir N. Oleynik <dzo@simtreas.ru>
5 * Copyright (C) 2003 Russ Dill <Russ.Dill@asu.edu>
6 *
7 * daemon() portion taken from uClibc:
8 *
9 * Copyright (c) 1991, 1993
10 * The Regents of the University of California. All rights reserved.
11 *
12 * Modified for uClibc by Erik Andersen <andersee@debian.org>
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 */
28
29#include <unistd.h>
30#include <stdio.h>
31#include <fcntl.h>
32#include <paths.h>
33#include "libbb.h"
34
35
36#if defined(__uClinux__)
37void vfork_daemon_rexec(int nochdir, int noclose,
38 int argc, char **argv, char *foreground_opt)
39{
40 int fd;
41 char **vfork_args;
42 int a = 0;
43
44 setsid();
45
46 if (!nochdir)
47 chdir("/");
48
49 if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
50 dup2(fd, STDIN_FILENO);
51 dup2(fd, STDOUT_FILENO);
52 dup2(fd, STDERR_FILENO);
53 if (fd > 2)
54 close(fd);
55 }
56
57 vfork_args = xcalloc(sizeof(char *), argc + 3);
58 vfork_args[a++] = "/bin/busybox";
59 while(*argv) {
60 vfork_args[a++] = *argv;
61 argv++;
62 }
63 vfork_args[a] = foreground_opt;
64 switch (vfork()) {
65 case 0: /* child */
66 /* Make certain we are not a session leader, or else we
67 * might reacquire a controlling terminal */
68 if (vfork())
69 _exit(0);
70 execv(vfork_args[0], vfork_args);
71 bb_perror_msg_and_die("execv %s", vfork_args[0]);
72 case -1: /* error */
73 bb_perror_msg_and_die("vfork");
74 default: /* parent */
75 exit(0);
76 }
77}
78#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdarg.h>
23#include <netdb.h>
24#include <stdio.h>
25
26#include "libbb.h"
27
28
29extern void bb_vherror_msg(const char *s, va_list p)
30{
31 if(s == 0)
32 s = "";
33 bb_verror_msg(s, p);
34 if (*s)
35 fputs(": ", stderr);
36 herror("");
37}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <errno.h>
24#include <string.h>
25#include <stdlib.h>
26#include "libbb.h"
27
28extern void bb_vperror_msg(const char *s, va_list p)
29{
30 int err=errno;
31 if(s == 0) s = "";
32 bb_verror_msg(s, p);
33 if (*s) s = ": ";
34 fprintf(stderr, "%s%s\n", s, strerror(err));
35}
36
37
38/* END CODE */
39/*
40Local Variables:
41c-file-style: "linux"
42c-basic-offset: 4
43tab-width: 4
44End:
45*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * warn_ignoring_args implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <libbb.h>
24
25extern void bb_warn_ignoring_args(int n)
26{
27 if (n) {
28 bb_perror_msg("ignoring all arguments");
29 }
30}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <errno.h>
24#include "libbb.h"
25
26FILE *bb_wfopen(const char *path, const char *mode)
27{
28 FILE *fp;
29 if ((fp = fopen(path, mode)) == NULL) {
30 bb_perror_msg("%s", path);
31 errno = 0;
32 }
33 return fp;
34}
35
36
37/* END CODE */
38/*
39Local Variables:
40c-file-style: "linux"
41c-basic-offset: 4
42tab-width: 4
43End:
44*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * wfopen_input implementation for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* A number of applets need to open a file for reading, where the filename
24 * is a command line arg. Since often that arg is '-' (meaning stdin),
25 * we avoid testing everywhere by consolidating things in this routine.
26 *
27 * Note: We also consider "" to main stdin (for 'cmp' at least).
28 */
29
30#include <stdio.h>
31#include <sys/stat.h>
32#include <libbb.h>
33
34FILE *bb_wfopen_input(const char *filename)
35{
36 FILE *fp = stdin;
37
38 if ((filename != bb_msg_standard_input)
39 && filename[0] && ((filename[0] != '-') || filename[1])
40 ) {
41#if 0
42 /* This check shouldn't be necessary for linux, but is left
43 * here disabled just in case. */
44 struct stat stat_buf;
45 if (is_directory(filename, 1, &stat_buf)) {
46 bb_error_msg("%s: Is a directory", filename);
47 return NULL;
48 }
49#endif
50 fp = bb_wfopen(filename, "r");
51 }
52
53 return fp;
54}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Connect to host at port using address resolution from getaddrinfo
6 *
7 */
8
9#include <unistd.h>
10#include <string.h>
11#include <stdlib.h>
12#include <sys/types.h>
13#include <sys/socket.h>
14#include <errno.h>
15#include <netdb.h>
16#include <sys/socket.h>
17#include <netinet/in.h>
18#include <arpa/inet.h>
19#include "libbb.h"
20
21/* Return network byte ordered port number for a service.
22 * If "port" is a number use it as the port.
23 * If "port" is a name it is looked up in /etc/services, if it isnt found return
24 * default_port
25 */
26unsigned short bb_lookup_port(const char *port, const char *protocol, unsigned short default_port)
27{
28 unsigned short port_nr = htons(default_port);
29 if (port) {
30 char *endptr;
31 int old_errno;
32 long port_long;
33
34 /* Since this is a lib function, we're not allowed to reset errno to 0.
35 * Doing so could break an app that is deferring checking of errno. */
36 old_errno = errno;
37 errno = 0;
38 port_long = strtol(port, &endptr, 10);
39 if (errno != 0 || *endptr!='\0' || endptr==port || port_long < 0 || port_long > 65535) {
40 struct servent *tserv = getservbyname(port, protocol);
41 if (tserv) {
42 port_nr = tserv->s_port;
43 }
44 } else {
45 port_nr = htons(port_long);
46 }
47 errno = old_errno;
48 }
49 return port_nr;
50}
51
52void bb_lookup_host(struct sockaddr_in *s_in, const char *host)
53{
54 struct hostent *he;
55
56 memset(s_in, 0, sizeof(struct sockaddr_in));
57 s_in->sin_family = AF_INET;
58 he = xgethostbyname(host);
59 memcpy(&(s_in->sin_addr), he->h_addr_list[0], he->h_length);
60}
61
62int xconnect(struct sockaddr_in *s_addr)
63{
64 int s = socket(AF_INET, SOCK_STREAM, 0);
65 if (connect(s, (struct sockaddr_in *)s_addr, sizeof(struct sockaddr_in)) < 0)
66 {
67 bb_perror_msg_and_die("Unable to connect to remote host (%s)",
68 inet_ntoa(s_addr->sin_addr));
69 }
70 return s;
71}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <stdio.h>
25#include <string.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <fcntl.h>
29#include "libbb.h"
30
31
32#ifndef DMALLOC
33#ifdef L_xmalloc
34extern void *xmalloc(size_t size)
35{
36 void *ptr = malloc(size);
37 if (ptr == NULL && size != 0)
38 bb_error_msg_and_die(bb_msg_memory_exhausted);
39 return ptr;
40}
41#endif
42
43#ifdef L_xrealloc
44extern void *xrealloc(void *ptr, size_t size)
45{
46 ptr = realloc(ptr, size);
47 if (ptr == NULL && size != 0)
48 bb_error_msg_and_die(bb_msg_memory_exhausted);
49 return ptr;
50}
51#endif
52
53#ifdef L_xcalloc
54extern void *xcalloc(size_t nmemb, size_t size)
55{
56 void *ptr = calloc(nmemb, size);
57 if (ptr == NULL && nmemb != 0 && size != 0)
58 bb_error_msg_and_die(bb_msg_memory_exhausted);
59 return ptr;
60}
61#endif
62#endif /* DMALLOC */
63
64#ifdef L_xstrdup
65extern char * bb_xstrdup (const char *s) {
66 char *t;
67
68 if (s == NULL)
69 return NULL;
70
71 t = strdup (s);
72
73 if (t == NULL)
74 bb_error_msg_and_die(bb_msg_memory_exhausted);
75
76 return t;
77}
78#endif
79
80#ifdef L_xstrndup
81extern char * bb_xstrndup (const char *s, int n) {
82 char *t;
83
84 if (s == NULL)
85 bb_error_msg_and_die("bb_xstrndup bug");
86
87 t = xmalloc(++n);
88
89 return safe_strncpy(t,s,n);
90}
91#endif
92
93#ifdef L_xfopen
94FILE *bb_xfopen(const char *path, const char *mode)
95{
96 FILE *fp;
97 if ((fp = fopen(path, mode)) == NULL)
98 bb_perror_msg_and_die("%s", path);
99 return fp;
100}
101#endif
102
103#ifdef L_xopen
104extern int bb_xopen(const char *pathname, int flags)
105{
106 int ret;
107
108 ret = open(pathname, flags, 0777);
109 if (ret == -1) {
110 bb_perror_msg_and_die("%s", pathname);
111 }
112 return ret;
113}
114#endif
115
116#ifdef L_xread
117extern ssize_t bb_xread(int fd, void *buf, size_t count)
118{
119 ssize_t size;
120
121 size = read(fd, buf, count);
122 if (size == -1) {
123 bb_perror_msg_and_die("Read error");
124 }
125 return(size);
126}
127#endif
128
129#ifdef L_xread_all
130extern void bb_xread_all(int fd, void *buf, size_t count)
131{
132 ssize_t size;
133
134 while (count) {
135 if ((size = bb_xread(fd, buf, count)) == 0) { /* EOF */
136 bb_error_msg_and_die("Short read");
137 }
138 count -= size;
139 buf = ((char *) buf) + size;
140 }
141 return;
142}
143#endif
144
145#ifdef L_xread_char
146extern unsigned char bb_xread_char(int fd)
147{
148 char tmp;
149
150 bb_xread_all(fd, &tmp, 1);
151
152 return(tmp);
153}
154#endif
155
156#ifdef L_xferror
157extern void bb_xferror(FILE *fp, const char *fn)
158{
159 if (ferror(fp)) {
160 bb_error_msg_and_die("%s", fn);
161 }
162}
163#endif
164
165#ifdef L_xferror_stdout
166extern void bb_xferror_stdout(void)
167{
168 bb_xferror(stdout, bb_msg_standard_output);
169}
170#endif
171
172#ifdef L_xfflush_stdout
173extern void bb_xfflush_stdout(void)
174{
175 if (fflush(stdout)) {
176 bb_perror_msg_and_die(bb_msg_standard_output);
177 }
178}
179#endif
180
181#ifdef L_strlen
182/* Stupid gcc always includes its own builtin strlen()... */
183#undef strlen
184size_t bb_strlen(const char *string)
185{
186 return(strlen(string));
187}
188#endif
189
190/* END CODE */
191/*
192Local Variables:
193c-file-style: "linux"
194c-basic-offset: 4
195tab-width: 4
196End:
197*/
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 @@
1/*
2 * xgetcwd.c -- return current directory with unlimited length
3 * Copyright (C) 1992, 1996 Free Software Foundation, Inc.
4 * Written by David MacKenzie <djm@gnu.ai.mit.edu>.
5 *
6 * Special function for busybox written by Vladimir Oleynik <dzo@simtreas.ru>
7*/
8
9#include <stdlib.h>
10#include <errno.h>
11#include <unistd.h>
12#include <limits.h>
13#include <sys/param.h>
14#include "libbb.h"
15
16/* Amount to increase buffer size by in each try. */
17#define PATH_INCR 32
18
19/* Return the current directory, newly allocated, arbitrarily long.
20 Return NULL and set errno on error.
21 If argument is not NULL (previous usage allocate memory), call free()
22*/
23
24char *
25xgetcwd (char *cwd)
26{
27 char *ret;
28 unsigned path_max;
29
30 path_max = (unsigned) PATH_MAX;
31 path_max += 2; /* The getcwd docs say to do this. */
32
33 if(cwd==0)
34 cwd = xmalloc (path_max);
35
36 while ((ret = getcwd (cwd, path_max)) == NULL && errno == ERANGE) {
37 path_max += PATH_INCR;
38 cwd = xrealloc (cwd, path_max);
39 }
40
41 if (ret == NULL) {
42 free (cwd);
43 bb_perror_msg("getcwd()");
44 return NULL;
45 }
46
47 return cwd;
48}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini xgethostbyname implementation.
4 *
5 * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <netdb.h>
24#include "libbb.h"
25
26
27struct hostent *xgethostbyname(const char *name)
28{
29 struct hostent *retval;
30
31 if ((retval = gethostbyname(name)) == NULL)
32 bb_herror_msg_and_die("%s", name);
33
34 return retval;
35}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini xgethostbyname2 implementation.
4 *
5 * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <netdb.h>
24#include "libbb.h"
25
26
27#ifdef CONFIG_FEATURE_IPV6
28struct hostent *xgethostbyname2(const char *name, int af)
29{
30 struct hostent *retval;
31
32 if ((retval = gethostbyname2(name, af)) == NULL)
33 bb_herror_msg_and_die("%s", name);
34
35 return retval;
36}
37#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Copyright (C) 2003-2004 Erik Andersen <andersen@codepoet.org>
4 */
5
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <getopt.h>
10#include <errno.h>
11#include <assert.h>
12#include <ctype.h>
13
14#include "busybox.h"
15
16extern long bb_xgetlarg(const char *arg, int base, long lower, long upper)
17{
18 long result;
19 char *endptr;
20 int errno_save = errno;
21
22 assert(arg!=NULL);
23
24 /* Don't allow leading whitespace. */
25 if ((isspace)(*arg)) { /* Use an actual funciton call for minimal size. */
26 bb_show_usage();
27 }
28
29 errno = 0;
30 result = strtol(arg, &endptr, base);
31 if (errno != 0 || *endptr!='\0' || endptr==arg || result < lower || result > upper)
32 bb_show_usage();
33 errno = errno_save;
34 return result;
35}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * xgetularg* implementations for busybox
4 *
5 * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <stdlib.h>
24#include <string.h>
25#include <limits.h>
26#include <ctype.h>
27#include <errno.h>
28#include <assert.h>
29#include "libbb.h"
30
31#ifdef L_xgetularg_bnd_sfx
32extern
33unsigned long bb_xgetularg_bnd_sfx(const char *arg, int base,
34 unsigned long lower,
35 unsigned long upper,
36 const struct suffix_mult *suffixes)
37{
38 unsigned long r;
39 int old_errno;
40 char *e;
41
42 assert(arg);
43
44 /* Disallow '-' and any leading whitespace. Speed isn't critical here
45 * since we're parsing commandline args. So make sure we get the
46 * actual isspace function rather than a larger macro implementaion. */
47 if ((*arg == '-') || (isspace)(*arg)) {
48 bb_show_usage();
49 }
50
51 /* Since this is a lib function, we're not allowed to reset errno to 0.
52 * Doing so could break an app that is deferring checking of errno.
53 * So, save the old value so that we can restore it if successful. */
54 old_errno = errno;
55 errno = 0;
56 r = strtoul(arg, &e, base);
57 /* Do the initial validity check. Note: The standards do not
58 * guarantee that errno is set if no digits were found. So we
59 * must test for this explicitly. */
60 if (errno || (arg == e)) { /* error or no digits */
61 bb_show_usage();
62 }
63 errno = old_errno; /* Ok. So restore errno. */
64
65 /* Do optional suffix parsing. Allow 'empty' suffix tables.
66 * Note that we also all nul suffixes with associated multipliers,
67 * to allow for scaling of the arg by some default multiplier. */
68
69 if (suffixes) {
70 while (suffixes->suffix) {
71 if (strcmp(suffixes->suffix, e) == 0) {
72 if (ULONG_MAX / suffixes->mult < r) { /* Overflow! */
73 bb_show_usage();
74 }
75 ++e;
76 r *= suffixes->mult;
77 break;
78 }
79 ++suffixes;
80 }
81 }
82
83 /* Finally, check for illegal trailing chars and range limits. */
84 /* Note: although we allow leading space (via stroul), trailing space
85 * is an error. It would be easy enough to allow though if desired. */
86 if (*e || (r < lower) || (r > upper)) {
87 bb_show_usage();
88 }
89
90 return r;
91}
92#endif
93
94#ifdef L_xgetlarg_bnd_sfx
95extern
96long bb_xgetlarg_bnd_sfx(const char *arg, int base,
97 long lower,
98 long upper,
99 const struct suffix_mult *suffixes)
100{
101 unsigned long u = LONG_MAX;
102 long r;
103 const char *p = arg;
104
105 if ((*p == '-') && (p[1] != '+')) {
106 ++p;
107#if LONG_MAX == (-(LONG_MIN + 1))
108 ++u; /* two's complement */
109#endif
110 }
111
112 r = bb_xgetularg_bnd_sfx(p, base, 0, u, suffixes);
113
114 if (*arg == '-') {
115 r = -r;
116 }
117
118 if ((r < lower) || (r > upper)) {
119 bb_show_usage();
120 }
121
122 return r;
123}
124#endif
125
126#ifdef L_getlarg10_sfx
127extern
128long bb_xgetlarg10_sfx(const char *arg, const struct suffix_mult *suffixes)
129{
130 return bb_xgetlarg_bnd_sfx(arg, 10, LONG_MIN, LONG_MAX, suffixes);
131}
132#endif
133
134#ifdef L_xgetularg_bnd
135extern
136unsigned long bb_xgetularg_bnd(const char *arg, int base,
137 unsigned long lower,
138 unsigned long upper)
139{
140 return bb_xgetularg_bnd_sfx(arg, base, lower, upper, NULL);
141}
142#endif
143
144#ifdef L_xgetularg10_bnd
145extern
146unsigned long bb_xgetularg10_bnd(const char *arg,
147 unsigned long lower,
148 unsigned long upper)
149{
150 return bb_xgetularg_bnd(arg, 10, lower, upper);
151}
152#endif
153
154#ifdef L_xgetularg10
155extern
156unsigned long bb_xgetularg10(const char *arg)
157{
158 return bb_xgetularg10_bnd(arg, 0, ULONG_MAX);
159}
160#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 @@
1/*
2 * xreadlink.c - safe implementation of readlink.
3 * Returns a NULL on failure...
4 */
5
6#include <stdio.h>
7
8/*
9 * NOTE: This function returns a malloced char* that you will have to free
10 * yourself. You have been warned.
11 */
12
13#include <unistd.h>
14#include "libbb.h"
15
16extern char *xreadlink(const char *path)
17{
18 static const int GROWBY = 80; /* how large we will grow strings by */
19
20 char *buf = NULL;
21 int bufsize = 0, readsize = 0;
22
23 do {
24 buf = xrealloc(buf, bufsize += GROWBY);
25 readsize = readlink(path, buf, bufsize); /* 1st try */
26 if (readsize == -1) {
27 bb_perror_msg("%s", path);
28 free(buf);
29 return NULL;
30 }
31 }
32 while (bufsize < readsize + 1);
33
34 buf[readsize] = '\0';
35
36 return buf;
37}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Utility routines.
4 *
5 * Copyright (C) many different people.
6 * If you wrote this, please acknowledge your work.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 * USA
22 */
23
24#include <stdio.h>
25#include "libbb.h"
26#include <regex.h>
27
28
29
30void xregcomp(regex_t *preg, const char *regex, int cflags)
31{
32 int ret;
33 if ((ret = regcomp(preg, regex, cflags)) != 0) {
34 int errmsgsz = regerror(ret, preg, NULL, 0);
35 char *errmsg = xmalloc(errmsgsz);
36 regerror(ret, preg, errmsg, errmsgsz);
37 bb_error_msg_and_die("xregcomp: %s", errmsg);
38 }
39}
40
41
42/* END CODE */
43/*
44Local Variables:
45c-file-style: "linux"
46c-basic-offset: 4
47tab-width: 4
48End:
49*/
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/libpwgrp
23LIBPWDGRP_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20LIBPWDGRP_AR:=libpwdgrp.a
21ifndef $(LIBPWDGRP_DIR)
22LIBPWDGRP_DIR:=$(top_builddir)/libpwdgrp/
23endif
24srcdir=$(top_srcdir)/libpwdgrp
25
26
27LIBPWDGRP_MSRC0:=$(srcdir)/pwd_grp.c
28LIBPWDGRP_MOBJ0-$(CONFIG_USE_BB_PWD_GRP):= fgetpwent_r.o fgetgrent_r.o \
29 fgetpwent.o fgetgrent.o getpwnam_r.o getgrnam_r.o getpwuid_r.o \
30 getgrgid_r.o getpwuid.o getgrgid.o getpwnam.o getgrnam.o getpw.o \
31 getpwent_r.o getgrent_r.o getpwent.o getgrent.o \
32 initgroups.o putpwent.o putgrent.o
33LIBPWDGRP_MOBJS0=$(patsubst %,$(LIBPWDGRP_DIR)%, $(LIBPWDGRP_MOBJ0-y))
34
35LIBPWDGRP_MSRC1:=$(srcdir)/pwd_grp.c
36LIBPWDGRP_MOBJ1-$(CONFIG_USE_BB_PWD_GRP):= __parsepwent.o __parsegrent.o \
37 __pgsreader.o fgetspent_r.o fgetspent.o sgetspent_r.o getspnam_r.o \
38 getspnam.o getspent_r.o getspent.o sgetspent.o \
39 putspent.o __parsespent.o # getspuid_r.o getspuid.o
40LIBPWDGRP_MOBJS1=$(patsubst %,$(LIBPWDGRP_DIR)%, $(LIBPWDGRP_MOBJ1-y))
41
42libraries-y+=$(LIBPWDGRP_DIR)$(LIBPWDGRP_AR)
43
44$(LIBPWDGRP_DIR)$(LIBPWDGRP_AR): $(LIBPWDGRP_MOBJS0) $(LIBPWDGRP_MOBJS1)
45 $(AR) -ro $@ $(LIBPWDGRP_MOBJS0) $(LIBPWDGRP_MOBJS1)
46
47$(LIBPWDGRP_MOBJS0): $(LIBPWDGRP_MSRC0)
48 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@
49
50$(LIBPWDGRP_MOBJS1): $(LIBPWDGRP_MSRC1)
51 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DL_$(notdir $*) -c $< -o $@
52
53
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 @@
1/* Copyright (C) 2003 Manuel Novoa III
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Library General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Library General Public License for more details.
12 *
13 * You should have received a copy of the GNU Library General Public
14 * License along with this library; if not, write to the Free
15 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 */
17
18/* Nov 6, 2003 Initial version.
19 *
20 * NOTE: This implementation is quite strict about requiring all
21 * field seperators. It also does not allow leading whitespace
22 * except when processing the numeric fields. glibc is more
23 * lenient. See the various glibc difference comments below.
24 *
25 * TODO:
26 * Move to dynamic allocation of (currently staticly allocated)
27 * buffers; especially for the group-related functions since
28 * large group member lists will cause error returns.
29 *
30 */
31
32#include <features.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <stdint.h>
36#include <string.h>
37#include <stddef.h>
38#include <errno.h>
39#include <assert.h>
40#include <ctype.h>
41#include "busybox.h"
42#include "pwd_.h"
43#include "grp_.h"
44#include "shadow_.h"
45
46#ifndef _PATH_SHADOW
47#define _PATH_SHADOW "/etc/shadow"
48#endif
49#ifndef _PATH_PASSWD
50#define _PATH_PASSWD "/etc/passwd"
51#endif
52#ifndef _PATH_GROUP
53#define _PATH_GROUP "/etc/group"
54#endif
55
56/**********************************************************************/
57/* Sizes for staticly allocated buffers. */
58
59/* If you change these values, also change _SC_GETPW_R_SIZE_MAX and
60 * _SC_GETGR_R_SIZE_MAX in libc/unistd/sysconf.c to match */
61#define PWD_BUFFER_SIZE 256
62#define GRP_BUFFER_SIZE 256
63
64/**********************************************************************/
65/* Prototypes for internal functions. */
66
67extern int __parsepwent(void *pw, char *line);
68extern int __parsegrent(void *gr, char *line);
69extern int __parsespent(void *sp, char *line);
70
71extern int __pgsreader(int (*__parserfunc)(void *d, char *line), void *data,
72 char *__restrict line_buff, size_t buflen, FILE *f);
73
74/**********************************************************************/
75/* For the various fget??ent_r funcs, return
76 *
77 * 0: success
78 * ENOENT: end-of-file encountered
79 * ERANGE: buflen too small
80 * other error values possible. See __pgsreader.
81 *
82 * Also, *result == resultbuf on success and NULL on failure.
83 *
84 * NOTE: glibc difference - For the ENOENT case, glibc also sets errno.
85 * We do not, as it really isn't an error if we reach the end-of-file.
86 * Doing so is analogous to having fgetc() set errno on EOF.
87 */
88/**********************************************************************/
89#ifdef L_fgetpwent_r
90
91int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf,
92 char *__restrict buffer, size_t buflen,
93 struct passwd **__restrict result)
94{
95 int rv;
96
97 *result = NULL;
98
99 if (!(rv = __pgsreader(__parsepwent, resultbuf, buffer, buflen, stream))) {
100 *result = resultbuf;
101 }
102
103 return rv;
104}
105
106#endif
107/**********************************************************************/
108#ifdef L_fgetgrent_r
109
110int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf,
111 char *__restrict buffer, size_t buflen,
112 struct group **__restrict result)
113{
114 int rv;
115
116 *result = NULL;
117
118 if (!(rv = __pgsreader(__parsegrent, resultbuf, buffer, buflen, stream))) {
119 *result = resultbuf;
120 }
121
122 return rv;
123}
124
125#endif
126/**********************************************************************/
127#ifdef L_fgetspent_r
128
129int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf,
130 char *__restrict buffer, size_t buflen,
131 struct spwd **__restrict result)
132{
133 int rv;
134
135 *result = NULL;
136
137 if (!(rv = __pgsreader(__parsespent, resultbuf, buffer, buflen, stream))) {
138 *result = resultbuf;
139 }
140
141 return rv;
142}
143
144#endif
145/**********************************************************************/
146/* For the various fget??ent funcs, return NULL on failure and a
147 * pointer to the appropriate struct (staticly allocated) on success.
148 */
149/**********************************************************************/
150#ifdef L_fgetpwent
151
152struct passwd *fgetpwent(FILE *stream)
153{
154 static char buffer[PWD_BUFFER_SIZE];
155 static struct passwd resultbuf;
156 struct passwd *result;
157
158 fgetpwent_r(stream, &resultbuf, buffer, sizeof(buffer), &result);
159 return result;
160}
161
162#endif
163/**********************************************************************/
164#ifdef L_fgetgrent
165
166struct group *fgetgrent(FILE *stream)
167{
168 static char buffer[GRP_BUFFER_SIZE];
169 static struct group resultbuf;
170 struct group *result;
171
172 fgetgrent_r(stream, &resultbuf, buffer, sizeof(buffer), &result);
173 return result;
174}
175
176#endif
177/**********************************************************************/
178#ifdef L_fgetspent
179
180extern int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf,
181 char *__restrict buffer, size_t buflen,
182 struct spwd **__restrict result);
183struct spwd *fgetspent(FILE *stream)
184{
185 static char buffer[PWD_BUFFER_SIZE];
186 static struct spwd resultbuf;
187 struct spwd *result;
188
189 fgetspent_r(stream, &resultbuf, buffer, sizeof(buffer), &result);
190 return result;
191}
192
193#endif
194/**********************************************************************/
195#ifdef L_sgetspent_r
196
197int sgetspent_r(const char *string, struct spwd *result_buf,
198 char *buffer, size_t buflen, struct spwd **result)
199{
200 int rv = ERANGE;
201
202 *result = NULL;
203
204 if (buflen < PWD_BUFFER_SIZE) {
205 DO_ERANGE:
206 errno=rv;
207 goto DONE;
208 }
209
210 if (string != buffer) {
211 if (strlen(string) >= buflen) {
212 goto DO_ERANGE;
213 }
214 strcpy(buffer, string);
215 }
216
217 if (!(rv = __parsespent(result_buf, buffer))) {
218 *result = result_buf;
219 }
220
221 DONE:
222 return rv;
223}
224
225#endif
226/**********************************************************************/
227
228#ifdef GETXXKEY_R_FUNC
229#error GETXXKEY_R_FUNC is already defined!
230#endif
231
232#ifdef L_getpwnam_r
233#define GETXXKEY_R_FUNC getpwnam_r
234#define GETXXKEY_R_PARSER __parsepwent
235#define GETXXKEY_R_ENTTYPE struct passwd
236#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->pw_name, key))
237#define DO_GETXXKEY_R_KEYTYPE const char *__restrict
238#define DO_GETXXKEY_R_PATHNAME _PATH_PASSWD
239#endif
240
241#ifdef L_getgrnam_r
242#define GETXXKEY_R_FUNC getgrnam_r
243#define GETXXKEY_R_PARSER __parsegrent
244#define GETXXKEY_R_ENTTYPE struct group
245#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->gr_name, key))
246#define DO_GETXXKEY_R_KEYTYPE const char *__restrict
247#define DO_GETXXKEY_R_PATHNAME _PATH_GROUP
248#endif
249
250#ifdef L_getspnam_r
251#define GETXXKEY_R_FUNC getspnam_r
252#define GETXXKEY_R_PARSER __parsespent
253#define GETXXKEY_R_ENTTYPE struct spwd
254#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->sp_namp, key))
255#define DO_GETXXKEY_R_KEYTYPE const char *__restrict
256#define DO_GETXXKEY_R_PATHNAME _PATH_SHADOW
257#endif
258
259#ifdef L_getpwuid_r
260#define GETXXKEY_R_FUNC getpwuid_r
261#define GETXXKEY_R_PARSER __parsepwent
262#define GETXXKEY_R_ENTTYPE struct passwd
263#define GETXXKEY_R_TEST(ENT) ((ENT)->pw_uid == key)
264#define DO_GETXXKEY_R_KEYTYPE uid_t
265#define DO_GETXXKEY_R_PATHNAME _PATH_PASSWD
266#endif
267
268#ifdef L_getgrgid_r
269#define GETXXKEY_R_FUNC getgrgid_r
270#define GETXXKEY_R_PARSER __parsegrent
271#define GETXXKEY_R_ENTTYPE struct group
272#define GETXXKEY_R_TEST(ENT) ((ENT)->gr_gid == key)
273#define DO_GETXXKEY_R_KEYTYPE gid_t
274#define DO_GETXXKEY_R_PATHNAME _PATH_GROUP
275#endif
276
277/**********************************************************************/
278#ifdef GETXXKEY_R_FUNC
279
280int GETXXKEY_R_FUNC(DO_GETXXKEY_R_KEYTYPE key,
281 GETXXKEY_R_ENTTYPE *__restrict resultbuf,
282 char *__restrict buffer, size_t buflen,
283 GETXXKEY_R_ENTTYPE **__restrict result)
284{
285 FILE *stream;
286 int rv;
287
288 *result = NULL;
289
290 if (!(stream = fopen(DO_GETXXKEY_R_PATHNAME, "r"))) {
291 rv = errno;
292 } else {
293 do {
294 if (!(rv = __pgsreader(GETXXKEY_R_PARSER, resultbuf,
295 buffer, buflen, stream))
296 ) {
297 if (GETXXKEY_R_TEST(resultbuf)) { /* Found key? */
298 *result = resultbuf;
299 break;
300 }
301 } else {
302 if (rv == ENOENT) { /* end-of-file encountered. */
303 rv = 0;
304 }
305 break;
306 }
307 } while (1);
308 fclose(stream);
309 }
310
311 return rv;
312}
313
314#endif
315/**********************************************************************/
316#ifdef L_getpwuid
317
318struct passwd *getpwuid(uid_t uid)
319{
320 static char buffer[PWD_BUFFER_SIZE];
321 static struct passwd resultbuf;
322 struct passwd *result;
323
324 getpwuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result);
325 return result;
326}
327
328#endif
329/**********************************************************************/
330#ifdef L_getgrgid
331
332struct group *getgrgid(gid_t gid)
333{
334 static char buffer[GRP_BUFFER_SIZE];
335 static struct group resultbuf;
336 struct group *result;
337
338 getgrgid_r(gid, &resultbuf, buffer, sizeof(buffer), &result);
339 return result;
340}
341
342#endif
343/**********************************************************************/
344#ifdef L_getspuid_r
345
346/* This function is non-standard and is currently not built. It seems
347 * to have been created as a reentrant version of the non-standard
348 * functions getspuid. Why getspuid was added, I do not know. */
349
350int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf,
351 char *__restrict buffer, size_t buflen,
352 struct spwd **__restrict result)
353{
354 int rv;
355 struct passwd *pp;
356 struct passwd password;
357 char pwd_buff[PWD_BUFFER_SIZE];
358
359 *result = NULL;
360 if (!(rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp))) {
361 rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result);
362 }
363
364 return rv;
365}
366
367#endif
368/**********************************************************************/
369#ifdef L_getspuid
370
371/* This function is non-standard and is currently not built.
372 * Why it was added, I do not know. */
373
374struct spwd *getspuid(uid_t uid)
375{
376 static char buffer[PWD_BUFFER_SIZE];
377 static struct spwd resultbuf;
378 struct spwd *result;
379
380 getspuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result);
381 return result;
382}
383
384#endif
385/**********************************************************************/
386#ifdef L_getpwnam
387
388struct passwd *getpwnam(const char *name)
389{
390 static char buffer[PWD_BUFFER_SIZE];
391 static struct passwd resultbuf;
392 struct passwd *result;
393
394 getpwnam_r(name, &resultbuf, buffer, sizeof(buffer), &result);
395 return result;
396}
397
398#endif
399/**********************************************************************/
400#ifdef L_getgrnam
401
402struct group *getgrnam(const char *name)
403{
404 static char buffer[GRP_BUFFER_SIZE];
405 static struct group resultbuf;
406 struct group *result;
407
408 getgrnam_r(name, &resultbuf, buffer, sizeof(buffer), &result);
409 return result;
410}
411
412#endif
413/**********************************************************************/
414#ifdef L_getspnam
415
416struct spwd *getspnam(const char *name)
417{
418 static char buffer[PWD_BUFFER_SIZE];
419 static struct spwd resultbuf;
420 struct spwd *result;
421
422 getspnam_r(name, &resultbuf, buffer, sizeof(buffer), &result);
423 return result;
424}
425
426#endif
427/**********************************************************************/
428#ifdef L_getpw
429
430int getpw(uid_t uid, char *buf)
431{
432 struct passwd resultbuf;
433 struct passwd *result;
434 char buffer[PWD_BUFFER_SIZE];
435
436 if (!buf) {
437 errno=EINVAL;
438 } else if (!getpwuid_r(uid, &resultbuf, buffer, sizeof(buffer), &result)) {
439 if (sprintf(buf, "%s:%s:%lu:%lu:%s:%s:%s\n",
440 resultbuf.pw_name, resultbuf.pw_passwd,
441 (unsigned long)(resultbuf.pw_uid),
442 (unsigned long)(resultbuf.pw_gid),
443 resultbuf.pw_gecos, resultbuf.pw_dir,
444 resultbuf.pw_shell) >= 0
445 ) {
446 return 0;
447 }
448 }
449
450 return -1;
451}
452
453#endif
454/**********************************************************************/
455#ifdef L_getpwent_r
456
457static FILE *pwf /*= NULL*/;
458void setpwent(void)
459{
460 if (pwf) {
461 rewind(pwf);
462 }
463}
464
465void endpwent(void)
466{
467 if (pwf) {
468 fclose(pwf);
469 pwf = NULL;
470 }
471}
472
473
474int getpwent_r(struct passwd *__restrict resultbuf,
475 char *__restrict buffer, size_t buflen,
476 struct passwd **__restrict result)
477{
478 int rv;
479
480 *result = NULL; /* In case of error... */
481
482 if (!pwf) {
483 if (!(pwf = fopen(_PATH_PASSWD, "r"))) {
484 rv = errno;
485 goto ERR;
486 }
487 }
488
489 if (!(rv = __pgsreader(__parsepwent, resultbuf,
490 buffer, buflen, pwf))) {
491 *result = resultbuf;
492 }
493
494 ERR:
495 return rv;
496}
497
498#endif
499/**********************************************************************/
500#ifdef L_getgrent_r
501
502static FILE *grf /*= NULL*/;
503void setgrent(void)
504{
505 if (grf) {
506 rewind(grf);
507 }
508}
509
510void endgrent(void)
511{
512 if (grf) {
513 fclose(grf);
514 grf = NULL;
515 }
516}
517
518int getgrent_r(struct group *__restrict resultbuf,
519 char *__restrict buffer, size_t buflen,
520 struct group **__restrict result)
521{
522 int rv;
523
524 *result = NULL; /* In case of error... */
525
526 if (!grf) {
527 if (!(grf = fopen(_PATH_GROUP, "r"))) {
528 rv = errno;
529 goto ERR;
530 }
531 }
532
533 if (!(rv = __pgsreader(__parsegrent, resultbuf,
534 buffer, buflen, grf))) {
535 *result = resultbuf;
536 }
537
538 ERR:
539 return rv;
540}
541
542#endif
543/**********************************************************************/
544#ifdef L_getspent_r
545
546static FILE *spf /*= NULL*/;
547void setspent(void)
548{
549 if (spf) {
550 rewind(spf);
551 }
552}
553
554void endspent(void)
555{
556 if (spf) {
557 fclose(spf);
558 spf = NULL;
559 }
560}
561
562int getspent_r(struct spwd *resultbuf, char *buffer,
563 size_t buflen, struct spwd **result)
564{
565 int rv;
566
567 *result = NULL; /* In case of error... */
568
569 if (!spf) {
570 if (!(spf = fopen(_PATH_SHADOW, "r"))) {
571 rv = errno;
572 goto ERR;
573 }
574 }
575
576 if (!(rv = __pgsreader(__parsespent, resultbuf,
577 buffer, buflen, spf))) {
578 *result = resultbuf;
579 }
580
581 ERR:
582 return rv;
583}
584
585#endif
586/**********************************************************************/
587#ifdef L_getpwent
588
589struct passwd *getpwent(void)
590{
591 static char line_buff[PWD_BUFFER_SIZE];
592 static struct passwd pwd;
593 struct passwd *result;
594
595 getpwent_r(&pwd, line_buff, sizeof(line_buff), &result);
596 return result;
597}
598
599#endif
600/**********************************************************************/
601#ifdef L_getgrent
602
603struct group *getgrent(void)
604{
605 static char line_buff[GRP_BUFFER_SIZE];
606 static struct group gr;
607 struct group *result;
608
609 getgrent_r(&gr, line_buff, sizeof(line_buff), &result);
610 return result;
611}
612
613#endif
614/**********************************************************************/
615#ifdef L_getspent
616
617struct spwd *getspent(void)
618{
619 static char line_buff[PWD_BUFFER_SIZE];
620 static struct spwd spwd;
621 struct spwd *result;
622
623 getspent_r(&spwd, line_buff, sizeof(line_buff), &result);
624 return result;
625}
626
627#endif
628/**********************************************************************/
629#ifdef L_sgetspent
630
631struct spwd *sgetspent(const char *string)
632{
633 static char line_buff[PWD_BUFFER_SIZE];
634 static struct spwd spwd;
635 struct spwd *result;
636
637 sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result);
638 return result;
639}
640
641#endif
642/**********************************************************************/
643#ifdef L_initgroups
644
645int initgroups(const char *user, gid_t gid)
646{
647 FILE *grf;
648 gid_t *group_list;
649 int num_groups, rv;
650 char **m;
651 struct group group;
652 char buff[PWD_BUFFER_SIZE];
653
654 rv = -1;
655
656 /* We alloc space for 8 gids at a time. */
657 if (((group_list = (gid_t *) malloc(8*sizeof(gid_t *))) != NULL)
658 && ((grf = fopen(_PATH_GROUP, "r")) != NULL)
659 ) {
660
661 *group_list = gid;
662 num_groups = 1;
663
664 while (!__pgsreader(__parsegrent, &group, buff, sizeof(buff), grf)) {
665 assert(group.gr_mem); /* Must have at least a NULL terminator. */
666 if (group.gr_gid != gid) {
667 for (m=group.gr_mem ; *m ; m++) {
668 if (!strcmp(*m, user)) {
669 if (!(num_groups & 7)) {
670 gid_t *tmp = (gid_t *)
671 realloc(group_list,
672 (num_groups+8) * sizeof(gid_t *));
673 if (!tmp) {
674 rv = -1;
675 goto DO_CLOSE;
676 }
677 group_list = tmp;
678 }
679 group_list[num_groups++] = group.gr_gid;
680 break;
681 }
682 }
683 }
684 }
685
686 rv = setgroups(num_groups, group_list);
687 DO_CLOSE:
688 fclose(grf);
689 }
690
691 /* group_list will be NULL if initial malloc failed, which may trigger
692 * warnings from various malloc debuggers. */
693 free(group_list);
694 return rv;
695}
696
697#endif
698/**********************************************************************/
699#ifdef L_putpwent
700
701int putpwent(const struct passwd *__restrict p, FILE *__restrict f)
702{
703 int rv = -1;
704
705 if (!p || !f) {
706 errno=EINVAL;
707 } else {
708 /* No extra thread locking is needed above what fprintf does. */
709 if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n",
710 p->pw_name, p->pw_passwd,
711 (unsigned long)(p->pw_uid),
712 (unsigned long)(p->pw_gid),
713 p->pw_gecos, p->pw_dir, p->pw_shell) >= 0
714 ) {
715 rv = 0;
716 }
717 }
718
719 return rv;
720}
721
722#endif
723/**********************************************************************/
724#ifdef L_putgrent
725
726int putgrent(const struct group *__restrict p, FILE *__restrict f)
727{
728 static const char format[] = ",%s";
729 char **m;
730 const char *fmt;
731 int rv = -1;
732
733 if (!p || !f) { /* Sigh... glibc checks. */
734 errno=EINVAL;
735 } else {
736 if (fprintf(f, "%s:%s:%lu:",
737 p->gr_name, p->gr_passwd,
738 (unsigned long)(p->gr_gid)) >= 0
739 ) {
740
741 fmt = format + 1;
742
743 assert(p->gr_mem);
744 m = p->gr_mem;
745
746 do {
747 if (!*m) {
748 if (fputc_unlocked('\n', f) >= 0) {
749 rv = 0;
750 }
751 break;
752 }
753 if (fprintf(f, fmt, *m) < 0) {
754 break;
755 }
756 ++m;
757 fmt = format;
758 } while (1);
759
760 }
761
762 }
763
764 return rv;
765}
766
767#endif
768/**********************************************************************/
769#ifdef L_putspent
770
771static const unsigned char sp_off[] = {
772 offsetof(struct spwd, sp_lstchg), /* 2 - not a char ptr */
773 offsetof(struct spwd, sp_min), /* 3 - not a char ptr */
774 offsetof(struct spwd, sp_max), /* 4 - not a char ptr */
775 offsetof(struct spwd, sp_warn), /* 5 - not a char ptr */
776 offsetof(struct spwd, sp_inact), /* 6 - not a char ptr */
777 offsetof(struct spwd, sp_expire), /* 7 - not a char ptr */
778};
779
780int putspent(const struct spwd *p, FILE *stream)
781{
782 static const char ld_format[] = "%ld:";
783 const char *f;
784 long int x;
785 int i;
786 int rv = -1;
787
788 /* Unlike putpwent and putgrent, glibc does not check the args. */
789 if (fprintf(stream, "%s:%s:", p->sp_namp,
790 (p->sp_pwdp ? p->sp_pwdp : "")) < 0
791 ) {
792 goto DO_UNLOCK;
793 }
794
795 for (i=0 ; i < sizeof(sp_off) ; i++) {
796 f = ld_format;
797 if ((x = *(const long int *)(((const char *) p) + sp_off[i])) == -1) {
798 f += 3;
799 }
800 if (fprintf(stream, f, x) < 0) {
801 goto DO_UNLOCK;
802 }
803 }
804
805 if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) {
806 goto DO_UNLOCK;
807 }
808
809 if (fputc_unlocked('\n', stream) > 0) {
810 rv = 0;
811 }
812
813DO_UNLOCK:
814 return rv;
815}
816
817#endif
818/**********************************************************************/
819/* Internal uClibc functions. */
820/**********************************************************************/
821#ifdef L___parsepwent
822
823static const unsigned char pw_off[] = {
824 offsetof(struct passwd, pw_name), /* 0 */
825 offsetof(struct passwd, pw_passwd), /* 1 */
826 offsetof(struct passwd, pw_uid), /* 2 - not a char ptr */
827 offsetof(struct passwd, pw_gid), /* 3 - not a char ptr */
828 offsetof(struct passwd, pw_gecos), /* 4 */
829 offsetof(struct passwd, pw_dir), /* 5 */
830 offsetof(struct passwd, pw_shell) /* 6 */
831};
832
833int __parsepwent(void *data, char *line)
834{
835 char *endptr;
836 char *p;
837 int i;
838
839 i = 0;
840 do {
841 p = ((char *) ((struct passwd *) data)) + pw_off[i];
842
843 if ((i & 6) ^ 2) { /* i!=2 and i!=3 */
844 *((char **) p) = line;
845 if (i==6) {
846 return 0;
847 }
848 /* NOTE: glibc difference - glibc allows omission of
849 * ':' seperators after the gid field if all remaining
850 * entries are empty. We require all separators. */
851 if (!(line = strchr(line, ':'))) {
852 break;
853 }
854 } else {
855 unsigned long t = strtoul(line, &endptr, 10);
856 /* Make sure we had at least one digit, and that the
857 * failing char is the next field seperator ':'. See
858 * glibc difference note above. */
859 /* TODO: Also check for leading whitespace? */
860 if ((endptr == line) || (*endptr != ':')) {
861 break;
862 }
863 line = endptr;
864 if (i & 1) { /* i == 3 -- gid */
865 *((gid_t *) p) = t;
866 } else { /* i == 2 -- uid */
867 *((uid_t *) p) = t;
868 }
869 }
870
871 *line++ = 0;
872 ++i;
873 } while (1);
874
875 return -1;
876}
877
878#endif
879/**********************************************************************/
880#ifdef L___parsegrent
881
882static const unsigned char gr_off[] = {
883 offsetof(struct group, gr_name), /* 0 */
884 offsetof(struct group, gr_passwd), /* 1 */
885 offsetof(struct group, gr_gid) /* 2 - not a char ptr */
886};
887
888int __parsegrent(void *data, char *line)
889{
890 char *endptr;
891 char *p;
892 int i;
893 char **members;
894 char *end_of_buf;
895
896 end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */
897 i = 0;
898 do {
899 p = ((char *) ((struct group *) data)) + gr_off[i];
900
901 if (i < 2) {
902 *((char **) p) = line;
903 if (!(line = strchr(line, ':'))) {
904 break;
905 }
906 *line++ = 0;
907 ++i;
908 } else {
909 *((gid_t *) p) = strtoul(line, &endptr, 10);
910
911 /* NOTE: glibc difference - glibc allows omission of the
912 * trailing colon when there is no member list. We treat
913 * this as an error. */
914
915 /* Make sure we had at least one digit, and that the
916 * failing char is the next field seperator ':'. See
917 * glibc difference note above. */
918 if ((endptr == line) || (*endptr != ':')) {
919 break;
920 }
921
922 i = 1; /* Count terminating NULL ptr. */
923 p = endptr;
924
925 if (p[1]) { /* We have a member list to process. */
926 /* Overwrite the last ':' with a ',' before counting.
927 * This allows us to test for initial ',' and adds
928 * one ',' so that the ',' count equals the member
929 * count. */
930 *p = ',';
931 do {
932 /* NOTE: glibc difference - glibc allows and trims leading
933 * (but not trailing) space. We treat this as an error. */
934 /* NOTE: glibc difference - glibc allows consecutive and
935 * trailing commas, and ignores "empty string" users. We
936 * treat this as an error. */
937 if (*p == ',') {
938 ++i;
939 *p = 0; /* nul-terminate each member string. */
940 if (!*++p || (*p == ',') || isspace(*p)) {
941 goto ERR;
942 }
943 }
944 } while (*++p);
945 }
946
947 /* Now align (p+1), rounding up. */
948 /* Assumes sizeof(char **) is a power of 2. */
949 members = (char **)( (((intptr_t) p) + sizeof(char **))
950 & ~((intptr_t)(sizeof(char **) - 1)) );
951
952 if (((char *)(members + i)) > end_of_buf) { /* No space. */
953 break;
954 }
955
956 ((struct group *) data)->gr_mem = members;
957
958 if (--i) {
959 p = endptr; /* Pointing to char prior to first member. */
960 do {
961 *members++ = ++p;
962 if (!--i) break;
963 while (*++p) {}
964 } while (1);
965 }
966 *members = NULL;
967
968 return 0;
969 }
970 } while (1);
971
972 ERR:
973 return -1;
974}
975
976#endif
977/**********************************************************************/
978#ifdef L___parsespent
979
980static const unsigned char sp_off[] = {
981 offsetof(struct spwd, sp_namp), /* 0 */
982 offsetof(struct spwd, sp_pwdp), /* 1 */
983 offsetof(struct spwd, sp_lstchg), /* 2 - not a char ptr */
984 offsetof(struct spwd, sp_min), /* 3 - not a char ptr */
985 offsetof(struct spwd, sp_max), /* 4 - not a char ptr */
986 offsetof(struct spwd, sp_warn), /* 5 - not a char ptr */
987 offsetof(struct spwd, sp_inact), /* 6 - not a char ptr */
988 offsetof(struct spwd, sp_expire), /* 7 - not a char ptr */
989 offsetof(struct spwd, sp_flag) /* 8 - not a char ptr */
990};
991
992int __parsespent(void *data, char * line)
993{
994 char *endptr;
995 char *p;
996 int i;
997
998 i = 0;
999 do {
1000 p = ((char *) ((struct spwd *) data)) + sp_off[i];
1001 if (i < 2) {
1002 *((char **) p) = line;
1003 if (!(line = strchr(line, ':'))) {
1004 break;
1005 }
1006 } else {
1007#if 0
1008 if (i==5) { /* Support for old format. */
1009 while (isspace(*line)) ++line; /* glibc eats space here. */
1010 if (!*line) {
1011 ((struct spwd *) data)->sp_warn = -1;
1012 ((struct spwd *) data)->sp_inact = -1;
1013 ((struct spwd *) data)->sp_expire = -1;
1014 ((struct spwd *) data)->sp_flag = ~0UL;
1015 return 0;
1016 }
1017 }
1018#endif
1019
1020 *((long *) p) = (long) strtoul(line, &endptr, 10);
1021
1022 if (endptr == line) {
1023 *((long *) p) = ((i != 8) ? -1L : ((long)(~0UL)));
1024 }
1025
1026 line = endptr;
1027
1028 if (i == 8) {
1029 if (!*endptr) {
1030 return 0;
1031 }
1032 break;
1033 }
1034
1035 if (*endptr != ':') {
1036 break;
1037 }
1038
1039 }
1040
1041 *line++ = 0;
1042 ++i;
1043 } while (1);
1044
1045 return EINVAL;
1046}
1047
1048#endif
1049/**********************************************************************/
1050#ifdef L___pgsreader
1051
1052/* Reads until if EOF, or until if finds a line which fits in the buffer
1053 * and for which the parser function succeeds.
1054 *
1055 * Returns 0 on success and ENOENT for end-of-file (glibc concession).
1056 */
1057
1058int __pgsreader(int (*__parserfunc)(void *d, char *line), void *data,
1059 char *__restrict line_buff, size_t buflen, FILE *f)
1060{
1061 int line_len;
1062 int skip;
1063 int rv = ERANGE;
1064
1065 if (buflen < PWD_BUFFER_SIZE) {
1066 errno=rv;
1067 } else {
1068 skip = 0;
1069 do {
1070 if (!fgets_unlocked(line_buff, buflen, f)) {
1071 if (feof_unlocked(f)) {
1072 rv = ENOENT;
1073 }
1074 break;
1075 }
1076
1077 line_len = strlen(line_buff) - 1; /* strlen() must be > 0. */
1078 if (line_buff[line_len] == '\n') {
1079 line_buff[line_len] = 0;
1080 } else if (line_len + 2 == buflen) { /* line too long */
1081 ++skip;
1082 continue;
1083 }
1084
1085 if (skip) {
1086 --skip;
1087 continue;
1088 }
1089
1090 /* NOTE: glibc difference - glibc strips leading whitespace from
1091 * records. We do not allow leading whitespace. */
1092
1093 /* Skip empty lines, comment lines, and lines with leading
1094 * whitespace. */
1095 if (*line_buff && (*line_buff != '#') && !isspace(*line_buff)) {
1096 if (__parserfunc == __parsegrent) { /* Do evil group hack. */
1097 /* The group entry parsing function needs to know where
1098 * the end of the buffer is so that it can construct the
1099 * group member ptr table. */
1100 ((struct group *) data)->gr_name = line_buff + buflen;
1101 }
1102
1103 if (!__parserfunc(data, line_buff)) {
1104 rv = 0;
1105 break;
1106 }
1107 }
1108 } while (1);
1109
1110 }
1111
1112 return rv;
1113}
1114
1115#endif
1116/**********************************************************************/
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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Login/Password Management Utilities"
7
8config CONFIG_USE_BB_PWD_GRP
9 bool "Use internal password and group functions rather than system functions"
10 default n
11 help
12 If you leave this disabled, busybox will use the system's password
13 and group functions. And if you are using the GNU C library
14 (glibc), you will then need to install the /etc/nsswitch.conf
15 configuration file and the required /lib/libnss_* libraries in
16 order for the password and group functions to work. This generally
17 makes your embedded system quite a bit larger.
18
19 Enabling this option will cause busybox to directly access the
20 system's /etc/password, /etc/group files (and your system will be
21 smaller, and I will get fewer emails asking about how glibc NSS
22 works). When this option is enabled, you will not be able to use
23 PAM to access remote LDAP password servers and whatnot. And if you
24 want hostname resolution to work with glibc, you still need the
25 /lib/libnss_* libraries.
26
27 If you enable this option, it will add about 1.5k to busybox.
28
29
30config CONFIG_ADDGROUP
31 bool "addgroup"
32 default n
33 help
34 Utility for creating a new group account.
35
36config CONFIG_DELGROUP
37 bool "delgroup"
38 default n
39 help
40 Utility for deleting a group account.
41
42config CONFIG_ADDUSER
43 bool "adduser"
44 default n
45 help
46 Utility for creating a new user account.
47
48config CONFIG_DELUSER
49 bool "deluser"
50 default n
51 help
52 Utility for deleting a user account.
53
54config CONFIG_GETTY
55 bool "getty"
56 default n
57 help
58 getty lets you log in on a tty, it is normally invoked by init.
59
60config CONFIG_FEATURE_U_W_TMP
61 bool " Support utmp and wtmp files"
62 depends on CONFIG_GETTY || CONFIG_LOGIN || CONFIG_SU || CONFIG_WHO || CONFIG_LAST
63 default n
64 help
65 The files /var/run/utmp and /var/run/wtmp can be used to track when
66 user's have logged into and logged out of the system, allowing programs
67 such as 'who' and 'last' to list who is currently logged in.
68
69config CONFIG_LOGIN
70 bool "login"
71 default n
72 select CONFIG_FEATURE_SUID
73 help
74 login is used when signing onto a system.
75
76 Note that Busybox binary must be setuid root for this applet to
77 work properly.
78
79config CONFIG_FEATURE_SECURETTY
80 bool " Support for /etc/securetty"
81 default y
82 depends on CONFIG_LOGIN
83 help
84 The file /etc/securetty is used by (some versions of) login(1).
85 The file contains the device names of tty lines (one per line,
86 without leading /dev/) on which root is allowed to login.
87
88config CONFIG_PASSWD
89 bool "passwd"
90 default n
91 select CONFIG_FEATURE_SUID
92 help
93 passwd changes passwords for user and group accounts. A normal user
94 may only change the password for his/her own account, the super user
95 may change the password for any account. The administrator of a group
96 may change the password for the group.
97
98 Note that Busybox binary must be setuid root for this applet to
99 work properly.
100
101config CONFIG_SU
102 bool "su"
103 default n
104 select CONFIG_FEATURE_SUID
105 help
106 su is used to become another user during a login session.
107 Invoked without a username, su defaults to becoming the super user.
108
109 Note that Busybox binary must be setuid root for this applet to
110 work properly.
111
112config CONFIG_SULOGIN
113 bool "sulogin"
114 default n
115 help
116 sulogin is invoked when the system goes into single user
117 mode (this is done through an entry in inittab).
118
119config CONFIG_VLOCK
120 bool "vlock"
121 default n
122 select CONFIG_FEATURE_SUID
123 help
124 Build the "vlock" applet which allows you to lock (virtual) terminals.
125
126 Note that Busybox binary must be setuid root for this applet to
127 work properly.
128
129comment "Common options for adduser, deluser, login, su"
130 depends on CONFIG_ADDUSER || CONFIG_DELUSER || CONFIG_LOGIN || CONFIG_SU
131
132config CONFIG_FEATURE_SHADOWPASSWDS
133 bool "Support for shadow passwords"
134 default n
135 depends on CONFIG_ADDUSER || CONFIG_DELUSER || CONFIG_LOGIN || CONFIG_SU
136 help
137 Build support for shadow password in /etc/shadow. This file is only
138 readable by root and thus the encrypted passwords are no longer
139 publicly readable.
140
141config CONFIG_USE_BB_SHADOW
142 bool " Use busybox shadow password functions"
143 default n
144 depends on CONFIG_USE_BB_PWD_GRP && CONFIG_FEATURE_SHADOWPASSWDS
145 help
146 If you leave this disabled, busybox will use the system's shadow
147 password handling functions. And if you are using the GNU C library
148 (glibc), you will then need to install the /etc/nsswitch.conf
149 configuration file and the required /lib/libnss_* libraries in
150 order for the shadow password functions to work. This generally
151 makes your embedded system quite a bit larger.
152
153 Enabling this option will cause busybox to directly access the
154 system's /etc/shadow file when handling shadow passwords. This
155 makes your system smaller and I will get fewer emails asking about
156 how glibc NSS works). When this option is enabled, you will not be
157 able to use PAM to access shadow passwords from remote LDAP
158 password servers and whatnot.
159
160endmenu
161
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/loginutils
23LOGINUTILS_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20LOGINUTILS_AR:=loginutils.a
21ifndef LOGINUTILS_DIR
22LOGINUTILS_DIR:=$(top_builddir)/loginutils/
23endif
24srcdir=$(top_srcdir)/loginutils
25
26LOGINUTILS-y:=
27LOGINUTILS-$(CONFIG_ADDGROUP) += addgroup.o
28LOGINUTILS-$(CONFIG_ADDUSER) += adduser.o
29LOGINUTILS-$(CONFIG_GETTY) += getty.o
30LOGINUTILS-$(CONFIG_LOGIN) += login.o
31LOGINUTILS-$(CONFIG_PASSWD) += passwd.o
32LOGINUTILS-$(CONFIG_SU) += su.o
33LOGINUTILS-$(CONFIG_SULOGIN) += sulogin.o
34LOGINUTILS-$(CONFIG_VLOCK) += vlock.o
35LOGINUTILS-$(CONFIG_DELUSER) += deluser.o
36LOGINUTILS-$(CONFIG_DELGROUP) += delgroup.o
37
38libraries-y+=$(LOGINUTILS_DIR)$(LOGINUTILS_AR)
39
40needcrypt-y:=
41needcrypt-$(CONFIG_LOGIN) := y
42needcrypt-$(CONFIG_PASSWD) := y
43needcrypt-$(CONFIG_SU) := y
44needcrypt-$(CONFIG_SULOGIN) := y
45needcrypt-$(CONFIG_VLOCK) := y
46
47
48ifeq ($(needcrypt-y),y)
49 LIBRARIES += -lcrypt
50endif
51
52$(LOGINUTILS_DIR)$(LOGINUTILS_AR): $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y))
53 $(AR) -ro $@ $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y))
54
55$(LOGINUTILS_DIR)%.o: $(srcdir)/%.c
56 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
57
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * addgroup - add users to /etc/passwd and /etc/shadow
4 *
5 * Copyright (C) 1999 by Lineo, inc. and John Beppu
6 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <errno.h>
25#include <fcntl.h>
26#include <stdarg.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/param.h>
31#include <sys/stat.h>
32#include <sys/types.h>
33#include <unistd.h>
34#include "busybox.h"
35#include "pwd_.h"
36#include "grp_.h"
37
38
39/* structs __________________________ */
40
41/* data _____________________________ */
42
43/* defaults : should this be in an external file? */
44static const char default_passwd[] = "x";
45
46
47/* make sure gr_name isn't taken, make sure gid is kosher
48 * return 1 on failure */
49static int group_study(const char *filename, struct group *g)
50{
51 FILE *etc_group;
52 gid_t desired;
53
54 struct group *grp;
55 const int max = 65000;
56
57 etc_group = bb_xfopen(filename, "r");
58
59 /* make sure gr_name isn't taken, make sure gid is kosher */
60 desired = g->gr_gid;
61 while ((grp = fgetgrent(etc_group))) {
62 if ((strcmp(grp->gr_name, g->gr_name)) == 0) {
63 bb_error_msg_and_die("%s: group already in use\n", g->gr_name);
64 }
65 if ((desired) && grp->gr_gid == desired) {
66 bb_error_msg_and_die("%d: gid has already been allocated\n",
67 desired);
68 }
69 if ((grp->gr_gid > g->gr_gid) && (grp->gr_gid < max)) {
70 g->gr_gid = grp->gr_gid;
71 }
72 }
73 fclose(etc_group);
74
75 /* gid */
76 if (desired) {
77 g->gr_gid = desired;
78 } else {
79 g->gr_gid++;
80 }
81 /* return 1; */
82 return 0;
83}
84
85/* append a new user to the passwd file */
86static int addgroup(const char *filename, char *group, gid_t gid, const char *user)
87{
88 FILE *etc_group;
89
90#ifdef CONFIG_FEATURE_SHADOWPASSWDS
91 FILE *etc_gshadow;
92#endif
93
94 struct group gr;
95
96 /* group:passwd:gid:userlist */
97 static const char entryfmt[] = "%s:%s:%d:%s\n";
98
99 /* make sure gid and group haven't already been allocated */
100 gr.gr_gid = gid;
101 gr.gr_name = group;
102 if (group_study(filename, &gr))
103 return 1;
104
105 /* add entry to group */
106 etc_group = bb_xfopen(filename, "a");
107
108 fprintf(etc_group, entryfmt, group, default_passwd, gr.gr_gid, user);
109 fclose(etc_group);
110
111
112#ifdef CONFIG_FEATURE_SHADOWPASSWDS
113 /* add entry to gshadow if necessary */
114 if (access(bb_path_gshadow_file, F_OK|W_OK) == 0) {
115 etc_gshadow = bb_xfopen(bb_path_gshadow_file, "a");
116 fprintf(etc_gshadow, "%s:!::\n", group);
117 fclose(etc_gshadow);
118 }
119#endif
120
121 /* return 1; */
122 return 0;
123}
124
125#ifndef CONFIG_ADDUSER
126static inline void if_i_am_not_root(void)
127{
128 if (geteuid()) {
129 bb_error_msg_and_die( "Only root may add a user or group to the system.");
130 }
131}
132#else
133extern void if_i_am_not_root(void);
134#endif
135
136/*
137 * addgroup will take a login_name as its first parameter.
138 *
139 * gid
140 *
141 * can be customized via command-line parameters.
142 * ________________________________________________________________________ */
143int addgroup_main(int argc, char **argv)
144{
145 char *group;
146 char *user;
147 gid_t gid = 0;
148
149 /* get remaining args */
150 if(bb_getopt_ulflags(argc, argv, "g:", &group)) {
151 gid = bb_xgetlarg(group, 10, 0, LONG_MAX);
152 }
153
154 if (optind < argc) {
155 group = argv[optind];
156 optind++;
157 } else {
158 bb_show_usage();
159 }
160
161 if (optind < argc) {
162 user = argv[optind];
163 } else {
164 user = "";
165 }
166
167 if_i_am_not_root();
168
169 /* werk */
170 return addgroup(bb_path_group_file, group, gid, user);
171}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * adduser - add users to /etc/passwd and /etc/shadow
4 *
5 * Copyright (C) 1999 by Lineo, inc. and John Beppu
6 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#ifndef _GNU_SOURCE
25#define _GNU_SOURCE
26#endif
27#include <errno.h>
28#include <fcntl.h>
29#include <stdarg.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <time.h>
34#include <unistd.h>
35#include <getopt.h>
36#include <sys/param.h>
37#include <sys/stat.h>
38#include <sys/types.h>
39#include "busybox.h"
40
41
42
43/* structs __________________________ */
44
45typedef struct {
46 uid_t u;
47 gid_t g;
48} Id;
49
50/* data _____________________________ */
51
52/* defaults : should this be in an external file? */
53static const char default_passwd[] = "x";
54static const char default_gecos[] = "Linux User,,,";
55static const char default_home_prefix[] = "/home";
56
57#ifdef CONFIG_FEATURE_SHADOWPASSWDS
58/* shadow in use? */
59static int shadow_enabled = 0;
60#endif
61
62/* remix */
63/* EDR recoded such that the uid may be passed in *p */
64static int passwd_study(const char *filename, struct passwd *p)
65{
66 struct passwd *pw;
67 FILE *passwd;
68
69 const int min = 500;
70 const int max = 65000;
71
72 passwd = bb_wfopen(filename, "r");
73 if (!passwd)
74 return 4;
75
76 /* EDR if uid is out of bounds, set to min */
77 if ((p->pw_uid > max) || (p->pw_uid < min))
78 p->pw_uid = min;
79
80 /* stuff to do:
81 * make sure login isn't taken;
82 * find free uid and gid;
83 */
84 while ((pw = fgetpwent(passwd))) {
85 if (strcmp(pw->pw_name, p->pw_name) == 0) {
86 /* return 0; */
87 return 1;
88 }
89 if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max)
90 && (pw->pw_uid >= min)) {
91 p->pw_uid = pw->pw_uid + 1;
92 }
93 }
94
95 if (p->pw_gid == 0) {
96 /* EDR check for an already existing gid */
97 while (getgrgid(p->pw_uid) != NULL)
98 p->pw_uid++;
99
100 /* EDR also check for an existing group definition */
101 if (getgrnam(p->pw_name) != NULL)
102 return 3;
103
104 /* EDR create new gid always = uid */
105 p->pw_gid = p->pw_uid;
106 }
107
108 /* EDR bounds check */
109 if ((p->pw_uid > max) || (p->pw_uid < min))
110 return 2;
111
112 /* return 1; */
113 return 0;
114}
115
116static void addgroup_wrapper(const char *login, gid_t gid)
117{
118 char *cmd;
119
120 bb_xasprintf(&cmd, "addgroup -g %d %s", gid, login);
121 system(cmd);
122 free(cmd);
123}
124
125static void passwd_wrapper(const char *login) __attribute__ ((noreturn));
126
127static void passwd_wrapper(const char *login)
128{
129 static const char prog[] = "passwd";
130 execlp(prog, prog, login, NULL);
131 bb_error_msg_and_die("Failed to execute '%s', you must set the password for '%s' manually", prog, login);
132}
133
134/* putpwent(3) remix */
135static int adduser(const char *filename, struct passwd *p, int makehome, int setpass)
136{
137 FILE *passwd;
138 int r;
139#ifdef CONFIG_FEATURE_SHADOWPASSWDS
140 FILE *shadow;
141 struct spwd *sp;
142#endif
143 int new_group = 1;
144
145 /* if using a pre-existing group, don't create one */
146 if (p->pw_gid != 0)
147 new_group = 0;
148
149 /* make sure everything is kosher and setup uid && gid */
150 passwd = bb_wfopen(filename, "a");
151 if (passwd == NULL) {
152 return 1;
153 }
154 fseek(passwd, 0, SEEK_END);
155
156 /* if (passwd_study(filename, p) == 0) { */
157 r = passwd_study(filename, p);
158 if (r) {
159 if (r == 1)
160 bb_error_msg("%s: login already in use", p->pw_name);
161 else if (r == 2)
162 bb_error_msg("illegal uid or no uids left");
163 else if (r == 3)
164 bb_error_msg("group name %s already in use", p->pw_name);
165 else
166 bb_error_msg("generic error.");
167 return 1;
168 }
169
170 /* add to passwd */
171 if (putpwent(p, passwd) == -1) {
172 return 1;
173 }
174 fclose(passwd);
175
176#ifdef CONFIG_FEATURE_SHADOWPASSWDS
177 /* add to shadow if necessary */
178 if (shadow_enabled) {
179 shadow = bb_wfopen(bb_path_shadow_file, "a");
180 if (shadow == NULL) {
181 return 1;
182 }
183 fseek(shadow, 0, SEEK_END);
184 sp = pwd_to_spwd(p);
185 sp->sp_max = 99999; /* debianish */
186 sp->sp_warn = 7;
187 fprintf(shadow, "%s:!:%ld:%ld:%ld:%ld:::\n",
188 sp->sp_namp, sp->sp_lstchg, sp->sp_min, sp->sp_max,
189 sp->sp_warn);
190 fclose(shadow);
191 }
192#endif
193
194 if (new_group) {
195 /* add to group */
196 /* addgroup should be responsible for dealing w/ gshadow */
197 addgroup_wrapper(p->pw_name, p->pw_gid);
198 }
199
200 /* Clear the umask for this process so it doesn't
201 * * screw up the permissions on the mkdir and chown. */
202 umask(0);
203
204 if (makehome) {
205 /* mkdir */
206 if (mkdir(p->pw_dir, 0755)) {
207 bb_perror_msg("%s", p->pw_dir);
208 }
209 /* Set the owner and group so it is owned by the new user. */
210 if (chown(p->pw_dir, p->pw_uid, p->pw_gid)) {
211 bb_perror_msg("%s", p->pw_dir);
212 }
213 /* Now fix up the permissions to 2755. Can't do it before now
214 * since chown will clear the setgid bit */
215 if (chmod(p->pw_dir, 02755)) {
216 bb_perror_msg("%s", p->pw_dir);
217 }
218 }
219
220 if (setpass) {
221 /* interactively set passwd */
222 passwd_wrapper(p->pw_name);
223 }
224
225 return 0;
226}
227
228
229/* return current uid (root is always uid == 0, right?) */
230#ifndef CONFIG_ADDGROUP
231static inline void if_i_am_not_root(void)
232#else
233void if_i_am_not_root(void)
234#endif
235{
236 if (geteuid()) {
237 bb_error_msg_and_die( "Only root may add a user or group to the system.");
238 }
239}
240
241#define SETPASS (1 << 4)
242#define MAKEHOME (1 << 6)
243
244/*
245 * adduser will take a login_name as its first parameter.
246 *
247 * home
248 * shell
249 * gecos
250 *
251 * can be customized via command-line parameters.
252 * ________________________________________________________________________ */
253int adduser_main(int argc, char **argv)
254{
255 struct passwd pw;
256 const char *login;
257 const char *gecos = default_gecos;
258 const char *home = NULL;
259 const char *shell = DEFAULT_SHELL;
260 const char *usegroup = NULL;
261 int flags;
262 int setpass = 1;
263 int makehome = 1;
264
265 /* init */
266 if (argc < 2) {
267 bb_show_usage();
268 }
269 /* get args */
270 flags = bb_getopt_ulflags(argc, argv, "h:g:s:G:DSH", &home, &gecos, &shell, &usegroup);
271
272 if (flags & SETPASS) {
273 setpass = 0;
274 }
275 if (flags & MAKEHOME) {
276 makehome = 0;
277 }
278
279 /* got root? */
280 if_i_am_not_root();
281
282 /* get login */
283 if (optind >= argc) {
284 bb_error_msg_and_die( "no user specified");
285 }
286 login = argv[optind];
287
288 /* create string for $HOME if not specified already */
289 if (!home) {
290 home = concat_path_file(default_home_prefix, login);
291 }
292#ifdef CONFIG_FEATURE_SHADOWPASSWDS
293 /* is /etc/shadow in use? */
294 shadow_enabled = (0 == access(bb_path_shadow_file, F_OK));
295#endif
296
297 /* create a passwd struct */
298 pw.pw_name = (char *)login;
299 pw.pw_passwd = (char *)default_passwd;
300 pw.pw_uid = 0;
301 pw.pw_gid = 0;
302 pw.pw_gecos = (char *)gecos;
303 pw.pw_dir = (char *)home;
304 pw.pw_shell = (char *)shell;
305
306 if (usegroup) {
307 /* Add user to a group that already exists */
308 pw.pw_gid = my_getgrnam(usegroup);
309 /* exits on error */
310 }
311
312 /* grand finale */
313 return adduser(bb_path_passwd_file, &pw, makehome, setpass);
314}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * deluser (remove lusers from the system ;) for TinyLogin
4 *
5 * Copyright (C) 1999 by Lineo, inc. and John Beppu
6 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <sys/stat.h>
25#include <unistd.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include "busybox.h"
30
31
32#if ! defined CONFIG_DELUSER
33#include "delline.c"
34#else
35extern int del_line_matching(const char *login, const char *filename);
36#endif
37
38int delgroup_main(int argc, char **argv)
39{
40 /* int successful; */
41 int failure;
42
43 if (argc != 2) {
44 bb_show_usage();
45 } else {
46
47 failure = del_line_matching(argv[1], bb_path_group_file);
48#ifdef CONFIG_FEATURE_SHADOWPASSWDS
49 if (access(bb_path_gshadow_file, W_OK) == 0) {
50 /* EDR the |= works if the error is not 0, so he had it wrong */
51 failure |= del_line_matching(argv[1], bb_path_gshadow_file);
52 }
53#endif
54 if (failure) {
55 bb_error_msg_and_die("%s: Group could not be removed\n", argv[1]);
56 }
57
58 }
59 return (EXIT_SUCCESS);
60}
61
62/* $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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * deluser (remove lusers from the system ;) for TinyLogin
4 *
5 * Copyright (C) 1999 by Lineo, inc. and John Beppu
6 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <sys/stat.h>
25#include <unistd.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include "busybox.h"
30
31
32
33/* where to start and stop deletion */
34typedef struct {
35 size_t start;
36 size_t stop;
37} Bounds;
38
39/* An interesting side-effect of boundary()'s
40 * implementation is that the first user (typically root)
41 * cannot be removed. Let's call it a feature. */
42static inline Bounds boundary(const char *buffer, const char *login)
43{
44 char needle[256];
45 char *start;
46 char *stop;
47 Bounds b;
48
49 snprintf(needle, 256, "\n%s:", login);
50 needle[255] = 0;
51 start = strstr(buffer, needle);
52 if (!start) {
53 b.start = 0;
54 b.stop = 0;
55 return b;
56 }
57 start++;
58
59 stop = index(start, '\n'); /* index is a BSD-ism */
60 b.start = start - buffer;
61 b.stop = stop - buffer;
62 return b;
63}
64
65/* grep -v ^login (except it only deletes the first match) */
66/* ...in fact, I think I'm going to simplify this later */
67int del_line_matching(const char *login, const char *filename)
68{
69 char *buffer;
70 FILE *passwd;
71 size_t len;
72 Bounds b;
73 struct stat statbuf;
74
75 /* load into buffer */
76 passwd = fopen(filename, "r");
77 if (!passwd) {
78 return 1;
79 }
80 stat(filename, &statbuf);
81 len = statbuf.st_size;
82 buffer = (char *) malloc(len * sizeof(char));
83
84 if (!buffer) {
85 fclose(passwd);
86 return 1;
87 }
88 fread(buffer, len, sizeof(char), passwd);
89
90 fclose(passwd);
91
92 /* find the user to remove */
93 b = boundary(buffer, login);
94 if (b.stop == 0) {
95 free(buffer);
96 return 1;
97 }
98
99 /* write the file w/o the user */
100 passwd = fopen(filename, "w");
101 if (!passwd) {
102 return 1;
103 }
104 fwrite(buffer, (b.start - 1), sizeof(char), passwd);
105 fwrite(&buffer[b.stop], (len - b.stop), sizeof(char), passwd);
106
107 fclose(passwd);
108
109 return 0;
110}
111
112
113/* $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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * deluser (remove lusers from the system ;) for TinyLogin
4 *
5 * Copyright (C) 1999 by Lineo, inc. and John Beppu
6 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <sys/stat.h>
25#include <unistd.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include "busybox.h"
30
31
32#include "delline.c"
33
34static const char deluser_format[]="%s: User could not be removed from %s";
35
36int deluser_main(int argc, char **argv)
37{
38 /* int successful; */
39 int failure;
40
41 if (argc != 2) {
42 bb_show_usage();
43 } else {
44
45 failure = del_line_matching(argv[1], bb_path_passwd_file);
46 if (failure) {
47 bb_error_msg_and_die(deluser_format, argv[1], bb_path_passwd_file);
48 }
49#ifdef CONFIG_FEATURE_SHADOWPASSWDS
50 failure = del_line_matching(argv[1], bb_path_shadow_file);
51 if (failure) {
52 bb_error_msg_and_die(deluser_format, argv[1], bb_path_shadow_file);
53 }
54 failure = del_line_matching(argv[1], bb_path_gshadow_file);
55 if (failure) {
56 bb_error_msg_and_die(deluser_format, argv[1], bb_path_gshadow_file);
57 }
58#endif
59 failure = del_line_matching(argv[1], bb_path_group_file);
60 if (failure) {
61 bb_error_msg_and_die(deluser_format, argv[1], bb_path_group_file);
62 }
63
64 }
65 return (EXIT_SUCCESS);
66}
67
68/* $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 @@
1/* vi: set sw=4 ts=4: */
2/* agetty.c - another getty program for Linux. By W. Z. Venema 1989
3 Ported to Linux by Peter Orbaek <poe@daimi.aau.dk>
4 This program is freely distributable. The entire man-page used to
5 be here. Now read the real man-page agetty.8 instead.
6
7 -f option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95
8
9 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
10 - added Native Language Support
11
12 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>
13 - enable hardware flow control before displaying /etc/issue
14
15*/
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <unistd.h>
20#include <string.h>
21#include <sys/ioctl.h>
22#include <errno.h>
23#include <sys/stat.h>
24#include <sys/signal.h>
25#include <fcntl.h>
26#include <stdarg.h>
27#include <ctype.h>
28#include <utmp.h>
29#include <getopt.h>
30#include <termios.h>
31#include "busybox.h"
32
33#define _PATH_LOGIN "/bin/login"
34
35 /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */
36#ifdef CONFIG_SYSLOGD
37#include <sys/param.h>
38#define USE_SYSLOG
39#include <syslog.h>
40#endif
41
42
43 /*
44 * Some heuristics to find out what environment we are in: if it is not
45 * System V, assume it is SunOS 4.
46 */
47
48#ifdef LOGIN_PROCESS /* defined in System V utmp.h */
49#define SYSV_STYLE /* select System V style getty */
50#ifdef CONFIG_FEATURE_U_W_TMP
51extern void updwtmp(const char *filename, const struct utmp *ut);
52#endif
53#endif /* LOGIN_PROCESS */
54
55 /*
56 * Things you may want to modify.
57 *
58 * If ISSUE is not defined, agetty will never display the contents of the
59 * /etc/issue file. You will not want to spit out large "issue" files at the
60 * wrong baud rate. Relevant for System V only.
61 *
62 * You may disagree with the default line-editing etc. characters defined
63 * below. Note, however, that DEL cannot be used for interrupt generation
64 * and for line editing at the same time.
65 */
66
67#ifdef SYSV_STYLE
68#define ISSUE "/etc/issue" /* displayed before the login prompt */
69#include <sys/utsname.h>
70#include <time.h>
71#endif
72
73/* Some shorthands for control characters. */
74
75#define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */
76#define CR CTL('M') /* carriage return */
77#define NL CTL('J') /* line feed */
78#define BS CTL('H') /* back space */
79#define DEL CTL('?') /* delete */
80
81/* Defaults for line-editing etc. characters; you may want to change this. */
82
83#define DEF_ERASE DEL /* default erase character */
84#define DEF_INTR CTL('C') /* default interrupt character */
85#define DEF_QUIT CTL('\\') /* default quit char */
86#define DEF_KILL CTL('U') /* default kill char */
87#define DEF_EOF CTL('D') /* default EOF char */
88#define DEF_EOL 0
89#define DEF_SWITCH 0 /* default switch char */
90
91 /*
92 * SunOS 4.1.1 termio is broken. We must use the termios stuff instead,
93 * because the termio -> termios translation does not clear the termios
94 * CIBAUD bits. Therefore, the tty driver would sometimes report that input
95 * baud rate != output baud rate. I did not notice that problem with SunOS
96 * 4.1. We will use termios where available, and termio otherwise.
97 */
98
99/* linux 0.12 termio is broken too, if we use it c_cc[VERASE] isn't set
100 properly, but all is well if we use termios?! */
101
102#ifdef TCGETS
103#undef TCGETA
104#undef TCSETA
105#undef TCSETAW
106#define termio termios
107#define TCGETA TCGETS
108#define TCSETA TCSETS
109#define TCSETAW TCSETSW
110#endif
111
112 /*
113 * This program tries to not use the standard-i/o library. This keeps the
114 * executable small on systems that do not have shared libraries (System V
115 * Release <3).
116 */
117#ifndef BUFSIZ
118#define BUFSIZ 1024
119#endif
120
121 /*
122 * When multiple baud rates are specified on the command line, the first one
123 * we will try is the first one specified.
124 */
125
126#define FIRST_SPEED 0
127
128/* Storage for command-line options. */
129
130#define MAX_SPEED 10 /* max. nr. of baud rates */
131
132struct options {
133 int flags; /* toggle switches, see below */
134 int timeout; /* time-out period */
135 char *login; /* login program */
136 char *tty; /* name of tty */
137 char *initstring; /* modem init string */
138 char *issue; /* alternative issue file */
139 int numspeed; /* number of baud rates to try */
140 int speeds[MAX_SPEED]; /* baud rates to be tried */
141};
142
143#define F_PARSE (1<<0) /* process modem status messages */
144#define F_ISSUE (1<<1) /* display /etc/issue */
145#define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */
146#define F_LOCAL (1<<3) /* force local */
147#define F_INITSTRING (1<<4) /* initstring is set */
148#define F_WAITCRLF (1<<5) /* wait for CR or LF */
149#define F_CUSTISSUE (1<<6) /* give alternative issue file */
150#define F_NOPROMPT (1<<7) /* don't ask for login name! */
151
152/* Storage for things detected while the login name was read. */
153
154struct chardata {
155 int erase; /* erase character */
156 int kill; /* kill character */
157 int eol; /* end-of-line character */
158 int parity; /* what parity did we see */
159 int capslock; /* upper case without lower case */
160};
161
162/* Initial values for the above. */
163
164struct chardata init_chardata = {
165 DEF_ERASE, /* default erase character */
166 DEF_KILL, /* default kill character */
167 13, /* default eol char */
168 0, /* space parity */
169 0, /* no capslock */
170};
171
172#if 0
173struct Speedtab {
174 long speed;
175 int code;
176};
177
178static struct Speedtab speedtab[] = {
179 {50, B50},
180 {75, B75},
181 {110, B110},
182 {134, B134},
183 {150, B150},
184 {200, B200},
185 {300, B300},
186 {600, B600},
187 {1200, B1200},
188 {1800, B1800},
189 {2400, B2400},
190 {4800, B4800},
191 {9600, B9600},
192#ifdef B19200
193 {19200, B19200},
194#endif
195#ifdef B38400
196 {38400, B38400},
197#endif
198#ifdef EXTA
199 {19200, EXTA},
200#endif
201#ifdef EXTB
202 {38400, EXTB},
203#endif
204#ifdef B57600
205 {57600, B57600},
206#endif
207#ifdef B115200
208 {115200, B115200},
209#endif
210#ifdef B230400
211 {230400, B230400},
212#endif
213 {0, 0},
214};
215#endif
216
217static void parse_args(int argc, char **argv, struct options *op);
218static void parse_speeds(struct options *op, char *arg);
219static void open_tty(char *tty, struct termio *tp, int local);
220static void termio_init(struct termio *tp, int speed, struct options *op);
221static void auto_baud(struct termio *tp);
222static void do_prompt(struct options *op, struct termio *tp);
223static void next_speed(struct termio *tp, struct options *op);
224static char *get_logname(struct options *op, struct chardata *cp,
225
226 struct termio *tp);
227static void termio_final(struct options *op, struct termio *tp,
228
229 struct chardata *cp);
230static int caps_lock(const char *s);
231static int bcode(char *s);
232static void error(const char *fmt, ...) __attribute__ ((noreturn));
233
234#ifdef CONFIG_FEATURE_U_W_TMP
235static void update_utmp(char *line);
236#endif
237
238/* The following is used for understandable diagnostics. */
239
240/* Fake hostname for ut_host specified on command line. */
241static char *fakehost = NULL;
242
243/* ... */
244#ifdef DEBUGGING
245#define debug(s) fprintf(dbf,s); fflush(dbf)
246#define DEBUGTERM "/dev/ttyp0"
247FILE *dbf;
248#else
249#define debug(s) /* nothing */
250#endif
251
252int getty_main(int argc, char **argv)
253{
254 char *logname = NULL; /* login name, given to /bin/login */
255 struct chardata chardata; /* set by get_logname() */
256 struct termio termio; /* terminal mode bits */
257 static struct options options = {
258 F_ISSUE, /* show /etc/issue (SYSV_STYLE) */
259 0, /* no timeout */
260 _PATH_LOGIN, /* default login program */
261 "tty1", /* default tty line */
262 "", /* modem init string */
263 ISSUE, /* default issue file */
264 0, /* no baud rates known yet */
265 };
266
267#ifdef DEBUGGING
268 dbf = bb_xfopen(DEBUGTERM, "w");
269
270 {
271 int i;
272
273 for (i = 1; i < argc; i++) {
274 debug(argv[i]);
275 debug("\n");
276 }
277 }
278#endif
279
280 /* Parse command-line arguments. */
281
282 parse_args(argc, argv, &options);
283
284#ifdef __linux__
285 setsid();
286#endif
287
288 /* Update the utmp file. */
289
290
291#ifdef SYSV_STYLE
292#ifdef CONFIG_FEATURE_U_W_TMP
293 update_utmp(options.tty);
294#endif
295#endif
296
297 debug("calling open_tty\n");
298 /* Open the tty as standard { input, output, error }. */
299 open_tty(options.tty, &termio, options.flags & F_LOCAL);
300
301#ifdef __linux__
302 {
303 int iv;
304
305 iv = getpid();
306 ioctl(0, TIOCSPGRP, &iv);
307 }
308#endif
309 /* Initialize the termio settings (raw mode, eight-bit, blocking i/o). */
310 debug("calling termio_init\n");
311 termio_init(&termio, options.speeds[FIRST_SPEED], &options);
312
313 /* write the modem init string and DON'T flush the buffers */
314 if (options.flags & F_INITSTRING) {
315 debug("writing init string\n");
316 write(1, options.initstring, strlen(options.initstring));
317 }
318
319 if (!(options.flags & F_LOCAL)) {
320 /* go to blocking write mode unless -L is specified */
321 fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK);
322 }
323
324 /* Optionally detect the baud rate from the modem status message. */
325 debug("before autobaud\n");
326 if (options.flags & F_PARSE)
327 auto_baud(&termio);
328
329 /* Set the optional timer. */
330 if (options.timeout)
331 (void) alarm((unsigned) options.timeout);
332
333 /* optionally wait for CR or LF before writing /etc/issue */
334 if (options.flags & F_WAITCRLF) {
335 char ch;
336
337 debug("waiting for cr-lf\n");
338 while (read(0, &ch, 1) == 1) {
339 ch &= 0x7f; /* strip "parity bit" */
340#ifdef DEBUGGING
341 fprintf(dbf, "read %c\n", ch);
342#endif
343 if (ch == '\n' || ch == '\r')
344 break;
345 }
346 }
347
348 chardata = init_chardata;
349 if (!(options.flags & F_NOPROMPT)) {
350 /* Read the login name. */
351 debug("reading login name\n");
352 /* while ((logname = get_logname(&options, &chardata, &termio)) == 0) */
353 while ((logname = get_logname(&options, &chardata, &termio)) ==
354 NULL) next_speed(&termio, &options);
355 }
356
357 /* Disable timer. */
358
359 if (options.timeout)
360 (void) alarm(0);
361
362 /* Finalize the termio settings. */
363
364 termio_final(&options, &termio, &chardata);
365
366 /* Now the newline character should be properly written. */
367
368 (void) write(1, "\n", 1);
369
370 /* Let the login program take care of password validation. */
371
372 (void) execl(options.login, options.login, "--", logname, (char *) 0);
373 error("%s: can't exec %s: %m", options.tty, options.login);
374}
375
376/* parse-args - parse command-line arguments */
377
378static void parse_args(int argc, char **argv, struct options *op)
379{
380 extern char *optarg; /* getopt */
381 extern int optind; /* getopt */
382 int c;
383
384 while (isascii(c = getopt(argc, argv, "I:LH:f:hil:mt:wn"))) {
385 switch (c) {
386 case 'I':
387 if (!(op->initstring = strdup(optarg)))
388 error(bb_msg_memory_exhausted);
389
390 {
391 const char *p;
392 char *q;
393
394 /* copy optarg into op->initstring decoding \ddd
395 octal codes into chars */
396 q = op->initstring;
397 p = optarg;
398 while (*p) {
399 if (*p == '\\') {
400 p++;
401 *q++ = bb_process_escape_sequence(&p);
402 } else {
403 *q++ = *p++;
404 }
405 }
406 *q = '\0';
407 }
408 op->flags |= F_INITSTRING;
409 break;
410
411 case 'L': /* force local */
412 op->flags |= F_LOCAL;
413 break;
414 case 'H': /* fake login host */
415 fakehost = optarg;
416 break;
417 case 'f': /* custom issue file */
418 op->flags |= F_CUSTISSUE;
419 op->issue = optarg;
420 break;
421 case 'h': /* enable h/w flow control */
422 op->flags |= F_RTSCTS;
423 break;
424 case 'i': /* do not show /etc/issue */
425 op->flags &= ~F_ISSUE;
426 break;
427 case 'l':
428 op->login = optarg; /* non-default login program */
429 break;
430 case 'm': /* parse modem status message */
431 op->flags |= F_PARSE;
432 break;
433 case 'n':
434 op->flags |= F_NOPROMPT;
435 break;
436 case 't': /* time out */
437 if ((op->timeout = atoi(optarg)) <= 0)
438 error("bad timeout value: %s", optarg);
439 break;
440 case 'w':
441 op->flags |= F_WAITCRLF;
442 break;
443 default:
444 bb_show_usage();
445 }
446 }
447 debug("after getopt loop\n");
448 if (argc < optind + 2) /* check parameter count */
449 bb_show_usage();
450
451 /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
452 if ('0' <= argv[optind][0] && argv[optind][0] <= '9') {
453 /* a number first, assume it's a speed (BSD style) */
454 parse_speeds(op, argv[optind++]); /* baud rate(s) */
455 op->tty = argv[optind]; /* tty name */
456 } else {
457 op->tty = argv[optind++]; /* tty name */
458 parse_speeds(op, argv[optind]); /* baud rate(s) */
459 }
460
461 optind++;
462 if (argc > optind && argv[optind])
463 setenv("TERM", argv[optind], 1);
464
465 debug("exiting parseargs\n");
466}
467
468/* parse_speeds - parse alternate baud rates */
469
470static void parse_speeds(struct options *op, char *arg)
471{
472 char *cp;
473
474 debug("entered parse_speeds\n");
475 for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {
476 if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
477 error("bad speed: %s", cp);
478 if (op->numspeed > MAX_SPEED)
479 error("too many alternate speeds");
480 }
481 debug("exiting parsespeeds\n");
482}
483
484#ifdef SYSV_STYLE
485#ifdef CONFIG_FEATURE_U_W_TMP
486
487/* update_utmp - update our utmp entry */
488static void update_utmp(char *line)
489{
490 struct utmp ut;
491 struct utmp *utp;
492 time_t t;
493 int mypid = getpid();
494#if ! (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1))
495 struct flock lock;
496#endif
497
498 /*
499 * The utmp file holds miscellaneous information about things started by
500 * /sbin/init and other system-related events. Our purpose is to update
501 * the utmp entry for the current process, in particular the process type
502 * and the tty line we are listening to. Return successfully only if the
503 * utmp file can be opened for update, and if we are able to find our
504 * entry in the utmp file.
505 */
506 if (access(_PATH_UTMP, R_OK|W_OK) == -1) {
507 close(creat(_PATH_UTMP, 0664));
508 }
509 utmpname(_PATH_UTMP);
510 setutent();
511 while ((utp = getutent())
512 && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid)) /* nothing */
513 ;
514
515 if (utp) {
516 memcpy(&ut, utp, sizeof(ut));
517 } else {
518 /* some inits don't initialize utmp... */
519 memset(&ut, 0, sizeof(ut));
520 strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
521 }
522 /*endutent(); */
523
524 strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user));
525 strncpy(ut.ut_line, line, sizeof(ut.ut_line));
526 if (fakehost)
527 strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
528 time(&t);
529 ut.ut_time = t;
530 ut.ut_type = LOGIN_PROCESS;
531 ut.ut_pid = mypid;
532
533 pututline(&ut);
534 endutent();
535
536 {
537 if (access(_PATH_WTMP, R_OK|W_OK) == -1) {
538 close(creat(_PATH_WTMP, 0664));
539 }
540 updwtmp(_PATH_WTMP, &ut);
541 }
542}
543
544#endif /* CONFIG_FEATURE_U_W_TMP */
545#endif /* SYSV_STYLE */
546
547/* open_tty - set up tty as standard { input, output, error } */
548static void open_tty(char *tty, struct termio *tp, int local)
549{
550 /* Get rid of the present standard { output, error} if any. */
551
552 (void) close(1);
553 (void) close(2);
554 errno = 0; /* ignore above errors */
555
556 /* Set up new standard input, unless we are given an already opened port. */
557
558 if (strcmp(tty, "-")) {
559 struct stat st;
560
561 /* Sanity checks... */
562
563 if (chdir("/dev"))
564 error("/dev: chdir() failed: %m");
565 if (stat(tty, &st) < 0)
566 error("/dev/%s: %m", tty);
567 if ((st.st_mode & S_IFMT) != S_IFCHR)
568 error("/dev/%s: not a character device", tty);
569
570 /* Open the tty as standard input. */
571
572 (void) close(0);
573 errno = 0; /* ignore close(2) errors */
574
575 debug("open(2)\n");
576 if (open(tty, O_RDWR | O_NONBLOCK, 0) != 0)
577 error("/dev/%s: cannot open as standard input: %m", tty);
578
579 } else {
580
581 /*
582 * Standard input should already be connected to an open port. Make
583 * sure it is open for read/write.
584 */
585
586 if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR)
587 error("%s: not open for read/write", tty);
588 }
589
590 /* Set up standard output and standard error file descriptors. */
591 debug("duping\n");
592 if (dup(0) != 1 || dup(0) != 2) /* set up stdout and stderr */
593 error("%s: dup problem: %m", tty); /* we have a problem */
594
595 /*
596 * The following ioctl will fail if stdin is not a tty, but also when
597 * there is noise on the modem control lines. In the latter case, the
598 * common course of action is (1) fix your cables (2) give the modem more
599 * time to properly reset after hanging up. SunOS users can achieve (2)
600 * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
601 * 5 seconds seems to be a good value.
602 */
603
604 if (ioctl(0, TCGETA, tp) < 0)
605 error("%s: ioctl: %m", tty);
606
607 /*
608 * It seems to be a terminal. Set proper protections and ownership. Mode
609 * 0622 is suitable for SYSV <4 because /bin/login does not change
610 * protections. SunOS 4 login will change the protections to 0620 (write
611 * access for group tty) after the login has succeeded.
612 */
613
614#ifdef DEBIAN
615 {
616 /* tty to root.dialout 660 */
617 struct group *gr;
618 int id;
619
620 id = (gr = getgrnam("dialout")) ? gr->gr_gid : 0;
621 chown(tty, 0, id);
622 chmod(tty, 0660);
623
624 /* vcs,vcsa to root.sys 600 */
625 if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) {
626 char *vcs, *vcsa;
627
628 if (!(vcs = strdup(tty)))
629 error("Can't malloc for vcs");
630 if (!(vcsa = malloc(strlen(tty) + 2)))
631 error("Can't malloc for vcsa");
632 strcpy(vcs, "vcs");
633 strcpy(vcs + 3, tty + 3);
634 strcpy(vcsa, "vcsa");
635 strcpy(vcsa + 4, tty + 3);
636
637 id = (gr = getgrnam("sys")) ? gr->gr_gid : 0;
638 chown(vcs, 0, id);
639 chmod(vcs, 0600);
640 chown(vcsa, 0, id);
641 chmod(vcs, 0600);
642
643 free(vcs);
644 free(vcsa);
645 }
646 }
647#else
648 (void) chown(tty, 0, 0); /* root, sys */
649 (void) chmod(tty, 0622); /* crw--w--w- */
650 errno = 0; /* ignore above errors */
651#endif
652}
653
654/* termio_init - initialize termio settings */
655
656static void termio_init(struct termio *tp, int speed, struct options *op)
657{
658
659 /*
660 * Initial termio settings: 8-bit characters, raw-mode, blocking i/o.
661 * Special characters are set after we have read the login name; all
662 * reads will be done in raw mode anyway. Errors will be dealt with
663 * lateron.
664 */
665#ifdef __linux__
666 /* flush input and output queues, important for modems! */
667 (void) ioctl(0, TCFLSH, TCIOFLUSH);
668#endif
669
670 tp->c_cflag = CS8 | HUPCL | CREAD | speed;
671 if (op->flags & F_LOCAL) {
672 tp->c_cflag |= CLOCAL;
673 }
674
675 tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0;
676 tp->c_cc[VMIN] = 1;
677 tp->c_cc[VTIME] = 0;
678
679 /* Optionally enable hardware flow control */
680
681#ifdef CRTSCTS
682 if (op->flags & F_RTSCTS)
683 tp->c_cflag |= CRTSCTS;
684#endif
685
686 (void) ioctl(0, TCSETA, tp);
687
688 /* go to blocking input even in local mode */
689 fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK);
690
691 debug("term_io 2\n");
692}
693
694/* auto_baud - extract baud rate from modem status message */
695static void auto_baud(struct termio *tp)
696{
697 int speed;
698 int vmin;
699 unsigned iflag;
700 char buf[BUFSIZ];
701 char *bp;
702 int nread;
703
704 /*
705 * This works only if the modem produces its status code AFTER raising
706 * the DCD line, and if the computer is fast enough to set the proper
707 * baud rate before the message has gone by. We expect a message of the
708 * following format:
709 *
710 * <junk><number><junk>
711 *
712 * The number is interpreted as the baud rate of the incoming call. If the
713 * modem does not tell us the baud rate within one second, we will keep
714 * using the current baud rate. It is advisable to enable BREAK
715 * processing (comma-separated list of baud rates) if the processing of
716 * modem status messages is enabled.
717 */
718
719 /*
720 * Use 7-bit characters, don't block if input queue is empty. Errors will
721 * be dealt with lateron.
722 */
723
724 iflag = tp->c_iflag;
725 tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */
726 vmin = tp->c_cc[VMIN];
727 tp->c_cc[VMIN] = 0; /* don't block if queue empty */
728 (void) ioctl(0, TCSETA, tp);
729
730 /*
731 * Wait for a while, then read everything the modem has said so far and
732 * try to extract the speed of the dial-in call.
733 */
734
735 (void) sleep(1);
736 if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) {
737 buf[nread] = '\0';
738 for (bp = buf; bp < buf + nread; bp++) {
739 if (isascii(*bp) && isdigit(*bp)) {
740 if ((speed = bcode(bp))) {
741 tp->c_cflag &= ~CBAUD;
742 tp->c_cflag |= speed;
743 }
744 break;
745 }
746 }
747 }
748 /* Restore terminal settings. Errors will be dealt with lateron. */
749
750 tp->c_iflag = iflag;
751 tp->c_cc[VMIN] = vmin;
752 (void) ioctl(0, TCSETA, tp);
753}
754
755/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
756static void do_prompt(struct options *op, struct termio *tp)
757{
758#ifdef ISSUE /* optional: show /etc/issue */
759 print_login_issue(op->issue, op->tty);
760#endif
761 print_login_prompt();
762}
763
764/* next_speed - select next baud rate */
765static void next_speed(struct termio *tp, struct options *op)
766{
767 static int baud_index = FIRST_SPEED; /* current speed index */
768
769 baud_index = (baud_index + 1) % op->numspeed;
770 tp->c_cflag &= ~CBAUD;
771 tp->c_cflag |= op->speeds[baud_index];
772 (void) ioctl(0, TCSETA, tp);
773}
774
775/* get_logname - get user name, establish parity, speed, erase, kill, eol */
776/* return NULL on failure, logname on success */
777static char *get_logname(struct options *op, struct chardata *cp, struct termio *tp)
778{
779 static char logname[BUFSIZ];
780 char *bp;
781 char c; /* input character, full eight bits */
782 char ascval; /* low 7 bits of input character */
783 int bits; /* # of "1" bits per character */
784 int mask; /* mask with 1 bit up */
785 static char *erase[] = { /* backspace-space-backspace */
786 "\010\040\010", /* space parity */
787 "\010\040\010", /* odd parity */
788 "\210\240\210", /* even parity */
789 "\210\240\210", /* no parity */
790 };
791
792 /* Initialize kill, erase, parity etc. (also after switching speeds). */
793
794 *cp = init_chardata;
795
796 /* Flush pending input (esp. after parsing or switching the baud rate). */
797
798 (void) sleep(1);
799 (void) ioctl(0, TCFLSH, TCIFLUSH);
800
801 /* Prompt for and read a login name. */
802
803 for (*logname = 0; *logname == 0; /* void */ ) {
804
805 /* Write issue file and prompt, with "parity" bit == 0. */
806
807 do_prompt(op, tp);
808
809 /* Read name, watch for break, parity, erase, kill, end-of-line. */
810
811 for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) {
812
813 /* Do not report trivial EINTR/EIO errors. */
814
815 if (read(0, &c, 1) < 1) {
816 if (errno == EINTR || errno == EIO)
817 exit(0);
818 error("%s: read: %m", op->tty);
819 }
820 /* Do BREAK handling elsewhere. */
821
822 if ((c == 0) && op->numspeed > 1)
823 /* return (0); */
824 return NULL;
825
826 /* Do parity bit handling. */
827
828 if (c != (ascval = (c & 0177))) { /* "parity" bit on ? */
829 for (bits = 1, mask = 1; mask & 0177; mask <<= 1)
830 if (mask & ascval)
831 bits++; /* count "1" bits */
832 cp->parity |= ((bits & 1) ? 1 : 2);
833 }
834 /* Do erase, kill and end-of-line processing. */
835
836 switch (ascval) {
837 case CR:
838 case NL:
839 *bp = 0; /* terminate logname */
840 cp->eol = ascval; /* set end-of-line char */
841 break;
842 case BS:
843 case DEL:
844 case '#':
845 cp->erase = ascval; /* set erase character */
846 if (bp > logname) {
847 (void) write(1, erase[cp->parity], 3);
848 bp--;
849 }
850 break;
851 case CTL('U'):
852 case '@':
853 cp->kill = ascval; /* set kill character */
854 while (bp > logname) {
855 (void) write(1, erase[cp->parity], 3);
856 bp--;
857 }
858 break;
859 case CTL('D'):
860 exit(0);
861 default:
862 if (!isascii(ascval) || !isprint(ascval)) {
863 /* ignore garbage characters */ ;
864 } else if (bp - logname >= sizeof(logname) - 1) {
865 error("%s: input overrun", op->tty);
866 } else {
867 (void) write(1, &c, 1); /* echo the character */
868 *bp++ = ascval; /* and store it */
869 }
870 break;
871 }
872 }
873 }
874 /* Handle names with upper case and no lower case. */
875
876 if ((cp->capslock = caps_lock(logname))) {
877 for (bp = logname; *bp; bp++)
878 if (isupper(*bp))
879 *bp = tolower(*bp); /* map name to lower case */
880 }
881 return (logname);
882}
883
884/* termio_final - set the final tty mode bits */
885static void termio_final(struct options *op, struct termio *tp, struct chardata *cp)
886{
887 /* General terminal-independent stuff. */
888
889 tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */
890 tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
891 /* no longer| ECHOCTL | ECHOPRT */
892 tp->c_oflag |= OPOST;
893 /* tp->c_cflag = 0; */
894 tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */
895 tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */
896 tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */
897 tp->c_cc[VEOL] = DEF_EOL;
898 tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */
899
900 /* Account for special characters seen in input. */
901
902 if (cp->eol == CR) {
903 tp->c_iflag |= ICRNL; /* map CR in input to NL */
904 tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */
905 }
906 tp->c_cc[VERASE] = cp->erase; /* set erase character */
907 tp->c_cc[VKILL] = cp->kill; /* set kill character */
908
909 /* Account for the presence or absence of parity bits in input. */
910
911 switch (cp->parity) {
912 case 0: /* space (always 0) parity */
913 break;
914 case 1: /* odd parity */
915 tp->c_cflag |= PARODD;
916 /* FALLTHROUGH */
917 case 2: /* even parity */
918 tp->c_cflag |= PARENB;
919 tp->c_iflag |= INPCK | ISTRIP;
920 /* FALLTHROUGH */
921 case (1 | 2): /* no parity bit */
922 tp->c_cflag &= ~CSIZE;
923 tp->c_cflag |= CS7;
924 break;
925 }
926 /* Account for upper case without lower case. */
927
928 if (cp->capslock) {
929 tp->c_iflag |= IUCLC;
930 tp->c_lflag |= XCASE;
931 tp->c_oflag |= OLCUC;
932 }
933 /* Optionally enable hardware flow control */
934
935#ifdef CRTSCTS
936 if (op->flags & F_RTSCTS)
937 tp->c_cflag |= CRTSCTS;
938#endif
939
940 /* Finally, make the new settings effective */
941
942 if (ioctl(0, TCSETA, tp) < 0)
943 error("%s: ioctl: TCSETA: %m", op->tty);
944}
945
946/* caps_lock - string contains upper case without lower case */
947/* returns 1 if true, 0 if false */
948static int caps_lock(const char *s)
949{
950 int capslock;
951
952 for (capslock = 0; *s; s++) {
953 if (islower(*s))
954 return (0);
955 if (capslock == 0)
956 capslock = isupper(*s);
957 }
958 return (capslock);
959}
960
961/* bcode - convert speed string to speed code; return 0 on failure */
962static int bcode(char *s)
963{
964 int r;
965 unsigned long value;
966 if (safe_strtoul(s, &value)) {
967 return -1;
968 }
969 if ((r = bb_value_to_baud(value)) > 0) {
970 return r;
971 }
972 return 0;
973}
974
975/* error - report errors to console or syslog; only understands %s and %m */
976
977#define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2)
978
979/*
980 * output error messages
981 */
982static void error(const char *fmt, ...)
983{
984 va_list va_alist;
985 char buf[256], *bp;
986
987#ifndef USE_SYSLOG
988 int fd;
989#endif
990
991#ifdef USE_SYSLOG
992 buf[0] = '\0';
993 bp = buf;
994#else
995 strncpy(buf, bb_applet_name, 256);
996 strncat(buf, ": ", 256);
997 buf[255] = 0;
998 bp = buf + strlen(buf);
999#endif
1000
1001 va_start(va_alist, fmt);
1002 vsnprintf(bp, 256 - strlen(buf), fmt, va_alist);
1003 buf[255] = 0;
1004 va_end(va_alist);
1005
1006#ifdef USE_SYSLOG
1007 openlog(bb_applet_name, 0, LOG_AUTH);
1008 syslog(LOG_ERR, "%s", buf);
1009 closelog();
1010#else
1011 strncat(bp, "\r\n", 256 - strlen(buf));
1012 buf[255] = 0;
1013 if ((fd = open("/dev/console", 1)) >= 0) {
1014 write(fd, buf, strlen(buf));
1015 close(fd);
1016 }
1017#endif
1018 (void) sleep((unsigned) 10); /* be kind to init(8) */
1019 exit(1);
1020}
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 @@
1/* vi: set sw=4 ts=4: */
2#include <fcntl.h>
3#include <signal.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <syslog.h>
8#include <termios.h>
9#include <unistd.h>
10#include <utmp.h>
11#include <sys/resource.h>
12#include <sys/stat.h>
13#include <sys/time.h>
14#include <sys/types.h>
15#include <ctype.h>
16#include <time.h>
17
18#include "busybox.h"
19#ifdef CONFIG_SELINUX
20#include <flask_util.h>
21#include <get_sid_list.h>
22#include <proc_secure.h>
23#include <fs_secure.h>
24#endif
25
26#ifdef CONFIG_FEATURE_U_W_TMP
27// import from utmp.c
28static void checkutmp(int picky);
29static void setutmp(const char *name, const char *line);
30/* Stuff global to this file */
31struct utmp utent;
32#endif
33
34// login defines
35#define TIMEOUT 60
36#define EMPTY_USERNAME_COUNT 10
37#define USERNAME_SIZE 32
38
39
40static int check_nologin ( int amroot );
41
42#if defined CONFIG_FEATURE_SECURETTY
43static int check_tty ( const char *tty );
44
45#else
46static inline int check_tty ( const char *tty ) { return 1; }
47
48#endif
49
50static int is_my_tty ( const char *tty );
51static int login_prompt ( char *buf_name );
52static void motd ( void );
53
54
55static void alarm_handler ( int sig )
56{
57 fprintf (stderr, "\nLogin timed out after %d seconds.\n", TIMEOUT );
58 exit ( EXIT_SUCCESS );
59}
60
61
62extern int login_main(int argc, char **argv)
63{
64 char tty[BUFSIZ];
65 char full_tty[200];
66 char fromhost[512];
67 char username[USERNAME_SIZE];
68 const char *tmp;
69 int amroot;
70 int flag;
71 int failed;
72 int count=0;
73 struct passwd *pw, pw_copy;
74#ifdef CONFIG_WHEEL_GROUP
75 struct group *grp;
76#endif
77 int opt_preserve = 0;
78 int opt_fflag = 0;
79 char *opt_host = 0;
80 int alarmstarted = 0;
81#ifdef CONFIG_SELINUX
82 int flask_enabled = is_flask_enabled();
83 security_id_t sid = 0, old_tty_sid, new_tty_sid;
84#endif
85
86 username[0]=0;
87 amroot = ( getuid ( ) == 0 );
88 signal ( SIGALRM, alarm_handler );
89 alarm ( TIMEOUT );
90 alarmstarted = 1;
91
92 while (( flag = getopt(argc, argv, "f:h:p")) != EOF ) {
93 switch ( flag ) {
94 case 'p':
95 opt_preserve = 1;
96 break;
97 case 'f':
98 /*
99 * username must be a separate token
100 * (-f root, *NOT* -froot). --marekm
101 */
102 if ( optarg != argv[optind-1] )
103 bb_show_usage( );
104
105 if ( !amroot ) /* Auth bypass only if real UID is zero */
106 bb_error_msg_and_die ( "-f permission denied" );
107
108 safe_strncpy(username, optarg, USERNAME_SIZE);
109 opt_fflag = 1;
110 break;
111 case 'h':
112 opt_host = optarg;
113 break;
114 default:
115 bb_show_usage( );
116 }
117 }
118
119 if (optind < argc) // user from command line (getty)
120 safe_strncpy(username, argv[optind], USERNAME_SIZE);
121
122 if ( !isatty ( 0 ) || !isatty ( 1 ) || !isatty ( 2 ))
123 return EXIT_FAILURE; /* Must be a terminal */
124
125#ifdef CONFIG_FEATURE_U_W_TMP
126 checkutmp ( !amroot );
127#endif
128
129 tmp = ttyname ( 0 );
130 if ( tmp && ( strncmp ( tmp, "/dev/", 5 ) == 0 ))
131 safe_strncpy ( tty, tmp + 5, sizeof( tty ));
132 else if ( tmp && *tmp == '/' )
133 safe_strncpy ( tty, tmp, sizeof( tty ));
134 else
135 safe_strncpy ( tty, "UNKNOWN", sizeof( tty ));
136
137#ifdef CONFIG_FEATURE_U_W_TMP
138 if ( amroot )
139 memset ( utent.ut_host, 0, sizeof utent.ut_host );
140#endif
141
142 if ( opt_host ) {
143#ifdef CONFIG_FEATURE_U_W_TMP
144 safe_strncpy ( utent.ut_host, opt_host, sizeof( utent. ut_host ));
145#endif
146 snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s' from `%.200s'", tty, opt_host );
147 }
148 else
149 snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s'", tty );
150
151 setpgrp();
152
153 openlog ( "login", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH );
154
155 while ( 1 ) {
156 failed = 0;
157
158 if ( !username[0] )
159 if(!login_prompt ( username ))
160 return EXIT_FAILURE;
161
162 if ( !alarmstarted && ( TIMEOUT > 0 )) {
163 alarm ( TIMEOUT );
164 alarmstarted = 1;
165 }
166
167 if (!( pw = getpwnam ( username ))) {
168 pw_copy.pw_name = "UNKNOWN";
169 pw_copy.pw_passwd = "!";
170 opt_fflag = 0;
171 failed = 1;
172 } else
173 pw_copy = *pw;
174
175 pw = &pw_copy;
176
177 if (( pw-> pw_passwd [0] == '!' ) || ( pw-> pw_passwd[0] == '*' ))
178 failed = 1;
179
180 if ( opt_fflag ) {
181 opt_fflag = 0;
182 goto auth_ok;
183 }
184
185 if (!failed && ( pw-> pw_uid == 0 ) && ( !check_tty ( tty )))
186 failed = 1;
187
188 /* Don't check the password if password entry is empty (!) */
189 if ( !pw-> pw_passwd[0] )
190 goto auth_ok;
191
192 /* authorization takes place here */
193 if ( correct_password ( pw ))
194 goto auth_ok;
195
196 failed = 1;
197
198auth_ok:
199 if ( !failed)
200 break;
201
202 { // delay next try
203 time_t start, now;
204
205 time ( &start );
206 now = start;
207 while ( difftime ( now, start ) < FAIL_DELAY) {
208 sleep ( FAIL_DELAY );
209 time ( &now );
210 }
211 }
212
213 puts("Login incorrect");
214 username[0] = 0;
215 if ( ++count == 3 ) {
216 syslog ( LOG_WARNING, "invalid password for `%s'%s\n", pw->pw_name, fromhost);
217 return EXIT_FAILURE;
218 }
219 }
220
221 alarm ( 0 );
222 if ( check_nologin ( pw-> pw_uid == 0 ))
223 return EXIT_FAILURE;
224
225#ifdef CONFIG_FEATURE_U_W_TMP
226 setutmp ( username, tty );
227#endif
228#ifdef CONFIG_SELINUX
229 if (flask_enabled)
230 {
231 struct stat st;
232
233 if (get_default_sid(username, 0, &sid))
234 {
235 fprintf(stderr, "Unable to get SID for %s\n", username);
236 exit(1);
237 }
238 if (stat_secure(tty, &st, &old_tty_sid))
239 {
240 fprintf(stderr, "stat_secure(%.100s) failed: %.100s\n", tty, strerror(errno));
241 return EXIT_FAILURE;
242 }
243 if (security_change_sid (sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0)
244 {
245 fprintf(stderr, "security_change_sid(%.100s) failed: %.100s\n", tty, strerror(errno));
246 return EXIT_FAILURE;
247 }
248 if(chsid(tty, new_tty_sid) != 0)
249 {
250 fprintf(stderr, "chsid(%.100s, %d) failed: %.100s\n", tty, new_tty_sid, strerror(errno));
251 return EXIT_FAILURE;
252 }
253 }
254 else
255 sid = 0;
256#endif
257
258 if ( *tty != '/' )
259 snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty);
260 else
261 safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 );
262
263 if ( !is_my_tty ( full_tty ))
264 syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty );
265
266 /* Try these, but don't complain if they fail
267 * (for example when the root fs is read only) */
268 chown ( full_tty, pw-> pw_uid, pw-> pw_gid );
269 chmod ( full_tty, 0600 );
270
271 change_identity ( pw );
272 tmp = pw-> pw_shell;
273 if(!tmp || !*tmp)
274 tmp = DEFAULT_SHELL;
275 setup_environment ( tmp, 1, !opt_preserve, pw );
276
277 motd ( );
278 signal ( SIGALRM, SIG_DFL ); /* default alarm signal */
279
280 if ( pw-> pw_uid == 0 )
281 syslog ( LOG_INFO, "root login %s\n", fromhost );
282 run_shell ( tmp, 1, 0, 0
283#ifdef CONFIG_SELINUX
284 , sid
285#endif
286 ); /* exec the shell finally. */
287
288 return EXIT_FAILURE;
289}
290
291
292
293static int login_prompt ( char *buf_name )
294{
295 char buf [1024];
296 char *sp, *ep;
297 int i;
298
299 for(i=0; i<EMPTY_USERNAME_COUNT; i++) {
300 print_login_prompt();
301
302 if ( !fgets ( buf, sizeof( buf ) - 1, stdin ))
303 return 0;
304
305 if ( !strchr ( buf, '\n' ))
306 return 0;
307
308 for ( sp = buf; isspace ( *sp ); sp++ ) { }
309 for ( ep = sp; isgraph ( *ep ); ep++ ) { }
310
311 *ep = 0;
312 safe_strncpy(buf_name, sp, USERNAME_SIZE);
313 if(buf_name[0])
314 return 1;
315 }
316 return 0;
317}
318
319
320static int check_nologin ( int amroot )
321{
322 if ( access ( bb_path_nologin_file, F_OK ) == 0 ) {
323 FILE *fp;
324 int c;
325
326 if (( fp = fopen ( bb_path_nologin_file, "r" ))) {
327 while (( c = getc ( fp )) != EOF )
328 putchar (( c == '\n' ) ? '\r' : c );
329
330 fflush ( stdout );
331 fclose ( fp );
332 } else {
333 puts ( "\r\nSystem closed for routine maintenance.\r" );
334 }
335 if ( !amroot )
336 return 1;
337
338 puts ( "\r\n[Disconnect bypassed -- root login allowed.]\r" );
339 }
340 return 0;
341}
342
343#ifdef CONFIG_FEATURE_SECURETTY
344
345static int check_tty ( const char *tty )
346{
347 FILE *fp;
348 int i;
349 char buf[BUFSIZ];
350
351 if (( fp = fopen ( bb_path_securetty_file, "r" ))) {
352 while ( fgets ( buf, sizeof( buf ) - 1, fp )) {
353 for ( i = bb_strlen( buf ) - 1; i >= 0; --i ) {
354 if ( !isspace ( buf[i] ))
355 break;
356 }
357 buf[++i] = '\0';
358 if (( buf [0] == '\0' ) || ( buf [0] == '#' ))
359 continue;
360
361 if ( strcmp ( buf, tty ) == 0 ) {
362 fclose ( fp );
363 return 1;
364 }
365 }
366 fclose(fp);
367 return 0;
368 }
369 /* A missing securetty file is not an error. */
370 return 1;
371}
372
373#endif
374
375/* returns 1 if true */
376static int is_my_tty ( const char *tty )
377{
378 struct stat by_name, by_fd;
379
380 if ( stat ( tty, &by_name ) || fstat ( 0, &by_fd ))
381 return 0;
382
383 if ( by_name. st_rdev != by_fd. st_rdev )
384 return 0;
385 else
386 return 1;
387}
388
389
390static void motd ( )
391{
392 FILE *fp;
393 register int c;
394
395 if (( fp = fopen ( bb_path_motd_file, "r" ))) {
396 while (( c = getc ( fp )) != EOF )
397 putchar ( c );
398 fclose ( fp );
399 }
400}
401
402
403#ifdef CONFIG_FEATURE_U_W_TMP
404// vv Taken from tinylogin utmp.c vv
405
406#define NO_UTENT \
407 "No utmp entry. You must exec \"login\" from the lowest level \"sh\""
408#define NO_TTY \
409 "Unable to determine your tty name."
410
411/*
412 * checkutmp - see if utmp file is correct for this process
413 *
414 * System V is very picky about the contents of the utmp file
415 * and requires that a slot for the current process exist.
416 * The utmp file is scanned for an entry with the same process
417 * ID. If no entry exists the process exits with a message.
418 *
419 * The "picky" flag is for network and other logins that may
420 * use special flags. It allows the pid checks to be overridden.
421 * This means that getty should never invoke login with any
422 * command line flags.
423 */
424
425static void checkutmp(int picky)
426{
427 char *line;
428 struct utmp *ut;
429 pid_t pid = getpid();
430
431 setutent();
432
433 /* First, try to find a valid utmp entry for this process. */
434 while ((ut = getutent()))
435 if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] &&
436 (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS))
437 break;
438
439 /* If there is one, just use it, otherwise create a new one. */
440 if (ut) {
441 utent = *ut;
442 } else {
443 if (picky) {
444 puts(NO_UTENT);
445 exit(1);
446 }
447 line = ttyname(0);
448 if (!line) {
449 puts(NO_TTY);
450 exit(1);
451 }
452 if (strncmp(line, "/dev/", 5) == 0)
453 line += 5;
454 memset((void *) &utent, 0, sizeof utent);
455 utent.ut_type = LOGIN_PROCESS;
456 utent.ut_pid = pid;
457 strncpy(utent.ut_line, line, sizeof utent.ut_line);
458 /* XXX - assumes /dev/tty?? */
459 strncpy(utent.ut_id, utent.ut_line + 3, sizeof utent.ut_id);
460 strncpy(utent.ut_user, "LOGIN", sizeof utent.ut_user);
461 time(&utent.ut_time);
462 }
463}
464
465/*
466 * setutmp - put a USER_PROCESS entry in the utmp file
467 *
468 * setutmp changes the type of the current utmp entry to
469 * USER_PROCESS. the wtmp file will be updated as well.
470 */
471
472static void setutmp(const char *name, const char *line)
473{
474 utent.ut_type = USER_PROCESS;
475 strncpy(utent.ut_user, name, sizeof utent.ut_user);
476 time(&utent.ut_time);
477 /* other fields already filled in by checkutmp above */
478 setutent();
479 pututline(&utent);
480 endutent();
481 if (access(_PATH_WTMP, R_OK|W_OK) == -1) {
482 close(creat(_PATH_WTMP, 0664));
483 }
484 updwtmp(_PATH_WTMP, &utent);
485}
486#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 @@
1/* vi: set sw=4 ts=4: */
2#include <fcntl.h>
3#include <stdio.h>
4#include <string.h>
5#include <signal.h>
6#include <sys/stat.h>
7#include <sys/types.h>
8#include <unistd.h>
9#include <utime.h>
10#include <syslog.h>
11#include <time.h>
12#include <sys/resource.h>
13#include <errno.h>
14
15#include "busybox.h"
16
17static char crypt_passwd[128];
18
19static int create_backup(const char *backup, FILE * fp);
20static int new_password(const struct passwd *pw, int amroot, int algo);
21static void set_filesize_limit(int blocks);
22
23
24int get_algo(char *a)
25{
26 int x = 1; /* standard: MD5 */
27
28 if (strcasecmp(a, "des") == 0)
29 x = 0;
30 return x;
31}
32
33
34extern int update_passwd(const struct passwd *pw, char *crypt_pw)
35{
36 char filename[1024];
37 char buf[1025];
38 char buffer[80];
39 char username[32];
40 char *pw_rest;
41 int mask;
42 int continued;
43 FILE *fp;
44 FILE *out_fp;
45 struct stat sb;
46 struct flock lock;
47
48#ifdef CONFIG_FEATURE_SHADOWPASSWDS
49 if (access(bb_path_shadow_file, F_OK) == 0) {
50 snprintf(filename, sizeof filename, "%s", bb_path_shadow_file);
51 } else
52#endif
53 {
54 snprintf(filename, sizeof filename, "%s", bb_path_passwd_file);
55 }
56
57 if (((fp = fopen(filename, "r+")) == 0) || (fstat(fileno(fp), &sb))) {
58 /* return 0; */
59 return 1;
60 }
61
62 /* Lock the password file before updating */
63 lock.l_type = F_WRLCK;
64 lock.l_whence = SEEK_SET;
65 lock.l_start = 0;
66 lock.l_len = 0;
67 if (fcntl(fileno(fp), F_SETLK, &lock) < 0) {
68 fprintf(stderr, "%s: %s\n", filename, strerror(errno));
69 return 1;
70 }
71 lock.l_type = F_UNLCK;
72
73 snprintf(buf, sizeof buf, "%s-", filename);
74 if (create_backup(buf, fp)) {
75 fcntl(fileno(fp), F_SETLK, &lock);
76 fclose(fp);
77 return 1;
78 }
79 snprintf(buf, sizeof buf, "%s+", filename);
80 mask = umask(0777);
81 out_fp = fopen(buf, "w");
82 umask(mask);
83 if ((!out_fp) || (fchmod(fileno(out_fp), sb.st_mode & 0777))
84 || (fchown(fileno(out_fp), sb.st_uid, sb.st_gid))) {
85 fcntl(fileno(fp), F_SETLK, &lock);
86 fclose(fp);
87 fclose(out_fp);
88 return 1;
89 }
90
91 continued = 0;
92 snprintf(username, sizeof username, "%s:", pw->pw_name);
93 rewind(fp);
94 while (!feof(fp)) {
95 fgets(buffer, sizeof buffer, fp);
96 if (!continued) { // Check to see if we're updating this line.
97 if (strncmp(username, buffer, strlen(username)) == 0) { // we have a match.
98 pw_rest = strchr(buffer, ':');
99 *pw_rest++ = '\0';
100 pw_rest = strchr(pw_rest, ':');
101 fprintf(out_fp, "%s:%s%s", buffer, crypt_pw, pw_rest);
102 } else {
103 fputs(buffer, out_fp);
104 }
105 } else {
106 fputs(buffer, out_fp);
107 }
108 if (buffer[strlen(buffer) - 1] == '\n') {
109 continued = 0;
110 } else {
111 continued = 1;
112 }
113 bzero(buffer, sizeof buffer);
114 }
115
116 if (fflush(out_fp) || fsync(fileno(out_fp)) || fclose(out_fp)) {
117 unlink(buf);
118 fcntl(fileno(fp), F_SETLK, &lock);
119 fclose(fp);
120 return 1;
121 }
122 if (rename(buf, filename) < 0) {
123 fcntl(fileno(fp), F_SETLK, &lock);
124 fclose(fp);
125 return 1;
126 } else {
127 fcntl(fileno(fp), F_SETLK, &lock);
128 fclose(fp);
129 return 0;
130 }
131}
132
133
134extern int passwd_main(int argc, char **argv)
135{
136 int amroot;
137 char *cp;
138 char *np;
139 char *name;
140 char *myname;
141 int flag;
142 int algo = 1; /* -a - password algorithm */
143 int lflg = 0; /* -l - lock account */
144 int uflg = 0; /* -u - unlock account */
145 int dflg = 0; /* -d - delete password */
146 const struct passwd *pw;
147
148#ifdef CONFIG_FEATURE_SHADOWPASSWDS
149 const struct spwd *sp;
150#endif /* CONFIG_FEATURE_SHADOWPASSWDS */
151 amroot = (getuid() == 0);
152 openlog("passwd", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
153 while ((flag = getopt(argc, argv, "a:dlu")) != EOF) {
154 switch (flag) {
155 case 'a':
156 algo = get_algo(optarg);
157 break;
158 case 'd':
159 dflg++;
160 break;
161 case 'l':
162 lflg++;
163 break;
164 case 'u':
165 uflg++;
166 break;
167 default:
168 bb_show_usage();
169 }
170 }
171 myname = (char *) bb_xstrdup(my_getpwuid(NULL, getuid(), -1));
172 /* exits on error */
173 if (optind < argc) {
174 name = argv[optind];
175 } else {
176 name = myname;
177 }
178 if ((lflg || uflg || dflg) && (optind >= argc || !amroot)) {
179 bb_show_usage();
180 }
181 pw = getpwnam(name);
182 if (!pw) {
183 bb_error_msg_and_die("Unknown user %s\n", name);
184 }
185 if (!amroot && pw->pw_uid != getuid()) {
186 syslog(LOG_WARNING, "can't change pwd for `%s'", name);
187 bb_error_msg_and_die("Permission denied.\n");
188 }
189#ifdef CONFIG_FEATURE_SHADOWPASSWDS
190 sp = getspnam(name);
191 if (!sp) {
192 sp = (struct spwd *) pwd_to_spwd(pw);
193 }
194 cp = sp->sp_pwdp;
195 np = sp->sp_namp;
196#else
197 cp = pw->pw_passwd;
198 np = name;
199#endif /* CONFIG_FEATURE_SHADOWPASSWDS */
200
201 safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd));
202 if (!(dflg || lflg || uflg)) {
203 if (!amroot) {
204 if (cp[0] == '!') {
205 syslog(LOG_WARNING, "password locked for `%s'", np);
206 bb_error_msg_and_die( "The password for `%s' cannot be changed.\n", np);
207 }
208 }
209 printf("Changing password for %s\n", name);
210 if (new_password(pw, amroot, algo)) {
211 bb_error_msg_and_die( "The password for %s is unchanged.\n", name);
212 }
213 } else if (lflg) {
214 if (crypt_passwd[0] != '!') {
215 memmove(&crypt_passwd[1], crypt_passwd,
216 sizeof crypt_passwd - 1);
217 crypt_passwd[sizeof crypt_passwd - 1] = '\0';
218 crypt_passwd[0] = '!';
219 }
220 } else if (uflg) {
221 if (crypt_passwd[0] == '!') {
222 memmove(crypt_passwd, &crypt_passwd[1],
223 sizeof crypt_passwd - 1);
224 }
225 } else if (dflg) {
226 crypt_passwd[0] = '\0';
227 }
228 set_filesize_limit(30000);
229 signal(SIGHUP, SIG_IGN);
230 signal(SIGINT, SIG_IGN);
231 signal(SIGQUIT, SIG_IGN);
232 umask(077);
233 if (setuid(0)) {
234 syslog(LOG_ERR, "can't setuid(0)");
235 bb_error_msg_and_die( "Cannot change ID to root.\n");
236 }
237 if (!update_passwd(pw, crypt_passwd)) {
238 syslog(LOG_INFO, "password for `%s' changed by user `%s'", name,
239 myname);
240 printf("Password changed.\n");
241 } else {
242 syslog(LOG_WARNING, "an error occurred updating the password file");
243 bb_error_msg_and_die("An error occurred updating the password file.\n");
244 }
245 return (0);
246}
247
248
249
250static int create_backup(const char *backup, FILE * fp)
251{
252 struct stat sb;
253 struct utimbuf ub;
254 FILE *bkfp;
255 int c, mask;
256
257 if (fstat(fileno(fp), &sb))
258 /* return -1; */
259 return 1;
260
261 mask = umask(077);
262 bkfp = fopen(backup, "w");
263 umask(mask);
264 if (!bkfp)
265 /* return -1; */
266 return 1;
267
268 /* TODO: faster copy, not one-char-at-a-time. --marekm */
269 rewind(fp);
270 while ((c = getc(fp)) != EOF) {
271 if (putc(c, bkfp) == EOF)
272 break;
273 }
274 if (c != EOF || fflush(bkfp)) {
275 fclose(bkfp);
276 /* return -1; */
277 return 1;
278 }
279 if (fclose(bkfp))
280 /* return -1; */
281 return 1;
282
283 ub.actime = sb.st_atime;
284 ub.modtime = sb.st_mtime;
285 utime(backup, &ub);
286 return 0;
287}
288
289static int i64c(int i)
290{
291 if (i <= 0)
292 return ('.');
293 if (i == 1)
294 return ('/');
295 if (i >= 2 && i < 12)
296 return ('0' - 2 + i);
297 if (i >= 12 && i < 38)
298 return ('A' - 12 + i);
299 if (i >= 38 && i < 63)
300 return ('a' - 38 + i);
301 return ('z');
302}
303
304static char *crypt_make_salt(void)
305{
306 time_t now;
307 static unsigned long x;
308 static char result[3];
309
310 time(&now);
311 x += now + getpid() + clock();
312 result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077);
313 result[1] = i64c(((x >> 12) ^ x) & 077);
314 result[2] = '\0';
315 return result;
316}
317
318
319static int new_password(const struct passwd *pw, int amroot, int algo)
320{
321 char *clear;
322 char *cipher;
323 char *cp;
324 char orig[200];
325 char pass[200];
326 time_t start, now;
327
328 if (!amroot && crypt_passwd[0]) {
329 if (!(clear = bb_askpass(0, "Old password:"))) {
330 /* return -1; */
331 return 1;
332 }
333 cipher = pw_encrypt(clear, crypt_passwd);
334 if (strcmp(cipher, crypt_passwd) != 0) {
335 syslog(LOG_WARNING, "incorrect password for `%s'",
336 pw->pw_name);
337 time(&start);
338 now = start;
339 while (difftime(now, start) < FAIL_DELAY) {
340 sleep(FAIL_DELAY);
341 time(&now);
342 }
343 fprintf(stderr, "Incorrect password.\n");
344 /* return -1; */
345 return 1;
346 }
347 safe_strncpy(orig, clear, sizeof(orig));
348 bzero(clear, strlen(clear));
349 bzero(cipher, strlen(cipher));
350 } else {
351 orig[0] = '\0';
352 }
353 if (! (cp=bb_askpass(0, "Enter the new password (minimum of 5, maximum of 8 characters)\n"
354 "Please use a combination of upper and lower case letters and numbers.\n"
355 "Enter new password: ")))
356 {
357 bzero(orig, sizeof orig);
358 /* return -1; */
359 return 1;
360 }
361 safe_strncpy(pass, cp, sizeof(pass));
362 bzero(cp, strlen(cp));
363 /* if (!obscure(orig, pass, pw)) { */
364 if (obscure(orig, pass, pw)) {
365 if (amroot) {
366 printf("\nWarning: weak password (continuing).\n");
367 } else {
368 /* return -1; */
369 return 1;
370 }
371 }
372 if (!(cp = bb_askpass(0, "Re-enter new password: "))) {
373 bzero(orig, sizeof orig);
374 /* return -1; */
375 return 1;
376 }
377 if (strcmp(cp, pass)) {
378 fprintf(stderr, "Passwords do not match.\n");
379 /* return -1; */
380 return 1;
381 }
382 bzero(cp, strlen(cp));
383 bzero(orig, sizeof(orig));
384
385 if (algo == 1) {
386 cp = pw_encrypt(pass, "$1$");
387 } else
388 cp = pw_encrypt(pass, crypt_make_salt());
389 bzero(pass, sizeof pass);
390 safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd));
391 return 0;
392}
393
394static void set_filesize_limit(int blocks)
395{
396 struct rlimit rlimit_fsize;
397
398 rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * blocks;
399 setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
400}
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 @@
1/* vi: set sw=4 ts=4: */
2
3#include <fcntl.h>
4#include <signal.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <syslog.h>
9#include <termios.h>
10#include <unistd.h>
11#include <utmp.h>
12#include <sys/resource.h>
13#include <sys/stat.h>
14#include <sys/time.h>
15#include <sys/types.h>
16#include <ctype.h>
17#include <time.h>
18
19#include "busybox.h"
20
21
22
23/* The shell to run if none is given in the user's passwd entry. */
24#define DEFAULT_USER "root"
25
26//#define SYSLOG_SUCCESS
27#define SYSLOG_FAILURE
28
29
30#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
31/* Log the fact that someone has run su */
32
33# if defined( SYSLOG_SUCCESS ) && defined( SYSLOG_FAILURE )
34static void log_su (const char *successful, const char *old_user, const char *tty)
35{
36 syslog ( LOG_NOTICE, "%s%s on %s", successful, old_user, tty);
37}
38# define log_su_successful(cu, u, tty) if(!cu) log_su("", u, tty)
39# define log_su_failure(cu, u, tty) if(!cu) log_su("FAILED SU ", u, tty)
40# else
41 /* partial logging */
42# if !defined( SYSLOG_SUCESS )
43# define log_su_successful(cu, u, tty)
44# define log_su_failure(cu, u, t) if(!cu) \
45 syslog(LOG_NOTICE, "FAILED SU %s on %s", u, t)
46# else
47# define log_su_successful(cu, u, t) if(!cu) \
48 syslog(LOG_NOTICE, "%s on %s", u, t)
49# define log_su_failure(cu, u, tty)
50# endif
51# endif
52#else
53 /* logging not used */
54# define log_su_successful(cu, u, tty)
55# define log_su_failure(cu, u, tty)
56#endif
57
58
59int su_main ( int argc, char **argv )
60{
61 unsigned long flags;
62 int opt_preserve;
63 int opt_loginshell;
64 char *opt_shell = 0;
65 char *opt_command = 0;
66 char *opt_username = DEFAULT_USER;
67 char **opt_args = 0;
68 struct passwd *pw;
69 uid_t cur_uid = getuid();
70
71#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
72 const char *tty;
73 const char *old_user;
74#endif
75
76 flags = bb_getopt_ulflags(argc, argv, "mplc:s:",
77 &opt_command, &opt_shell);
78 opt_preserve = flags & 3;
79 opt_loginshell = (flags & 4 ? 1 : 0);
80
81 if (optind < argc && argv[optind][0] == '-' && argv[optind][1] == 0) {
82 opt_loginshell = 1;
83 ++optind;
84 }
85
86 /* get user if specified */
87 if ( optind < argc )
88 opt_username = argv [optind++];
89
90 if ( optind < argc )
91 opt_args = argv + optind;
92
93#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
94#ifdef CONFIG_FEATURE_U_W_TMP
95 /* The utmp entry (via getlogin) is probably the best way to identify
96 the user, especially if someone su's from a su-shell. */
97 old_user = getlogin ( );
98 if ( !old_user )
99#endif
100 {
101 /* getlogin can fail -- usually due to lack of utmp entry. Resort to getpwuid. */
102 pw = getpwuid ( cur_uid );
103 old_user = ( pw ? pw->pw_name : "" );
104 }
105 tty = ttyname ( 2 );
106 if(!tty)
107 tty = "none";
108
109 openlog ( bb_applet_name, 0, LOG_AUTH );
110#endif
111
112 pw = getpwnam ( opt_username );
113 if ( !pw )
114 bb_error_msg_and_die ( "user %s does not exist", opt_username );
115
116 /* Make sure pw->pw_shell is non-NULL. It may be NULL when NEW_USER
117 is a username that is retrieved via NIS (YP), but that doesn't have
118 a default shell listed. */
119 if ( !pw-> pw_shell || !pw->pw_shell [0] )
120 pw-> pw_shell = (char *) DEFAULT_SHELL;
121
122 if ((( cur_uid == 0 ) || correct_password ( pw ))) {
123 log_su_successful(pw->pw_uid, old_user, tty );
124 } else {
125 log_su_failure (pw->pw_uid, old_user, tty );
126 bb_error_msg_and_die ( "incorrect password" );
127 }
128
129#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
130 closelog();
131#endif
132
133 if ( !opt_shell && opt_preserve )
134 opt_shell = getenv ( "SHELL" );
135
136 if ( opt_shell && cur_uid && restricted_shell ( pw-> pw_shell )) {
137 /* The user being su'd to has a nonstandard shell, and so is
138 probably a uucp account or has restricted access. Don't
139 compromise the account by allowing access with a standard
140 shell. */
141 fputs ( "using restricted shell\n", stderr );
142 opt_shell = 0;
143 }
144
145 if ( !opt_shell )
146 opt_shell = pw->pw_shell;
147
148 change_identity ( pw );
149 setup_environment ( opt_shell, opt_loginshell, !opt_preserve, pw );
150 run_shell ( opt_shell, opt_loginshell, opt_command, (const char**)opt_args
151#ifdef CONFIG_SELINUX
152 , 0
153#endif
154 );
155
156 return EXIT_FAILURE;
157}
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 @@
1/* vi: set sw=4 ts=4: */
2#include <fcntl.h>
3#include <signal.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <syslog.h>
8#include <unistd.h>
9#include <utmp.h>
10#include <sys/resource.h>
11#include <sys/stat.h>
12#include <sys/time.h>
13#include <sys/types.h>
14#include <ctype.h>
15#include <time.h>
16
17#include "busybox.h"
18
19
20// sulogin defines
21#define SULOGIN_PROMPT "\nGive root password for system maintenance\n" \
22 "(or type Control-D for normal startup):"
23
24static const char *forbid[] = {
25 "ENV",
26 "BASH_ENV",
27 "HOME",
28 "IFS",
29 "PATH",
30 "SHELL",
31 "LD_LIBRARY_PATH",
32 "LD_PRELOAD",
33 "LD_TRACE_LOADED_OBJECTS",
34 "LD_BIND_NOW",
35 "LD_AOUT_LIBRARY_PATH",
36 "LD_AOUT_PRELOAD",
37 "LD_NOWARN",
38 "LD_KEEPDIR",
39 (char *) 0
40};
41
42
43
44static void catchalarm(int junk)
45{
46 exit(EXIT_FAILURE);
47}
48
49
50extern int sulogin_main(int argc, char **argv)
51{
52 char *cp;
53 char *device = (char *) 0;
54 const char *name = "root";
55 int timeout = 0;
56 static char pass[BUFSIZ];
57 struct passwd pwent;
58 struct passwd *pwd;
59 time_t start, now;
60 const char **p;
61#ifdef CONFIG_FEATURE_SHADOWPASSWDS
62 struct spwd *spwd = NULL;
63#endif /* CONFIG_FEATURE_SHADOWPASSWDS */
64
65 openlog("sulogin", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
66 if (argc > 1) {
67 if (strncmp(argv[1], "-t", 2) == 0) {
68 if (strcmp(argv[1], "-t") == 0) {
69 if (argc > 2) {
70 timeout = atoi(argv[2]);
71 if (argc > 3) {
72 device = argv[3];
73 }
74 }
75 } else {
76 if (argc > 2) {
77 device = argv[2];
78 }
79 }
80 } else {
81 device = argv[1];
82 }
83 if (device) {
84 close(0);
85 close(1);
86 close(2);
87 if (open(device, O_RDWR) >= 0) {
88 dup(0);
89 dup(0);
90 } else {
91 syslog(LOG_WARNING, "cannot open %s\n", device);
92 exit(EXIT_FAILURE);
93 }
94 }
95 }
96 if (access(bb_path_passwd_file, 0) == -1) {
97 syslog(LOG_WARNING, "No password file\n");
98 bb_error_msg_and_die("No password file\n");
99 }
100 if (!isatty(0) || !isatty(1) || !isatty(2)) {
101 exit(EXIT_FAILURE);
102 }
103
104
105 /* Clear out anything dangerous from the environment */
106 for (p = forbid; *p; p++)
107 unsetenv(*p);
108
109
110 signal(SIGALRM, catchalarm);
111 if (!(pwd = getpwnam(name))) {
112 syslog(LOG_WARNING, "No password entry for `root'\n");
113 bb_error_msg_and_die("No password entry for `root'\n");
114 }
115 pwent = *pwd;
116#ifdef CONFIG_FEATURE_SHADOWPASSWDS
117 spwd = NULL;
118 if (pwd && ((strcmp(pwd->pw_passwd, "x") == 0)
119 || (strcmp(pwd->pw_passwd, "*") == 0))) {
120 endspent();
121 spwd = getspnam(name);
122 if (spwd) {
123 pwent.pw_passwd = spwd->sp_pwdp;
124 }
125 }
126#endif /* CONFIG_FEATURE_SHADOWPASSWDS */
127 while (1) {
128 cp = bb_askpass(timeout, SULOGIN_PROMPT);
129 if (!cp || !*cp) {
130 puts("\n");
131 fflush(stdout);
132 syslog(LOG_INFO, "Normal startup\n");
133 exit(EXIT_SUCCESS);
134 } else {
135 safe_strncpy(pass, cp, sizeof(pass));
136 bzero(cp, strlen(cp));
137 }
138 if (strcmp(pw_encrypt(pass, pwent.pw_passwd), pwent.pw_passwd) == 0) {
139 break;
140 }
141 time(&start);
142 now = start;
143 while (difftime(now, start) < FAIL_DELAY) {
144 sleep(FAIL_DELAY);
145 time(&now);
146 }
147 puts("Login incorrect");
148 fflush(stdout);
149 syslog(LOG_WARNING, "Incorrect root password\n");
150 }
151 bzero(pass, strlen(pass));
152 signal(SIGALRM, SIG_DFL);
153 puts("Entering System Maintenance Mode\n");
154 fflush(stdout);
155 syslog(LOG_INFO, "System Maintenance Mode\n");
156 run_shell(pwent.pw_shell, 1, 0, 0);
157 return (0);
158}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * vlock implementation for busybox
4 *
5 * Copyright (C) 2000 by spoon <spoon@ix.netcom.com>
6 * Written by spoon <spon@ix.netcom.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24/* Shoutz to Michael K. Johnson <johnsonm@redhat.com>, author of the
25 * original vlock. I snagged a bunch of his code to write this
26 * minimalistic vlock.
27 */
28/* Fixed by Erik Andersen to do passwords the tinylogin way...
29 * It now works with md5, sha1, etc passwords. */
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <sys/vt.h>
34#include <signal.h>
35#include <string.h>
36#include <unistd.h>
37#include <fcntl.h>
38#include <errno.h>
39#include <sys/ioctl.h>
40#include <termios.h>
41
42#include "busybox.h"
43
44static struct passwd *pw;
45static struct vt_mode ovtm;
46static struct termios oterm;
47static int vfd;
48static int o_lock_all = 0;
49
50#ifdef CONFIG_FEATURE_SHADOWPASSWDS
51static struct spwd *spw;
52
53/* getspuid - get a shadow entry by uid */
54struct spwd *getspuid(uid_t uid)
55{
56 struct spwd *sp;
57 struct passwd *mypw;
58
59 if ((mypw = getpwuid(getuid())) == NULL) {
60 return (NULL);
61 }
62 setspent();
63 while ((sp = getspent()) != NULL) {
64 if (strcmp(mypw->pw_name, sp->sp_namp) == 0)
65 break;
66 }
67 endspent();
68 return (sp);
69}
70#endif
71
72static void release_vt(int signo)
73{
74 if (!o_lock_all)
75 ioctl(vfd, VT_RELDISP, 1);
76 else
77 ioctl(vfd, VT_RELDISP, 0);
78}
79
80static void acquire_vt(int signo)
81{
82 ioctl(vfd, VT_RELDISP, VT_ACKACQ);
83}
84
85static void restore_terminal(void)
86{
87 ioctl(vfd, VT_SETMODE, &ovtm);
88 tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
89}
90
91extern int vlock_main(int argc, char **argv)
92{
93 sigset_t sig;
94 struct sigaction sa;
95 struct vt_mode vtm;
96 int times = 0;
97 struct termios term;
98
99 if (argc > 2) {
100 bb_show_usage();
101 }
102
103 if (argc == 2) {
104 if (strncmp(argv[1], "-a", 2)) {
105 bb_show_usage();
106 } else {
107 o_lock_all = 1;
108 }
109 }
110
111 if ((pw = getpwuid(getuid())) == NULL) {
112 bb_error_msg_and_die("no password for uid %d\n", getuid());
113 }
114#ifdef CONFIG_FEATURE_SHADOWPASSWDS
115 if ((strcmp(pw->pw_passwd, "x") == 0)
116 || (strcmp(pw->pw_passwd, "*") == 0)) {
117
118 if ((spw = getspuid(getuid())) == NULL) {
119 bb_error_msg_and_die("could not read shadow password for uid %d: %s\n",
120 getuid(), strerror(errno));
121 }
122 if (spw->sp_pwdp) {
123 pw->pw_passwd = spw->sp_pwdp;
124 }
125 }
126#endif /* CONFIG_FEATURE_SHADOWPASSWDS */
127 if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*') {
128 bb_error_msg_and_die("Account disabled for uid %d\n", getuid());
129 }
130
131 /* we no longer need root privs */
132 setuid(getuid());
133 setgid(getgid());
134
135 if ((vfd = open("/dev/tty", O_RDWR)) < 0) {
136 bb_error_msg_and_die("/dev/tty");
137 };
138
139 if (ioctl(vfd, VT_GETMODE, &vtm) < 0) {
140 bb_error_msg_and_die("/dev/tty");
141 };
142
143 /* mask a bunch of signals */
144 sigprocmask(SIG_SETMASK, NULL, &sig);
145 sigdelset(&sig, SIGUSR1);
146 sigdelset(&sig, SIGUSR2);
147 sigaddset(&sig, SIGTSTP);
148 sigaddset(&sig, SIGTTIN);
149 sigaddset(&sig, SIGTTOU);
150 sigaddset(&sig, SIGHUP);
151 sigaddset(&sig, SIGCHLD);
152 sigaddset(&sig, SIGQUIT);
153 sigaddset(&sig, SIGINT);
154
155 sigemptyset(&(sa.sa_mask));
156 sa.sa_flags = SA_RESTART;
157 sa.sa_handler = release_vt;
158 sigaction(SIGUSR1, &sa, NULL);
159 sa.sa_handler = acquire_vt;
160 sigaction(SIGUSR2, &sa, NULL);
161
162 /* need to handle some signals so that we don't get killed by them */
163 sa.sa_handler = SIG_IGN;
164 sigaction(SIGHUP, &sa, NULL);
165 sigaction(SIGQUIT, &sa, NULL);
166 sigaction(SIGINT, &sa, NULL);
167 sigaction(SIGTSTP, &sa, NULL);
168
169 ovtm = vtm;
170 vtm.mode = VT_PROCESS;
171 vtm.relsig = SIGUSR1;
172 vtm.acqsig = SIGUSR2;
173 ioctl(vfd, VT_SETMODE, &vtm);
174
175 tcgetattr(STDIN_FILENO, &oterm);
176 term = oterm;
177 term.c_iflag &= ~BRKINT;
178 term.c_iflag |= IGNBRK;
179 term.c_lflag &= ~ISIG;
180 term.c_lflag &= ~(ECHO | ECHOCTL);
181 tcsetattr(STDIN_FILENO, TCSANOW, &term);
182
183 do {
184 char *pass, *crypt_pass;
185 char prompt[100];
186
187 if (o_lock_all) {
188 printf("All Virtual Consoles locked.\n");
189 } else {
190 printf("This Virtual Console locked.\n");
191 }
192 fflush(stdout);
193
194 snprintf(prompt, 100, "%s's password: ", pw->pw_name);
195
196 if ((pass = bb_askpass(0, prompt)) == NULL) {
197 restore_terminal();
198 bb_perror_msg_and_die("password");
199 }
200
201 crypt_pass = pw_encrypt(pass, pw->pw_passwd);
202 if (strncmp(crypt_pass, pw->pw_passwd, sizeof(crypt_pass)) == 0) {
203 memset(pass, 0, strlen(pass));
204 memset(crypt_pass, 0, strlen(crypt_pass));
205 restore_terminal();
206 return 0;
207 }
208 memset(pass, 0, strlen(pass));
209 memset(crypt_pass, 0, strlen(crypt_pass));
210
211 if (isatty(STDIN_FILENO) == 0) {
212 restore_terminal();
213 bb_perror_msg_and_die("isatty");
214 }
215
216 sleep(++times);
217 printf("Password incorrect.\n");
218 if (times >= 3) {
219 sleep(15);
220 times = 2;
221 }
222 } while (1);
223}
224
225/*
226Local Variables:
227c-file-style: "linux"
228c-basic-offset: 4
229tab-width: 4
230End:
231*/
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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Miscellaneous Utilities"
7
8config CONFIG_ADJTIMEX
9 bool "adjtimex"
10 default n
11 help
12 Adjtimex reads and optionally sets adjustment parameters for
13 the Linux clock adjustment algorithm.
14
15config CONFIG_CROND
16 bool "crond"
17 default n
18 select CONFIG_FEATURE_SUID
19 help
20 Crond is a background daemon that parses individual crontab
21 files and executes commands on behalf of the users in question.
22 This is a port of dcron from slackware. It uses files of the
23 format /var/spool/cron/crontabs/<username> files, for example:
24 $ cat /var/spool/cron/crontabs/root
25 # Run daily cron jobs at 4:40 every day:
26 40 4 * * * /etc/cron/daily > /dev/null 2>&1
27 Note that Busybox binary must be setuid root for this applet to
28 work properly.
29
30config CONFIG_FEATURE_CROND_CALL_SENDMAIL
31 bool " Using /usr/sbin/sendmail?"
32 default n
33 depends on CONFIG_CROND
34 help
35 Support calling /usr/sbin/sendmail for send cmd outputs.
36
37config CONFIG_CRONTAB
38 bool "crontab"
39 default n
40 help
41 Crontab manipulates the crontab for a particular user. Only
42 the superuser may specify a different user and/or crontab directory.
43
44config CONFIG_DC
45 bool "dc"
46 default n
47 help
48 Dc is a reverse-polish desk calculator which supports unlimited
49 precision arithmetic.
50
51config CONFIG_DEVFSD
52 bool "devfsd"
53 default n
54 help
55 Provides compatibility with old device names on a devfs systems.
56 You should set it to true if you have devfs enabled.
57 The following keywords in devsfd.conf are supported:
58 "CLEAR_CONFIG", "INCLUDE", "OPTIONAL_INCLUDE", "RESTORE",
59 "PERMISSIONS", "EXECUTE", "COPY", "IGNORE",
60 "MKOLDCOMPAT", "MKNEWCOMPAT","RMOLDCOMPAT", "RMNEWCOMPAT".
61
62 But only if they are written UPPERCASE!!!!!!!!
63
64config CONFIG_DEVFSD_MODLOAD
65 bool "Adds support for MODLOAD keyword in devsfd.conf"
66 default n
67 depends on CONFIG_DEVFSD
68 help
69 This actually doesn't work with busybox modutils but needs the real modutils.
70
71config CONFIG_DEVFSD_FG_NP
72 bool "Enables the -fg and -np options"
73 default n
74 depends on CONFIG_DEVFSD
75 help
76 -fg Run the daemon in the foreground.
77 -np Exit after parsing the configuration file. Do not poll for events.
78
79config CONFIG_DEVFSD_VERBOSE
80 bool "Increases logging (and size)"
81 default n
82 depends on CONFIG_DEVFSD
83 help
84 Increases logging to stderr or syslog.
85
86config CONFIG_LAST
87 bool "last"
88 default n
89 select CONFIG_FEATURE_U_W_TMP
90 help
91 'last' displays a list of the last users that logged into the system.
92
93config CONFIG_HDPARM
94 bool "hdparm"
95 default n
96 help
97 Get/Set hard drive parameters. Primarily intended for ATA
98 drives. Adds about 13k (or around 30k if you enable the
99 CONFIG_FEATURE_HDPARM_GET_IDENTITY option)....
100
101config CONFIG_FEATURE_HDPARM_GET_IDENTITY
102 bool " Support obtaining detailed information directly from drives"
103 default y
104 depends on CONFIG_HDPARM
105 help
106 Enables the -I and -Istdin options to obtain detailed information
107 directly from drives about their capabilities and supported ATA
108 feature set. Enabling this option will add about 16k...
109
110config CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF
111 bool " Register an IDE interface (DANGEROUS)"
112 default n
113 depends on CONFIG_HDPARM
114 help
115 Enables the 'hdparm -R' option to register an IDE interface.
116 This is dangerous stuff, so you should probably say N.
117
118config CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
119 bool " Un-register an IDE interface (DANGEROUS)"
120 default n
121 depends on CONFIG_HDPARM
122 help
123 Enables the 'hdparm -U' option to un-register an IDE interface.
124 This is dangerous stuff, so you should probably say N.
125
126config CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET
127 bool " perform device reset (DANGEROUS)"
128 default n
129 depends on CONFIG_HDPARM
130 help
131 Enables the 'hdparm -w' option to perform a device reset.
132 This is dangerous stuff, so you should probably say N.
133
134config CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
135 bool " tristate device for hotswap (DANGEROUS)"
136 default n
137 depends on CONFIG_HDPARM
138 help
139 Enables the 'hdparm -x' option to tristate device for hotswap,
140 and the '-b' option to get/set bus state. This is dangerous
141 stuff, so you should probably say N.
142
143config CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA
144 bool " get/set using_dma flag (DANGEROUS)"
145 default n
146 depends on CONFIG_HDPARM
147 help
148 Enables the 'hdparm -d' option to get/set using_dma flag.
149 This is dangerous stuff, so you should probably say N.
150
151config CONFIG_MAKEDEVS
152 bool "makedevs"
153 default n
154 help
155 'makedevs' is a utility used and created by the Linux Router Project.
156 It creates a large number of device special files (/dev devices)
157 rather quickly, and can be considerably faster then running mknod a
158 zillion times.
159
160config CONFIG_MT
161 bool "mt"
162 default n
163 help
164 mt is used to control tape devices. You can use the mt utility
165 to advance or rewind a tape past a specified number of archive
166 files on the tape.
167
168config CONFIG_RX
169 bool "rx"
170 default n
171 help
172 Receive files using the Xmodem protocol.
173
174config CONFIG_STRINGS
175 bool "strings"
176 default n
177 help
178 strings prints the printable character sequences for each file
179 specified.
180
181config CONFIG_TIME
182 bool "time"
183 default n
184 help
185 The time command runs the specified program with the given arguments.
186 When the command finishes, time writes a message to standard output
187 giving timing statistics about this program run.
188
189config CONFIG_WATCHDOG
190 bool "watchdog"
191 default n
192 help
193 The watchdog utility is used with hardware or software watchdog
194 device drivers. It opens the specified watchdog device special file
195 and periodically writes a magic character to the device. If the
196 watchdog applet ever fails to write the magic character within a
197 certain amount of time, the watchdog device assumes the system has
198 hung, and will cause the hardware to reboot.
199
200endmenu
201
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/miscutils
23MISCUTILS_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20MISCUTILS_AR:=miscutils.a
21ifndef $(MISCUTILS_DIR)
22MISCUTILS_DIR:=$(top_builddir)/miscutils/
23endif
24srcdir=$(top_srcdir)/miscutils
25
26MISCUTILS-y:=
27MISCUTILS-$(CONFIG_ADJTIMEX) += adjtimex.o
28MISCUTILS-$(CONFIG_CROND) += crond.o
29MISCUTILS-$(CONFIG_CRONTAB) += crontab.o
30MISCUTILS-$(CONFIG_DC) += dc.o
31MISCUTILS-$(CONFIG_DEVFSD) += devfsd.o
32MISCUTILS-$(CONFIG_HDPARM) += hdparm.o
33MISCUTILS-$(CONFIG_LAST) += last.o
34MISCUTILS-$(CONFIG_MAKEDEVS) += makedevs.o
35MISCUTILS-$(CONFIG_MT) += mt.o
36MISCUTILS-$(CONFIG_RX) += rx.o
37MISCUTILS-$(CONFIG_STRINGS) += strings.o
38MISCUTILS-$(CONFIG_TIME) += time.o
39MISCUTILS-$(CONFIG_WATCHDOG) += watchdog.o
40
41libraries-y+=$(MISCUTILS_DIR)$(MISCUTILS_AR)
42
43needlibm-y:=
44needlibm-$(CONFIG_DC) := y
45
46ifeq ($(needlibm-y),y)
47 LIBRARIES += -lm
48endif
49
50$(MISCUTILS_DIR)$(MISCUTILS_AR): $(patsubst %,$(MISCUTILS_DIR)%, $(MISCUTILS-y))
51 $(AR) -ro $@ $(patsubst %,$(MISCUTILS_DIR)%, $(MISCUTILS-y))
52
53$(MISCUTILS_DIR)%.o: $(srcdir)/%.c
54 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
55
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 @@
1/*
2 * adjtimex.c - read, and possibly modify, the Linux kernel `timex' variables.
3 *
4 * Originally written: October 1997
5 * Last hack: March 2001
6 * Copyright 1997, 2000, 2001 Larry Doolittle <LRDoolittle@lbl.gov>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License (Version 2,
10 * June 1991) as published by the Free Software Foundation. At the
11 * time of writing, that license was published by the FSF with the URL
12 * http://www.gnu.org/copyleft/gpl.html, and is incorporated herein by
13 * reference.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * This adjtimex(1) is very similar in intent to adjtimex(8) by Steven
21 * Dick <ssd@nevets.oau.org> and Jim Van Zandt <jrv@vanzandt.mv.com>
22 * (see http://metalab.unc.edu/pub/Linux/system/admin/time/adjtimex*).
23 * That version predates this one, and is _much_ bigger and more
24 * featureful. My independently written version was very similar to
25 * Steven's from the start, because they both follow the kernel timex
26 * structure. I further tweaked this version to be equivalent to Steven's
27 * where possible, but I don't like getopt_long, so the actual usage
28 * syntax is incompatible.
29 *
30 * Amazingly enough, my Red Hat 5.2 sys/timex (and sub-includes)
31 * don't actually give a prototype for adjtimex(2), so building
32 * this code (with -Wall) gives a warning. Later versions of
33 * glibc fix this issue.
34 *
35 * This program is too simple for a Makefile, just build with:
36 * gcc -Wall -O adjtimex.c -o adjtimex
37 *
38 * busyboxed 20 March 2001, Larry Doolittle <ldoolitt@recycle.lbl.gov>
39 * It will autosense if it is built in a busybox environment, based
40 * on the BB_VER preprocessor macro.
41 */
42
43#include <stdio.h>
44#include <sys/types.h>
45#include <stdlib.h>
46#include <unistd.h>
47#include <sys/timex.h>
48#include "busybox.h"
49
50static struct {int bit; char *name;} statlist[] = {
51 { STA_PLL, "PLL" },
52 { STA_PPSFREQ, "PPSFREQ" },
53 { STA_PPSTIME, "PPSTIME" },
54 { STA_FLL, "FFL" },
55 { STA_INS, "INS" },
56 { STA_DEL, "DEL" },
57 { STA_UNSYNC, "UNSYNC" },
58 { STA_FREQHOLD, "FREQHOLD" },
59 { STA_PPSSIGNAL, "PPSSIGNAL" },
60 { STA_PPSJITTER, "PPSJITTER" },
61 { STA_PPSWANDER, "PPSWANDER" },
62 { STA_PPSERROR, "PPSERROR" },
63 { STA_CLOCKERR, "CLOCKERR" },
64 { 0, NULL } };
65
66static char *ret_code_descript[] = {
67 "clock synchronized",
68 "insert leap second",
69 "delete leap second",
70 "leap second in progress",
71 "leap second has occurred",
72 "clock not synchronized" };
73
74#ifdef BB_VER
75#define main adjtimex_main
76#else
77void usage(char *prog)
78{
79 fprintf(stderr,
80 "Usage: %s [ -q ] [ -o offset ] [ -f frequency ] [ -p timeconstant ] [ -t tick ]\n",
81 prog);
82}
83#define bb_show_usage() usage(argv[0])
84#endif
85
86int main(int argc, char ** argv)
87{
88 struct timex txc;
89 int quiet=0;
90 int c, i, ret, sep;
91 char *descript;
92 txc.modes=0;
93 for (;;) {
94 c = getopt( argc, argv, "qo:f:p:t:");
95 if (c == EOF) break;
96 switch (c) {
97 case 'q':
98 quiet=1;
99 break;
100 case 'o':
101 txc.offset = atoi(optarg);
102 txc.modes |= ADJ_OFFSET_SINGLESHOT;
103 break;
104 case 'f':
105 txc.freq = atoi(optarg);
106 txc.modes |= ADJ_FREQUENCY;
107 break;
108 case 'p':
109 txc.constant = atoi(optarg);
110 txc.modes |= ADJ_TIMECONST;
111 break;
112 case 't':
113 txc.tick = atoi(optarg);
114 txc.modes |= ADJ_TICK;
115 break;
116 default:
117 bb_show_usage();
118 exit(1);
119 }
120 }
121 if (argc != optind) { /* no valid non-option parameters */
122 bb_show_usage();
123 exit(1);
124 }
125
126 ret = adjtimex(&txc);
127
128 if (ret < 0) perror("adjtimex");
129
130 if (!quiet && ret>=0) {
131 printf(
132 " mode: %d\n"
133 "-o offset: %ld\n"
134 "-f frequency: %ld\n"
135 " maxerror: %ld\n"
136 " esterror: %ld\n"
137 " status: %d ( ",
138 txc.modes, txc.offset, txc.freq, txc.maxerror,
139 txc.esterror, txc.status);
140
141 /* representative output of next code fragment:
142 "PLL | PPSTIME" */
143 sep=0;
144 for (i=0; statlist[i].name; i++) {
145 if (txc.status & statlist[i].bit) {
146 if (sep) fputs(" | ",stdout);
147 fputs(statlist[i].name,stdout);
148 sep=1;
149 }
150 }
151
152 descript = "error";
153 if (ret >= 0 && ret <= 5) descript = ret_code_descript[ret];
154 printf(" )\n"
155 "-p timeconstant: %ld\n"
156 " precision: %ld\n"
157 " tolerance: %ld\n"
158 "-t tick: %ld\n"
159 " time.tv_sec: %ld\n"
160 " time.tv_usec: %ld\n"
161 " return value: %d (%s)\n",
162 txc.constant,
163 txc.precision, txc.tolerance, txc.tick,
164 (long)txc.time.tv_sec, (long)txc.time.tv_usec, ret, descript);
165 }
166 return (ret<0);
167}
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 @@
1/*
2 * crond -d[#] -c <crondir> -f -b
3 *
4 * run as root, but NOT setuid root
5 *
6 * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
7 * May be distributed under the GNU General Public License
8 *
9 * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002 to be used in busybox
10 */
11
12#define VERSION "2.3.2"
13
14#undef FEATURE_DEBUG_OPT
15
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <stdarg.h>
20#include <string.h>
21#include <errno.h>
22#include <time.h>
23#include <dirent.h>
24#include <fcntl.h>
25#include <unistd.h>
26#include <syslog.h>
27#include <signal.h>
28#include <getopt.h>
29#include <sys/ioctl.h>
30#include <sys/wait.h>
31#include <sys/stat.h>
32#include <sys/resource.h>
33
34#include "busybox.h"
35
36#define arysize(ary) (sizeof(ary)/sizeof((ary)[0]))
37
38#ifndef CRONTABS
39#define CRONTABS "/var/spool/cron/crontabs"
40#endif
41#ifndef TMPDIR
42#define TMPDIR "/var/spool/cron"
43#endif
44#ifndef SENDMAIL
45#define SENDMAIL "/usr/sbin/sendmail"
46#endif
47#ifndef SENDMAIL_ARGS
48#define SENDMAIL_ARGS "-ti", "oem"
49#endif
50#ifndef CRONUPDATE
51#define CRONUPDATE "cron.update"
52#endif
53#ifndef MAXLINES
54#define MAXLINES 256 /* max lines in non-root crontabs */
55#endif
56
57typedef struct CronFile {
58 struct CronFile *cf_Next;
59 struct CronLine *cf_LineBase;
60 char *cf_User; /* username */
61 int cf_Ready; /* bool: one or more jobs ready */
62 int cf_Running; /* bool: one or more jobs running */
63 int cf_Deleted; /* marked for deletion, ignore */
64} CronFile;
65
66typedef struct CronLine {
67 struct CronLine *cl_Next;
68 char *cl_Shell; /* shell command */
69 pid_t cl_Pid; /* running pid, 0, or armed (-1) */
70 int cl_MailFlag; /* running pid is for mail */
71 int cl_MailPos; /* 'empty file' size */
72 char cl_Mins[60]; /* 0-59 */
73 char cl_Hrs[24]; /* 0-23 */
74 char cl_Days[32]; /* 1-31 */
75 char cl_Mons[12]; /* 0-11 */
76 char cl_Dow[7]; /* 0-6, beginning sunday */
77} CronLine;
78
79#define RUN_RANOUT 1
80#define RUN_RUNNING 2
81#define RUN_FAILED 3
82
83#define DaemonUid 0
84
85#ifdef FEATURE_DEBUG_OPT
86static short DebugOpt;
87#endif
88
89static short LogLevel = 8;
90static const char *LogFile;
91static const char *CDir = CRONTABS;
92
93static void startlogger(void);
94
95static void CheckUpdates(void);
96static void SynchronizeDir(void);
97static int TestJobs(time_t t1, time_t t2);
98static void RunJobs(void);
99static int CheckJobs(void);
100
101static void RunJob(const char *user, CronLine * line);
102
103#ifdef CONFIG_FEATURE_CROND_CALL_SENDMAIL
104static void EndJob(const char *user, CronLine * line);
105#else
106#define EndJob(user, line) line->cl_Pid = 0
107#endif
108
109static void DeleteFile(const char *userName);
110
111static CronFile *FileBase;
112
113
114static void crondlog(const char *ctl, ...)
115{
116 va_list va;
117 const char *fmt;
118 int level = (int) (ctl[0] & 0xf);
119 int type = level == 20 ?
120 LOG_ERR : ((ctl[0] & 0100) ? LOG_WARNING : LOG_NOTICE);
121
122
123 va_start(va, ctl);
124 fmt = ctl + 1;
125 if (level >= LogLevel) {
126
127#ifdef FEATURE_DEBUG_OPT
128 if (DebugOpt) {
129 vfprintf(stderr, fmt, va);
130 } else
131#endif
132 if (LogFile == 0) {
133 vsyslog(type, fmt, va);
134 } else {
135 int logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 600);
136 if (logfd >= 0) {
137 vdprintf(logfd, fmt, va);
138 close(logfd);
139#ifdef FEATURE_DEBUG_OPT
140 } else {
141 bb_perror_msg("Can't open log file");
142#endif
143 }
144 }
145 }
146 va_end(va);
147 if (ctl[0] & 0200) {
148 exit(20);
149 }
150}
151
152int crond_main(int ac, char **av)
153{
154 unsigned long opt;
155 char *lopt, *Lopt, *copt;
156
157#ifdef FEATURE_DEBUG_OPT
158 char *dopt;
159
160 bb_opt_complementaly = "f-b:b-f:S-L:L-S:d-l";
161#else
162 bb_opt_complementaly = "f-b:b-f:S-L:L-S";
163#endif
164
165 opterr = 0; /* disable getopt 'errors' message. */
166 opt = bb_getopt_ulflags(ac, av, "l:L:fbSc:"
167#ifdef FEATURE_DEBUG_OPT
168 "d:"
169#endif
170 , &lopt, &Lopt, &copt
171#ifdef FEATURE_DEBUG_OPT
172 , &dopt
173#endif
174 );
175 if (opt & 1) {
176 LogLevel = atoi(lopt);
177 }
178 if (opt & 2) {
179 if (*Lopt != 0) {
180 LogFile = Lopt;
181 }
182 }
183 if (opt & 32) {
184 if (*copt != 0) {
185 CDir = copt;
186 }
187 }
188#ifdef FEATURE_DEBUG_OPT
189 if (opt & 64) {
190 DebugOpt = atoi(dopt);
191 LogLevel = 0;
192 }
193#endif
194
195 /*
196 * change directory
197 */
198
199 if (chdir(CDir) != 0) {
200 bb_perror_msg_and_die("%s", CDir);
201 }
202 signal(SIGHUP, SIG_IGN); /* hmm.. but, if kill -HUP original
203 * version - his died. ;(
204 */
205 /*
206 * close stdin and stdout, stderr.
207 * close unused descriptors - don't need.
208 * optional detach from controlling terminal
209 */
210
211 if (!(opt & 4)) {
212#if defined(__uClinux__)
213 /* reexec for vfork() do continue parent */
214 vfork_daemon_rexec(1, 0, ac, av, "-f");
215#else /* uClinux */
216 if (daemon(1, 0) < 0) {
217 bb_perror_msg_and_die("daemon");
218 }
219#endif /* uClinux */
220 }
221
222 (void) startlogger(); /* need if syslog mode selected */
223
224 /*
225 * main loop - synchronize to 1 second after the minute, minimum sleep
226 * of 1 second.
227 */
228
229 crondlog("\011%s " VERSION " dillon, started, log level %d\n",
230 bb_applet_name, LogLevel);
231
232 SynchronizeDir();
233
234 {
235 time_t t1 = time(NULL);
236 time_t t2;
237 long dt;
238 short rescan = 60;
239 short sleep_time = 60;
240
241 for (;;) {
242 sleep((sleep_time + 1) - (short) (time(NULL) % sleep_time));
243
244 t2 = time(NULL);
245 dt = t2 - t1;
246
247 /*
248 * The file 'cron.update' is checked to determine new cron
249 * jobs. The directory is rescanned once an hour to deal
250 * with any screwups.
251 *
252 * check for disparity. Disparities over an hour either way
253 * result in resynchronization. A reverse-indexed disparity
254 * less then an hour causes us to effectively sleep until we
255 * match the original time (i.e. no re-execution of jobs that
256 * have just been run). A forward-indexed disparity less then
257 * an hour causes intermediate jobs to be run, but only once
258 * in the worst case.
259 *
260 * when running jobs, the inequality used is greater but not
261 * equal to t1, and less then or equal to t2.
262 */
263
264 if (--rescan == 0) {
265 rescan = 60;
266 SynchronizeDir();
267 }
268 CheckUpdates();
269#ifdef FEATURE_DEBUG_OPT
270 if (DebugOpt)
271 crondlog("\005Wakeup dt=%d\n", dt);
272#endif
273 if (dt < -60 * 60 || dt > 60 * 60) {
274 t1 = t2;
275 crondlog("\111time disparity of %d minutes detected\n", dt / 60);
276 } else if (dt > 0) {
277 TestJobs(t1, t2);
278 RunJobs();
279 sleep(5);
280 if (CheckJobs() > 0) {
281 sleep_time = 10;
282 } else {
283 sleep_time = 60;
284 }
285 t1 = t2;
286 }
287 }
288 }
289 /* not reached */
290}
291
292#if defined(FEATURE_DEBUG_OPT) || defined(CONFIG_FEATURE_CROND_CALL_SENDMAIL)
293/*
294 write to temp file..
295*/
296static void fdprintf(int fd, const char *ctl, ...)
297{
298 va_list va;
299
300 va_start(va, ctl);
301 vdprintf(fd, ctl, va);
302 va_end(va);
303}
304#endif
305
306
307static int ChangeUser(const char *user)
308{
309 struct passwd *pas;
310 const char *err_msg;
311
312 /*
313 * Obtain password entry and change privileges
314 */
315 pas = getpwnam(user);
316 if (pas == 0) {
317 crondlog("\011failed to get uid for %s", user);
318 return (-1);
319 }
320 setenv("USER", pas->pw_name, 1);
321 setenv("HOME", pas->pw_dir, 1);
322 setenv("SHELL", DEFAULT_SHELL, 1);
323
324 /*
325 * Change running state to the user in question
326 */
327 err_msg = change_identity_e2str(pas);
328 if (err_msg) {
329 crondlog("\011%s for user %s", err_msg, user);
330 return (-1);
331 }
332 if (chdir(pas->pw_dir) < 0) {
333 crondlog("\011chdir failed: %s: %m", pas->pw_dir);
334 if (chdir(TMPDIR) < 0) {
335 crondlog("\011chdir failed: %s: %m", TMPDIR);
336 return (-1);
337 }
338 }
339 return (pas->pw_uid);
340}
341
342static void startlogger(void)
343{
344 if (LogFile == 0) {
345 openlog(bb_applet_name, LOG_CONS | LOG_PID, LOG_CRON);
346 }
347#ifdef FEATURE_DEBUG_OPT
348 else { /* test logfile */
349 int logfd;
350
351 if ((logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 600)) >= 0) {
352 close(logfd);
353 } else {
354 bb_perror_msg("Failed to open log file '%s' reason", LogFile);
355 }
356 }
357#endif
358}
359
360
361static const char *const DowAry[] = {
362 "sun",
363 "mon",
364 "tue",
365 "wed",
366 "thu",
367 "fri",
368 "sat",
369
370 "Sun",
371 "Mon",
372 "Tue",
373 "Wed",
374 "Thu",
375 "Fri",
376 "Sat",
377 NULL
378};
379
380static const char *const MonAry[] = {
381 "jan",
382 "feb",
383 "mar",
384 "apr",
385 "may",
386 "jun",
387 "jul",
388 "aug",
389 "sep",
390 "oct",
391 "nov",
392 "dec",
393
394 "Jan",
395 "Feb",
396 "Mar",
397 "Apr",
398 "May",
399 "Jun",
400 "Jul",
401 "Aug",
402 "Sep",
403 "Oct",
404 "Nov",
405 "Dec",
406 NULL
407};
408
409static char *ParseField(char *user, char *ary, int modvalue, int off,
410 const char *const *names, char *ptr)
411{
412 char *base = ptr;
413 int n1 = -1;
414 int n2 = -1;
415
416 if (base == NULL) {
417 return (NULL);
418 }
419
420 while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
421 int skip = 0;
422
423 /* Handle numeric digit or symbol or '*' */
424
425 if (*ptr == '*') {
426 n1 = 0; /* everything will be filled */
427 n2 = modvalue - 1;
428 skip = 1;
429 ++ptr;
430 } else if (*ptr >= '0' && *ptr <= '9') {
431 if (n1 < 0) {
432 n1 = strtol(ptr, &ptr, 10) + off;
433 } else {
434 n2 = strtol(ptr, &ptr, 10) + off;
435 }
436 skip = 1;
437 } else if (names) {
438 int i;
439
440 for (i = 0; names[i]; ++i) {
441 if (strncmp(ptr, names[i], strlen(names[i])) == 0) {
442 break;
443 }
444 }
445 if (names[i]) {
446 ptr += strlen(names[i]);
447 if (n1 < 0) {
448 n1 = i;
449 } else {
450 n2 = i;
451 }
452 skip = 1;
453 }
454 }
455
456 /* handle optional range '-' */
457
458 if (skip == 0) {
459 crondlog("\111failed user %s parsing %s\n", user, base);
460 return (NULL);
461 }
462 if (*ptr == '-' && n2 < 0) {
463 ++ptr;
464 continue;
465 }
466
467 /*
468 * collapse single-value ranges, handle skipmark, and fill
469 * in the character array appropriately.
470 */
471
472 if (n2 < 0) {
473 n2 = n1;
474 }
475 if (*ptr == '/') {
476 skip = strtol(ptr + 1, &ptr, 10);
477 }
478 /*
479 * fill array, using a failsafe is the easiest way to prevent
480 * an endless loop
481 */
482
483 {
484 int s0 = 1;
485 int failsafe = 1024;
486
487 --n1;
488 do {
489 n1 = (n1 + 1) % modvalue;
490
491 if (--s0 == 0) {
492 ary[n1 % modvalue] = 1;
493 s0 = skip;
494 }
495 }
496 while (n1 != n2 && --failsafe);
497
498 if (failsafe == 0) {
499 crondlog("\111failed user %s parsing %s\n", user, base);
500 return (NULL);
501 }
502 }
503 if (*ptr != ',') {
504 break;
505 }
506 ++ptr;
507 n1 = -1;
508 n2 = -1;
509 }
510
511 if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
512 crondlog("\111failed user %s parsing %s\n", user, base);
513 return (NULL);
514 }
515
516 while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') {
517 ++ptr;
518 }
519#ifdef FEATURE_DEBUG_OPT
520 if (DebugOpt) {
521 int i;
522
523 for (i = 0; i < modvalue; ++i) {
524 crondlog("\005%d", ary[i]);
525 }
526 crondlog("\005\n");
527 }
528#endif
529
530 return (ptr);
531}
532
533static void FixDayDow(CronLine * line)
534{
535 short i;
536 short weekUsed = 0;
537 short daysUsed = 0;
538
539 for (i = 0; i < arysize(line->cl_Dow); ++i) {
540 if (line->cl_Dow[i] == 0) {
541 weekUsed = 1;
542 break;
543 }
544 }
545 for (i = 0; i < arysize(line->cl_Days); ++i) {
546 if (line->cl_Days[i] == 0) {
547 daysUsed = 1;
548 break;
549 }
550 }
551 if (weekUsed && !daysUsed) {
552 memset(line->cl_Days, 0, sizeof(line->cl_Days));
553 }
554 if (daysUsed && !weekUsed) {
555 memset(line->cl_Dow, 0, sizeof(line->cl_Dow));
556 }
557}
558
559
560
561static void SynchronizeFile(const char *fileName)
562{
563 int maxEntries = MAXLINES;
564 int maxLines;
565 char buf[1024];
566
567 if (strcmp(fileName, "root") == 0) {
568 maxEntries = 65535;
569 }
570 maxLines = maxEntries * 10;
571
572 if (fileName) {
573 FILE *fi;
574
575 DeleteFile(fileName);
576
577 fi = fopen(fileName, "r");
578 if (fi != NULL) {
579 struct stat sbuf;
580
581 if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {
582 CronFile *file = calloc(1, sizeof(CronFile));
583 CronLine **pline;
584
585 file->cf_User = strdup(fileName);
586 pline = &file->cf_LineBase;
587
588 while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) {
589 CronLine line;
590 char *ptr;
591
592 if (buf[0]) {
593 buf[strlen(buf) - 1] = 0;
594 }
595 if (buf[0] == 0 || buf[0] == '#' || buf[0] == ' ' || buf[0] == '\t') {
596 continue;
597 }
598 if (--maxEntries == 0) {
599 break;
600 }
601 memset(&line, 0, sizeof(line));
602
603#ifdef FEATURE_DEBUG_OPT
604 if (DebugOpt) {
605 crondlog("\111User %s Entry %s\n", fileName, buf);
606 }
607#endif
608
609 /* parse date ranges */
610 ptr = ParseField(file->cf_User, line.cl_Mins, 60, 0, NULL, buf);
611 ptr = ParseField(file->cf_User, line.cl_Hrs, 24, 0, NULL, ptr);
612 ptr = ParseField(file->cf_User, line.cl_Days, 32, 0, NULL, ptr);
613 ptr = ParseField(file->cf_User, line.cl_Mons, 12, -1, MonAry, ptr);
614 ptr = ParseField(file->cf_User, line.cl_Dow, 7, 0, DowAry, ptr);
615
616 /* check failure */
617 if (ptr == NULL) {
618 continue;
619 }
620
621 /*
622 * fix days and dow - if one is not * and the other
623 * is *, the other is set to 0, and vise-versa
624 */
625
626 FixDayDow(&line);
627
628 *pline = calloc(1, sizeof(CronLine));
629 **pline = line;
630
631 /* copy command */
632 (*pline)->cl_Shell = strdup(ptr);
633
634#ifdef FEATURE_DEBUG_OPT
635 if (DebugOpt) {
636 crondlog("\111 Command %s\n", ptr);
637 }
638#endif
639
640 pline = &((*pline)->cl_Next);
641 }
642 *pline = NULL;
643
644 file->cf_Next = FileBase;
645 FileBase = file;
646
647 if (maxLines == 0 || maxEntries == 0) {
648 crondlog("\111Maximum number of lines reached for user %s\n", fileName);
649 }
650 }
651 fclose(fi);
652 }
653 }
654}
655
656static void CheckUpdates(void)
657{
658 FILE *fi;
659 char buf[256];
660
661 fi = fopen(CRONUPDATE, "r");
662 if (fi != NULL) {
663 remove(CRONUPDATE);
664 while (fgets(buf, sizeof(buf), fi) != NULL) {
665 SynchronizeFile(strtok(buf, " \t\r\n"));
666 }
667 fclose(fi);
668 }
669}
670
671static void SynchronizeDir(void)
672{
673 /* Attempt to delete the database. */
674
675 for (;;) {
676 CronFile *file;
677
678 for (file = FileBase; file && file->cf_Deleted; file = file->cf_Next);
679 if (file == NULL) {
680 break;
681 }
682 DeleteFile(file->cf_User);
683 }
684
685 /*
686 * Remove cron update file
687 *
688 * Re-chdir, in case directory was renamed & deleted, or otherwise
689 * screwed up.
690 *
691 * scan directory and add associated users
692 */
693
694 remove(CRONUPDATE);
695 if (chdir(CDir) < 0) {
696 crondlog("\311unable to find %s\n", CDir);
697 }
698 {
699 DIR *dir = opendir(".");
700 struct dirent *den;
701
702 if (dir) {
703 while ((den = readdir(dir))) {
704 if (strchr(den->d_name, '.') != NULL) {
705 continue;
706 }
707 if (getpwnam(den->d_name)) {
708 SynchronizeFile(den->d_name);
709 } else {
710 crondlog("\007ignoring %s\n", den->d_name);
711 }
712 }
713 closedir(dir);
714 } else {
715 crondlog("\311Unable to open current dir!\n");
716 }
717 }
718}
719
720
721/*
722 * DeleteFile() - delete user database
723 *
724 * Note: multiple entries for same user may exist if we were unable to
725 * completely delete a database due to running processes.
726 */
727
728static void DeleteFile(const char *userName)
729{
730 CronFile **pfile = &FileBase;
731 CronFile *file;
732
733 while ((file = *pfile) != NULL) {
734 if (strcmp(userName, file->cf_User) == 0) {
735 CronLine **pline = &file->cf_LineBase;
736 CronLine *line;
737
738 file->cf_Running = 0;
739 file->cf_Deleted = 1;
740
741 while ((line = *pline) != NULL) {
742 if (line->cl_Pid > 0) {
743 file->cf_Running = 1;
744 pline = &line->cl_Next;
745 } else {
746 *pline = line->cl_Next;
747 free(line->cl_Shell);
748 free(line);
749 }
750 }
751 if (file->cf_Running == 0) {
752 *pfile = file->cf_Next;
753 free(file->cf_User);
754 free(file);
755 } else {
756 pfile = &file->cf_Next;
757 }
758 } else {
759 pfile = &file->cf_Next;
760 }
761 }
762}
763
764/*
765 * TestJobs()
766 *
767 * determine which jobs need to be run. Under normal conditions, the
768 * period is about a minute (one scan). Worst case it will be one
769 * hour (60 scans).
770 */
771
772static int TestJobs(time_t t1, time_t t2)
773{
774 short nJobs = 0;
775 time_t t;
776
777 /* Find jobs > t1 and <= t2 */
778
779 for (t = t1 - t1 % 60; t <= t2; t += 60) {
780 if (t > t1) {
781 struct tm *tp = localtime(&t);
782 CronFile *file;
783 CronLine *line;
784
785 for (file = FileBase; file; file = file->cf_Next) {
786#ifdef FEATURE_DEBUG_OPT
787 if (DebugOpt)
788 crondlog("\005FILE %s:\n", file->cf_User);
789#endif
790 if (file->cf_Deleted)
791 continue;
792 for (line = file->cf_LineBase; line; line = line->cl_Next) {
793#ifdef FEATURE_DEBUG_OPT
794 if (DebugOpt)
795 crondlog("\005 LINE %s\n", line->cl_Shell);
796#endif
797 if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour] &&
798 (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday])
799 && line->cl_Mons[tp->tm_mon]) {
800#ifdef FEATURE_DEBUG_OPT
801 if (DebugOpt) {
802 crondlog("\005 JobToDo: %d %s\n",
803 line->cl_Pid, line->cl_Shell);
804 }
805#endif
806 if (line->cl_Pid > 0) {
807 crondlog("\010 process already running: %s %s\n",
808 file->cf_User, line->cl_Shell);
809 } else if (line->cl_Pid == 0) {
810 line->cl_Pid = -1;
811 file->cf_Ready = 1;
812 ++nJobs;
813 }
814 }
815 }
816 }
817 }
818 }
819 return (nJobs);
820}
821
822static void RunJobs(void)
823{
824 CronFile *file;
825 CronLine *line;
826
827 for (file = FileBase; file; file = file->cf_Next) {
828 if (file->cf_Ready) {
829 file->cf_Ready = 0;
830
831 for (line = file->cf_LineBase; line; line = line->cl_Next) {
832 if (line->cl_Pid < 0) {
833
834 RunJob(file->cf_User, line);
835
836 crondlog("\010USER %s pid %3d cmd %s\n",
837 file->cf_User, line->cl_Pid, line->cl_Shell);
838 if (line->cl_Pid < 0) {
839 file->cf_Ready = 1;
840 }
841 else if (line->cl_Pid > 0) {
842 file->cf_Running = 1;
843 }
844 }
845 }
846 }
847 }
848}
849
850/*
851 * CheckJobs() - check for job completion
852 *
853 * Check for job completion, return number of jobs still running after
854 * all done.
855 */
856
857static int CheckJobs(void)
858{
859 CronFile *file;
860 CronLine *line;
861 int nStillRunning = 0;
862
863 for (file = FileBase; file; file = file->cf_Next) {
864 if (file->cf_Running) {
865 file->cf_Running = 0;
866
867 for (line = file->cf_LineBase; line; line = line->cl_Next) {
868 if (line->cl_Pid > 0) {
869 int status;
870 int r = wait4(line->cl_Pid, &status, WNOHANG, NULL);
871
872 if (r < 0 || r == line->cl_Pid) {
873 EndJob(file->cf_User, line);
874 if (line->cl_Pid) {
875 file->cf_Running = 1;
876 }
877 } else if (r == 0) {
878 file->cf_Running = 1;
879 }
880 }
881 }
882 }
883 nStillRunning += file->cf_Running;
884 }
885 return (nStillRunning);
886}
887
888
889#ifdef CONFIG_FEATURE_CROND_CALL_SENDMAIL
890static void
891ForkJob(const char *user, CronLine * line, int mailFd,
892 const char *prog, const char *cmd, const char *arg, const char *mailf)
893{
894 /* Fork as the user in question and run program */
895 pid_t pid = fork();
896
897 line->cl_Pid = pid;
898 if (pid == 0) {
899 /* CHILD */
900
901 /* Change running state to the user in question */
902
903 if (ChangeUser(user) < 0) {
904 exit(0);
905 }
906#ifdef FEATURE_DEBUG_OPT
907 if (DebugOpt) {
908 crondlog("\005Child Running %s\n", prog);
909 }
910#endif
911
912 if (mailFd >= 0) {
913 dup2(mailFd, mailf != NULL);
914 dup2((mailf ? mailFd : 1), 2);
915 close(mailFd);
916 }
917 execl(prog, prog, cmd, arg, NULL);
918 crondlog("\024unable to exec, user %s cmd %s %s %s\n", user, prog, cmd, arg);
919 if (mailf) {
920 fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);
921 }
922 exit(0);
923 } else if (pid < 0) {
924 /* FORK FAILED */
925 crondlog("\024couldn't fork, user %s\n", user);
926 line->cl_Pid = 0;
927 if (mailf) {
928 remove(mailf);
929 }
930 } else if (mailf) {
931 /* PARENT, FORK SUCCESS
932 * rename mail-file based on pid of process
933 */
934 char mailFile2[128];
935
936 snprintf(mailFile2, sizeof(mailFile2), TMPDIR "/cron.%s.%d", user, pid);
937 rename(mailf, mailFile2);
938 }
939 /*
940 * Close the mail file descriptor.. we can't just leave it open in
941 * a structure, closing it later, because we might run out of descriptors
942 */
943
944 if (mailFd >= 0) {
945 close(mailFd);
946 }
947}
948
949static void RunJob(const char *user, CronLine * line)
950{
951 char mailFile[128];
952 int mailFd;
953
954 line->cl_Pid = 0;
955 line->cl_MailFlag = 0;
956
957 /* open mail file - owner root so nobody can screw with it. */
958
959 snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, getpid());
960 mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600);
961
962 if (mailFd >= 0) {
963 line->cl_MailFlag = 1;
964 fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", user,
965 line->cl_Shell);
966 line->cl_MailPos = lseek(mailFd, 0, 1);
967 } else {
968 crondlog("\024unable to create mail file user %s file %s, output to /dev/null\n", user, mailFile);
969 }
970
971 ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile);
972}
973
974/*
975 * EndJob - called when job terminates and when mail terminates
976 */
977
978static void EndJob(const char *user, CronLine * line)
979{
980 int mailFd;
981 char mailFile[128];
982 struct stat sbuf;
983
984 /* No job */
985
986 if (line->cl_Pid <= 0) {
987 line->cl_Pid = 0;
988 return;
989 }
990
991 /*
992 * End of job and no mail file
993 * End of sendmail job
994 */
995
996 snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, line->cl_Pid);
997 line->cl_Pid = 0;
998
999 if (line->cl_MailFlag != 1) {
1000 return;
1001 }
1002 line->cl_MailFlag = 0;
1003
1004 /*
1005 * End of primary job - check for mail file. If size has increased and
1006 * the file is still valid, we sendmail it.
1007 */
1008
1009 mailFd = open(mailFile, O_RDONLY);
1010 remove(mailFile);
1011 if (mailFd < 0) {
1012 return;
1013 }
1014
1015 if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid || sbuf.st_nlink != 0 ||
1016 sbuf.st_size == line->cl_MailPos || !S_ISREG(sbuf.st_mode)) {
1017 close(mailFd);
1018 return;
1019 }
1020 ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
1021}
1022#else
1023/* crond without sendmail */
1024
1025static void RunJob(const char *user, CronLine * line)
1026{
1027 /* Fork as the user in question and run program */
1028 pid_t pid = fork();
1029
1030 if (pid == 0) {
1031 /* CHILD */
1032
1033 /* Change running state to the user in question */
1034
1035 if (ChangeUser(user) < 0) {
1036 exit(0);
1037 }
1038#ifdef FEATURE_DEBUG_OPT
1039 if (DebugOpt) {
1040 crondlog("\005Child Running %s\n", DEFAULT_SHELL);
1041 }
1042#endif
1043
1044 execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL);
1045 crondlog("\024unable to exec, user %s cmd %s -c %s\n", user,
1046 DEFAULT_SHELL, line->cl_Shell);
1047 exit(0);
1048 } else if (pid < 0) {
1049 /* FORK FAILED */
1050 crondlog("\024couldn't fork, user %s\n", user);
1051 pid = 0;
1052 }
1053 line->cl_Pid = pid;
1054}
1055#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 @@
1/*
2 * CRONTAB
3 *
4 * usually setuid root, -c option only works if getuid() == geteuid()
5 *
6 * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
7 * May be distributed under the GNU General Public License
8 *
9 * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002 to be used in busybox
10 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <stdarg.h>
16#include <string.h>
17#include <errno.h>
18#include <time.h>
19#include <dirent.h>
20#include <fcntl.h>
21#include <unistd.h>
22#include <syslog.h>
23#include <signal.h>
24#include <getopt.h>
25#include <sys/ioctl.h>
26#include <sys/wait.h>
27#include <sys/stat.h>
28#include <sys/resource.h>
29
30#ifndef CRONTABS
31#define CRONTABS "/var/spool/cron/crontabs"
32#endif
33#ifndef TMPDIR
34#define TMPDIR "/var/spool/cron"
35#endif
36#ifndef CRONUPDATE
37#define CRONUPDATE "cron.update"
38#endif
39#ifndef PATH_VI
40#define PATH_VI "/bin/vi" /* location of vi */
41#endif
42
43#include "busybox.h"
44
45static const char *CDir = CRONTABS;
46
47static void EditFile(const char *user, const char *file);
48static int GetReplaceStream(const char *user, const char *file);
49static int ChangeUser(const char *user, short dochdir);
50
51int
52crontab_main(int ac, char **av)
53{
54 enum { NONE, EDIT, LIST, REPLACE, DELETE } option = NONE;
55 const struct passwd *pas;
56 const char *repFile = NULL;
57 int repFd = 0;
58 int i;
59 char caller[256]; /* user that ran program */
60 int UserId;
61
62 UserId = getuid();
63 if ((pas = getpwuid(UserId)) == NULL)
64 bb_perror_msg_and_die("getpwuid");
65
66 strncpy(caller, pas->pw_name, sizeof(caller));
67
68 i = 1;
69 if (ac > 1) {
70 if (av[1][0] == '-' && av[1][1] == 0) {
71 option = REPLACE;
72 ++i;
73 } else if (av[1][0] != '-') {
74 option = REPLACE;
75 ++i;
76 repFile = av[1];
77 }
78 }
79
80 for (; i < ac; ++i) {
81 char *ptr = av[i];
82
83 if (*ptr != '-')
84 break;
85 ptr += 2;
86
87 switch(ptr[-1]) {
88 case 'l':
89 if (ptr[-1] == 'l')
90 option = LIST;
91 /* fall through */
92 case 'e':
93 if (ptr[-1] == 'e')
94 option = EDIT;
95 /* fall through */
96 case 'd':
97 if (ptr[-1] == 'd')
98 option = DELETE;
99 /* fall through */
100 case 'u':
101 if (i + 1 < ac && av[i+1][0] != '-') {
102 ++i;
103 if (getuid() == geteuid()) {
104 pas = getpwnam(av[i]);
105 if (pas) {
106 UserId = pas->pw_uid;
107 } else {
108 bb_error_msg_and_die("user %s unknown", av[i]);
109 }
110 } else {
111 bb_error_msg_and_die("only the superuser may specify a user");
112 }
113 }
114 break;
115 case 'c':
116 if (getuid() == geteuid()) {
117 CDir = (*ptr) ? ptr : av[++i];
118 } else {
119 bb_error_msg_and_die("-c option: superuser only");
120 }
121 break;
122 default:
123 i = ac;
124 break;
125 }
126 }
127 if (i != ac || option == NONE)
128 bb_show_usage();
129
130 /*
131 * Get password entry
132 */
133
134 if ((pas = getpwuid(UserId)) == NULL)
135 bb_perror_msg_and_die("getpwuid");
136
137 /*
138 * If there is a replacement file, obtain a secure descriptor to it.
139 */
140
141 if (repFile) {
142 repFd = GetReplaceStream(caller, repFile);
143 if (repFd < 0)
144 bb_error_msg_and_die("unable to read replacement file");
145 }
146
147 /*
148 * Change directory to our crontab directory
149 */
150
151 if (chdir(CDir) < 0)
152 bb_perror_msg_and_die("cannot change dir to %s", CDir);
153
154 /*
155 * Handle options as appropriate
156 */
157
158 switch(option) {
159 case LIST:
160 {
161 FILE *fi;
162 char buf[1024];
163
164 if ((fi = fopen(pas->pw_name, "r"))) {
165 while (fgets(buf, sizeof(buf), fi) != NULL)
166 fputs(buf, stdout);
167 fclose(fi);
168 } else {
169 bb_error_msg("no crontab for %s", pas->pw_name);
170 }
171 }
172 break;
173 case EDIT:
174 {
175 FILE *fi;
176 int fd;
177 int n;
178 char tmp[128];
179 char buf[1024];
180
181 snprintf(tmp, sizeof(tmp), TMPDIR "/crontab.%d", getpid());
182 if ((fd = open(tmp, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0600)) >= 0) {
183 chown(tmp, getuid(), getgid());
184 if ((fi = fopen(pas->pw_name, "r"))) {
185 while ((n = fread(buf, 1, sizeof(buf), fi)) > 0)
186 write(fd, buf, n);
187 }
188 EditFile(caller, tmp);
189 remove(tmp);
190 lseek(fd, 0L, 0);
191 repFd = fd;
192 } else {
193 bb_error_msg_and_die("unable to create %s", tmp);
194 }
195
196 }
197 option = REPLACE;
198 /* fall through */
199 case REPLACE:
200 {
201 char buf[1024];
202 char path[1024];
203 int fd;
204 int n;
205
206 snprintf(path, sizeof(path), "%s.new", pas->pw_name);
207 if ((fd = open(path, O_CREAT|O_TRUNC|O_APPEND|O_WRONLY, 0600)) >= 0) {
208 while ((n = read(repFd, buf, sizeof(buf))) > 0) {
209 write(fd, buf, n);
210 }
211 close(fd);
212 rename(path, pas->pw_name);
213 } else {
214 bb_error_msg("unable to create %s/%s", CDir, path);
215 }
216 close(repFd);
217 }
218 break;
219 case DELETE:
220 remove(pas->pw_name);
221 break;
222 case NONE:
223 default:
224 break;
225 }
226
227 /*
228 * Bump notification file. Handle window where crond picks file up
229 * before we can write our entry out.
230 */
231
232 if (option == REPLACE || option == DELETE) {
233 FILE *fo;
234 struct stat st;
235
236 while ((fo = fopen(CRONUPDATE, "a"))) {
237 fprintf(fo, "%s\n", pas->pw_name);
238 fflush(fo);
239 if (fstat(fileno(fo), &st) != 0 || st.st_nlink != 0) {
240 fclose(fo);
241 break;
242 }
243 fclose(fo);
244 /* loop */
245 }
246 if (fo == NULL) {
247 bb_error_msg("unable to append to %s/%s", CDir, CRONUPDATE);
248 }
249 }
250 return 0;
251}
252
253static int
254GetReplaceStream(const char *user, const char *file)
255{
256 int filedes[2];
257 int pid;
258 int fd;
259 int n;
260 char buf[1024];
261
262 if (pipe(filedes) < 0) {
263 perror("pipe");
264 return(-1);
265 }
266 if ((pid = fork()) < 0) {
267 perror("fork");
268 return(-1);
269 }
270 if (pid > 0) {
271 /*
272 * PARENT
273 */
274
275 close(filedes[1]);
276 if (read(filedes[0], buf, 1) != 1) {
277 close(filedes[0]);
278 filedes[0] = -1;
279 }
280 return(filedes[0]);
281 }
282
283 /*
284 * CHILD
285 */
286
287 close(filedes[0]);
288
289 if (ChangeUser(user, 0) < 0)
290 exit(0);
291
292 fd = open(file, O_RDONLY);
293 if (fd < 0) {
294 bb_error_msg("unable to open %s", file);
295 exit(0);
296 }
297 buf[0] = 0;
298 write(filedes[1], buf, 1);
299 while ((n = read(fd, buf, sizeof(buf))) > 0) {
300 write(filedes[1], buf, n);
301 }
302 exit(0);
303}
304
305static void
306EditFile(const char *user, const char *file)
307{
308 int pid;
309
310 if ((pid = fork()) == 0) {
311 /*
312 * CHILD - change user and run editor
313 */
314 char *ptr;
315 char visual[1024];
316
317 if (ChangeUser(user, 1) < 0)
318 exit(0);
319 if ((ptr = getenv("VISUAL")) == NULL || strlen(ptr) > 256)
320 ptr = PATH_VI;
321
322 snprintf(visual, sizeof(visual), "%s %s", ptr, file);
323 execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", visual, NULL);
324 perror("exec");
325 exit(0);
326 }
327 if (pid < 0) {
328 /*
329 * PARENT - failure
330 */
331 bb_perror_msg_and_die("fork");
332 }
333 wait4(pid, NULL, 0, NULL);
334}
335
336static int
337ChangeUser(const char *user, short dochdir)
338{
339 struct passwd *pas;
340
341 /*
342 * Obtain password entry and change privileges
343 */
344
345 if ((pas = getpwnam(user)) == 0) {
346 bb_perror_msg_and_die("failed to get uid for %s", user);
347 return(-1);
348 }
349 setenv("USER", pas->pw_name, 1);
350 setenv("HOME", pas->pw_dir, 1);
351 setenv("SHELL", DEFAULT_SHELL, 1);
352
353 /*
354 * Change running state to the user in question
355 */
356 change_identity(pas);
357
358 if (dochdir) {
359 if (chdir(pas->pw_dir) < 0) {
360 bb_perror_msg_and_die("chdir failed: %s %s", user, pas->pw_dir);
361 if (chdir(TMPDIR) < 0) {
362 bb_perror_msg_and_die("chdir failed: %s %s", user, TMPDIR);
363 return(-1);
364 }
365 }
366 }
367 return(pas->pw_uid);
368}
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 @@
1/* vi: set sw=4 ts=4: */
2#include <ctype.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <unistd.h>
7#include <math.h>
8#include "busybox.h"
9
10/* Tiny RPN calculator, because "expr" didn't give me bitwise operations. */
11
12static double stack[100];
13static unsigned int pointer;
14static unsigned char base;
15
16static void push(double a)
17{
18 if (pointer >= (sizeof(stack) / sizeof(*stack)))
19 bb_error_msg_and_die("stack overflow");
20 stack[pointer++] = a;
21}
22
23static double pop(void)
24{
25 if (pointer == 0)
26 bb_error_msg_and_die("stack underflow");
27 return stack[--pointer];
28}
29
30static void add(void)
31{
32 push(pop() + pop());
33}
34
35static void sub(void)
36{
37 double subtrahend = pop();
38
39 push(pop() - subtrahend);
40}
41
42static void mul(void)
43{
44 push(pop() * pop());
45}
46
47static void power(void)
48{
49 double topower = pop();
50
51 push(pow(pop(), topower));
52}
53
54static void divide(void)
55{
56 double divisor = pop();
57
58 push(pop() / divisor);
59}
60
61static void mod(void)
62{
63 unsigned int d = pop();
64
65 push((unsigned int) pop() % d);
66}
67
68static void and(void)
69{
70 push((unsigned int) pop() & (unsigned int) pop());
71}
72
73static void or(void)
74{
75 push((unsigned int) pop() | (unsigned int) pop());
76}
77
78static void eor(void)
79{
80 push((unsigned int) pop() ^ (unsigned int) pop());
81}
82
83static void not(void)
84{
85 push(~(unsigned int) pop());
86}
87
88static void set_output_base(void)
89{
90 base=(unsigned char)pop();
91 if ((base != 10) && (base != 16)) {
92 fprintf(stderr, "Error: base = %d is not supported.\n", base);
93 base=10;
94 }
95}
96
97static void print_base(double print)
98{
99 if (base == 16)
100 printf("%x\n", (unsigned int)print);
101 else
102 printf("%g\n", print);
103}
104
105static void print_stack_no_pop(void)
106{
107 unsigned int i=pointer;
108 while (i)
109 print_base(stack[--i]);
110}
111
112static void print_no_pop(void)
113{
114 print_base(stack[pointer-1]);
115}
116
117struct op {
118 const char *name;
119 void (*function) (void);
120};
121
122static const struct op operators[] = {
123 {"+", add},
124 {"add", add},
125 {"-", sub},
126 {"sub", sub},
127 {"*", mul},
128 {"mul", mul},
129 {"/", divide},
130 {"div", divide},
131 {"**", power},
132 {"exp", power},
133 {"pow", power},
134 {"%", mod},
135 {"mod", mod},
136 {"and", and},
137 {"or", or},
138 {"not", not},
139 {"eor", eor},
140 {"xor", eor},
141 {"p", print_no_pop},
142 {"f", print_stack_no_pop},
143 {"o", set_output_base},
144 {0, 0}
145};
146
147static void stack_machine(const char *argument)
148{
149 char *endPointer = 0;
150 double d;
151 const struct op *o = operators;
152
153 if (argument == 0)
154 return;
155
156 d = strtod(argument, &endPointer);
157
158 if (endPointer != argument) {
159 push(d);
160 return;
161 }
162
163 while (o->name != 0) {
164 if (strcmp(o->name, argument) == 0) {
165 (*(o->function)) ();
166 return;
167 }
168 o++;
169 }
170 bb_error_msg_and_die("%s: syntax error.", argument);
171}
172
173/* return pointer to next token in buffer and set *buffer to one char
174 * past the end of the above mentioned token
175 */
176static char *get_token(char **buffer)
177{
178 char *start = NULL;
179 char *current = *buffer;
180
181 while (isspace(*current)) { current++; }
182 if (*current != 0) {
183 start = current;
184 while (!isspace(*current) && *current != 0) { current++; }
185 *buffer = current;
186 }
187 return start;
188}
189
190/* In Perl one might say, scalar m|\s*(\S+)\s*|g */
191static int number_of_tokens(char *buffer)
192{
193 int i = 0;
194 char *b = buffer;
195 while (get_token(&b)) { i++; }
196 return i;
197}
198
199int dc_main(int argc, char **argv)
200{
201 /* take stuff from stdin if no args are given */
202 if (argc <= 1) {
203 int i, len;
204 char *line = NULL;
205 char *cursor = NULL;
206 char *token = NULL;
207 while ((line = bb_get_chomped_line_from_file(stdin))) {
208 cursor = line;
209 len = number_of_tokens(line);
210 for (i = 0; i < len; i++) {
211 token = get_token(&cursor);
212 *cursor++ = 0;
213 stack_machine(token);
214 }
215 free(line);
216 }
217 } else {
218 if (*argv[1]=='-')
219 bb_show_usage();
220 while (argc >= 2) {
221 stack_machine(argv[1]);
222 argv++;
223 argc--;
224 }
225 }
226 stack_machine(0);
227 return EXIT_SUCCESS;
228}
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 @@
1/*
2 devfsd implementation for busybox
3
4 Copyright (C) 2003 by Tito Ragusa <farmatito@tiscali.it>
5
6 Busybox version is based on some previous work and ideas
7 Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it>
8
9 devfsd.c
10
11 Main file for devfsd (devfs daemon for Linux).
12
13 Copyright (C) 1998-2002 Richard Gooch
14
15 devfsd.h
16
17 Header file for devfsd (devfs daemon for Linux).
18
19 Copyright (C) 1998-2000 Richard Gooch
20
21 compat_name.c
22
23 Compatibility name file for devfsd (build compatibility names).
24
25 Copyright (C) 1998-2002 Richard Gooch
26
27 expression.c
28
29 This code provides Borne Shell-like expression expansion.
30
31 Copyright (C) 1997-1999 Richard Gooch
32
33 This program is free software; you can redistribute it and/or modify
34 it under the terms of the GNU General Public License as published by
35 the Free Software Foundation; either version 2 of the License, or
36 (at your option) any later version.
37
38 This program is distributed in the hope that it will be useful,
39 but WITHOUT ANY WARRANTY; without even the implied warranty of
40 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
41 GNU General Public License for more details.
42
43 You should have received a copy of the GNU General Public License
44 along with this program; if not, write to the Free Software
45 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
46
47 Richard Gooch may be reached by email at rgooch@atnf.csiro.au
48 The postal address is:
49 Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
50*/
51
52#include "libbb.h"
53#include "busybox.h"
54#include <unistd.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <stdarg.h>
58#include <string.h>
59#include <ctype.h>
60#include <sys/time.h>
61#include <sys/stat.h>
62#include <sys/types.h>
63#include <sys/wait.h>
64#include <sys/ioctl.h>
65#include <sys/socket.h>
66#include <sys/un.h>
67#include <dirent.h>
68#include <fcntl.h>
69#include <syslog.h>
70#include <signal.h>
71#include <regex.h>
72#include <errno.h>
73#include <sys/sysmacros.h>
74
75
76/* Various defines taken from linux/major.h */
77#define IDE0_MAJOR 3
78#define IDE1_MAJOR 22
79#define IDE2_MAJOR 33
80#define IDE3_MAJOR 34
81#define IDE4_MAJOR 56
82#define IDE5_MAJOR 57
83#define IDE6_MAJOR 88
84#define IDE7_MAJOR 89
85#define IDE8_MAJOR 90
86#define IDE9_MAJOR 91
87
88
89/* Various defines taken from linux/devfs_fs.h */
90#define DEVFSD_PROTOCOL_REVISION_KERNEL 5
91#define DEVFSD_IOCTL_BASE 'd'
92/* These are the various ioctls */
93#define DEVFSDIOC_GET_PROTO_REV _IOR(DEVFSD_IOCTL_BASE, 0, int)
94#define DEVFSDIOC_SET_EVENT_MASK _IOW(DEVFSD_IOCTL_BASE, 2, int)
95#define DEVFSDIOC_RELEASE_EVENT_QUEUE _IOW(DEVFSD_IOCTL_BASE, 3, int)
96#define DEVFSDIOC_SET_CONFIG_DEBUG_MASK _IOW(DEVFSD_IOCTL_BASE, 4, int)
97#define DEVFSD_NOTIFY_REGISTERED 0
98#define DEVFSD_NOTIFY_UNREGISTERED 1
99#define DEVFSD_NOTIFY_ASYNC_OPEN 2
100#define DEVFSD_NOTIFY_CLOSE 3
101#define DEVFSD_NOTIFY_LOOKUP 4
102#define DEVFSD_NOTIFY_CHANGE 5
103#define DEVFSD_NOTIFY_CREATE 6
104#define DEVFSD_NOTIFY_DELETE 7
105#define DEVFS_PATHLEN 1024
106/* Never change this otherwise the binary interface will change */
107
108struct devfsd_notify_struct
109{ /* Use native C types to ensure same types in kernel and user space */
110 unsigned int type; /* DEVFSD_NOTIFY_* value */
111 unsigned int mode; /* Mode of the inode or device entry */
112 unsigned int major; /* Major number of device entry */
113 unsigned int minor; /* Minor number of device entry */
114 unsigned int uid; /* Uid of process, inode or device entry */
115 unsigned int gid; /* Gid of process, inode or device entry */
116 unsigned int overrun_count; /* Number of lost events */
117 unsigned int namelen; /* Number of characters not including '\0' */
118 /* The device name MUST come last */
119 char devname[DEVFS_PATHLEN]; /* This will be '\0' terminated */
120};
121
122#define BUFFER_SIZE 16384
123#define DEVFSD_VERSION "1.3.25"
124#define CONFIG_FILE "/etc/devfsd.conf"
125#define MODPROBE "/sbin/modprobe"
126#define MODPROBE_SWITCH_1 "-k"
127#define MODPROBE_SWITCH_2 "-C"
128#define CONFIG_MODULES_DEVFS "/etc/modules.devfs"
129#define MAX_ARGS (6 + 1)
130#define MAX_SUBEXPR 10
131#define STRING_LENGTH 255
132
133/* for get_uid_gid() */
134#define UID 0
135#define GID 1
136
137/* for msg_logger(), do_ioctl(),
138 fork_and_execute() */
139# define DIE 1
140# define NO_DIE 0
141
142/* for dir_operation() */
143#define RESTORE 0
144#define SERVICE 1
145#define READ_CONFIG 2
146
147/* Update only after changing code to reflect new protocol */
148#define DEVFSD_PROTOCOL_REVISION_DAEMON 5
149
150/* Compile-time check */
151#if DEVFSD_PROTOCOL_REVISION_KERNEL != DEVFSD_PROTOCOL_REVISION_DAEMON
152#error protocol version mismatch. Update your kernel headers
153#endif
154
155#define AC_PERMISSIONS 0
156#define AC_MODLOAD 1
157#define AC_EXECUTE 2
158#define AC_MFUNCTION 3 /* not supported by busybox */
159#define AC_CFUNCTION 4 /* not supported by busybox */
160#define AC_COPY 5
161#define AC_IGNORE 6
162#define AC_MKOLDCOMPAT 7
163#define AC_MKNEWCOMPAT 8
164#define AC_RMOLDCOMPAT 9
165#define AC_RMNEWCOMPAT 10
166#define AC_RESTORE 11
167
168
169struct permissions_type
170{
171 mode_t mode;
172 uid_t uid;
173 gid_t gid;
174};
175
176struct execute_type
177{
178 char *argv[MAX_ARGS + 1]; /* argv[0] must always be the programme */
179};
180
181struct copy_type
182{
183 const char *source;
184 const char *destination;
185};
186
187struct action_type
188{
189 unsigned int what;
190 unsigned int when;
191};
192
193struct config_entry_struct
194{
195 struct action_type action;
196 regex_t preg;
197 union
198 {
199 struct permissions_type permissions;
200 struct execute_type execute;
201 struct copy_type copy;
202 }
203 u;
204 struct config_entry_struct *next;
205};
206
207struct get_variable_info
208{
209 const struct devfsd_notify_struct *info;
210 const char *devname;
211 char devpath[STRING_LENGTH];
212};
213
214static void dir_operation(int , const char * , int, unsigned long* );
215static void service(struct stat statbuf, char *path);
216static int st_expr_expand(char *, unsigned, const char *, const char *(*) (const char *, void *), void *);
217static const char *get_old_name(const char *, unsigned, char *, unsigned, unsigned);
218static int mksymlink (const char *oldpath, const char *newpath);
219static void read_config_file (char *path, int optional, unsigned long *event_mask);
220static void process_config_line (const char *, unsigned long *);
221static int do_servicing (int, unsigned long);
222static void service_name (const struct devfsd_notify_struct *);
223static void action_permissions (const struct devfsd_notify_struct *, const struct config_entry_struct *);
224static void action_execute (const struct devfsd_notify_struct *, const struct config_entry_struct *,
225 const regmatch_t *, unsigned);
226#ifdef CONFIG_DEVFSD_MODLOAD
227static void action_modload (const struct devfsd_notify_struct *info, const struct config_entry_struct *entry);
228#endif
229static void action_copy (const struct devfsd_notify_struct *, const struct config_entry_struct *,
230 const regmatch_t *, unsigned);
231static void action_compat (const struct devfsd_notify_struct *, unsigned);
232static void free_config (void);
233static void restore(char *spath, struct stat source_stat, int rootlen);
234static int copy_inode (const char *, const struct stat *, mode_t, const char *, const struct stat *);
235static mode_t get_mode (const char *);
236static void signal_handler (int);
237static const char *get_variable (const char *, void *);
238static int make_dir_tree (const char *);
239static int expand_expression(char *, unsigned, const char *, const char *(*)(const char *, void *), void *,
240 const char *, const regmatch_t *, unsigned );
241static void expand_regexp (char *, size_t, const char *, const char *, const regmatch_t *, unsigned );
242static const char *expand_variable( char *, unsigned, unsigned *, const char *,
243 const char *(*) (const char *, void *), void * );
244static const char *get_variable_v2(const char *, const char *(*) (const char *, void *), void *);
245static char get_old_ide_name (unsigned , unsigned);
246static char *write_old_sd_name (char *, unsigned, unsigned, char *);
247
248/* busybox functions */
249#if defined(CONFIG_DEVFSD_VERBOSE) || defined(CONFIG_DEBUG)
250static void msg_logger(int die, int pri, const char * fmt, ... );
251#endif
252static void do_ioctl(int die, int fd, int request, unsigned long event_mask_flag);
253static void fork_and_execute(int die, char *arg0, char **arg );
254static int get_uid_gid ( int, const char *);
255static void safe_memcpy( char * dest, const char * src, int len);
256static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, char *ptr);
257static unsigned int scan_dev_name(const char *d, unsigned int n, char *ptr);
258
259/* Structs and vars */
260static struct config_entry_struct *first_config = NULL;
261static struct config_entry_struct *last_config = NULL;
262static const char *mount_point = NULL;
263static volatile int caught_signal = FALSE;
264static volatile int caught_sighup = FALSE;
265static struct initial_symlink_struct
266{
267 char *dest;
268 char *name;
269} initial_symlinks[] =
270{
271 {"/proc/self/fd", "fd"},
272 {"fd/0", "stdin"},
273 {"fd/1", "stdout"},
274 {"fd/2", "stderr"},
275 {NULL, NULL},
276};
277
278static struct event_type
279{
280 unsigned int type; /* The DEVFSD_NOTIFY_* value */
281 const char *config_name; /* The name used in the config file */
282} event_types[] =
283{
284 {DEVFSD_NOTIFY_REGISTERED, "REGISTER"},
285 {DEVFSD_NOTIFY_UNREGISTERED, "UNREGISTER"},
286 {DEVFSD_NOTIFY_ASYNC_OPEN, "ASYNC_OPEN"},
287 {DEVFSD_NOTIFY_CLOSE, "CLOSE"},
288 {DEVFSD_NOTIFY_LOOKUP, "LOOKUP"},
289 {DEVFSD_NOTIFY_CHANGE, "CHANGE"},
290 {DEVFSD_NOTIFY_CREATE, "CREATE"},
291 {DEVFSD_NOTIFY_DELETE, "DELETE"},
292 {0xffffffff, NULL}
293};
294
295/* busybox functions and messages */
296
297extern void xregcomp(regex_t * preg, const char *regex, int cflags);
298
299const char * const bb_msg_proto_rev = "protocol revision";
300#ifdef CONFIG_DEVFSD_VERBOSE
301const char * const bb_msg_bad_config = "bad %s config file: %s\n";
302const char * const bb_msg_small_buffer = "buffer too small\n";
303const char * const bb_msg_variable_not_found = "variable: %s not found\n";
304#endif
305
306#if defined(CONFIG_DEVFSD_VERBOSE) || defined(CONFIG_DEBUG)
307static void msg_logger(int die, int pri, const char * fmt, ... )
308{
309 va_list ap;
310
311 va_start(ap, fmt);
312 if (access ("/dev/log", F_OK) == 0)
313 {
314 openlog(bb_applet_name, 0, LOG_DAEMON);
315 vsyslog( pri , fmt , ap);
316 closelog();
317 }
318#ifndef CONFIG_DEBUG
319 else
320#endif
321 bb_verror_msg(fmt, ap);
322 va_end(ap);
323 if(die==DIE)
324 exit(EXIT_FAILURE);
325}
326#endif
327
328static void do_ioctl(int die, int fd, int request, unsigned long event_mask_flag)
329{
330#ifdef CONFIG_DEVFSD_VERBOSE
331 if (ioctl (fd, request, event_mask_flag) == -1)
332 msg_logger(die, LOG_ERR, "ioctl(): %m\n");
333#else
334 if (ioctl (fd, request, event_mask_flag) == -1)
335 exit(EXIT_FAILURE);
336#endif
337}
338
339static void fork_and_execute(int die, char *arg0, char **arg )
340{
341 switch ( fork () )
342 {
343 case 0:
344 /* Child */
345 break;
346 case -1:
347 /* Parent: Error : die or return */
348#ifdef CONFIG_DEVFSD_VERBOSE
349 msg_logger(die, LOG_ERR,(char *) bb_msg_memory_exhausted);
350#else
351 if(die == DIE)
352 exit(EXIT_FAILURE);
353#endif
354 return;
355 default:
356 /* Parent : ok : return or exit */
357 if(arg0 != NULL)
358 {
359 wait (NULL);
360 return;
361 }
362 exit (EXIT_SUCCESS);
363 }
364 /* Child : if arg0 != NULL do execvp */
365 if(arg0 != NULL )
366 {
367 execvp (arg0, arg);
368#ifdef CONFIG_DEVFSD_VERBOSE
369 msg_logger(DIE, LOG_ERR, "execvp(): %s: %m\n", arg0);
370#else
371 exit(EXIT_FAILURE);
372#endif
373 }
374}
375
376static void safe_memcpy( char *dest, const char *src, int len)
377{
378 memcpy (dest , src , len );
379 dest[len] = '\0';
380}
381
382static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, char *ptr)
383{
384 if( d[n - 4]=='d' && d[n - 3]=='i' && d[n - 2]=='s' && d[n - 1]=='c')
385 return (2 + addendum);
386 else if( d[n - 2]=='c' && d[n - 1]=='d')
387 return (3 + addendum);
388 else if(ptr[0]=='p' && ptr[1]=='a' && ptr[2]=='r' && ptr[3]=='t')
389 return (4 + addendum);
390 else if( ptr[n - 2]=='m' && ptr[n - 1]=='t')
391 return (5 + addendum);
392 else
393 return 0;
394}
395
396static unsigned int scan_dev_name(const char *d, unsigned int n, char *ptr)
397{
398 if(d[0]=='s' && d[1]=='c' && d[2]=='s' && d[3]=='i' && d[4]=='/')
399 {
400 if( d[n - 7]=='g' && d[n - 6]=='e' && d[n - 5]=='n' &&
401 d[n - 4]=='e' && d[n - 3]=='r' && d[n - 2]=='i' &&
402 d[n - 1]=='c' )
403 return 1;
404 return scan_dev_name_common(d, n, 0, ptr);
405 }
406 else if(d[0]=='i' && d[1]=='d' && d[2]=='e' && d[3]=='/' &&
407 d[4]=='h' && d[5]=='o' && d[6]=='s' && d[7]=='t')
408 {
409 return scan_dev_name_common(d, n, 4, ptr);
410 }
411 else if(d[0]=='s' && d[1]=='b' && d[2]=='p' && d[3]=='/')
412 {
413 return 10;
414 }
415 else if(d[0]=='v' && d[1]=='c' && d[2]=='c' && d[3]=='/')
416 {
417 return 11;
418 }
419 else if(d[0]=='p' && d[1]=='t' && d[2]=='y' && d[3]=='/')
420 {
421 return 12;
422 }
423 return 0;
424}
425
426/* Public functions follow */
427
428int devfsd_main (int argc, char **argv)
429{
430 int print_version = FALSE;
431#ifdef CONFIG_DEVFSD_FG_NP
432 int do_daemon = TRUE;
433 int no_polling = FALSE;
434#endif
435 int do_scan;
436 int fd, proto_rev, count;
437 unsigned long event_mask = 0;
438 struct sigaction new_action;
439 struct initial_symlink_struct *curr;
440
441 if (argc < 2)
442 bb_show_usage();
443
444 for (count = 2; count < argc; ++count)
445 {
446 if(argv[count][0] == '-')
447 {
448 if(argv[count][1]=='v' && !argv[count][2]) /* -v */
449 print_version = TRUE;
450#ifdef CONFIG_DEVFSD_FG_NP
451 else if(argv[count][1]=='f' && argv[count][2]=='g' && !argv[count][3]) /* -fg */
452 do_daemon = FALSE;
453 else if(argv[count][1]=='n' && argv[count][2]=='p' && !argv[count][3]) /* -np */
454 no_polling = TRUE;
455#endif
456 else
457 bb_show_usage();
458 }
459 }
460
461 /* strip last / from mount point, so we don't need to check for it later */
462 while( argv[1][1]!='\0' && argv[1][strlen(argv[1])-1] == '/' )
463 argv[1][strlen(argv[1]) -1] = '\0';
464
465 mount_point = argv[1];
466
467 if (chdir (mount_point) != 0)
468#ifdef CONFIG_DEVFSD_VERBOSE
469 bb_error_msg_and_die( " %s: %m", mount_point);
470#else
471 exit(EXIT_FAILURE);
472#endif
473
474 fd = bb_xopen (".devfsd", O_RDONLY);
475
476 if (fcntl (fd, F_SETFD, FD_CLOEXEC) != 0)
477#ifdef CONFIG_DEVFSD_VERBOSE
478 bb_error_msg( "FD_CLOEXEC");
479#else
480 exit(EXIT_FAILURE);
481#endif
482
483 do_ioctl(DIE, fd, DEVFSDIOC_GET_PROTO_REV,(int )&proto_rev);
484
485 /*setup initial entries */
486 for (curr = initial_symlinks; curr->dest != NULL; ++curr)
487 symlink (curr->dest, curr->name);
488
489 /* NB: The check for CONFIG_FILE is done in read_config_file() */
490
491 if ( print_version || (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev) )
492 {
493 bb_printf( "%s v%s\nDaemon %s:\t%d\nKernel-side %s:\t%d\n",
494 bb_applet_name,DEVFSD_VERSION,bb_msg_proto_rev,
495 DEVFSD_PROTOCOL_REVISION_DAEMON,bb_msg_proto_rev, proto_rev);
496 if (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev)
497 bb_error_msg_and_die( "%s mismatch!",bb_msg_proto_rev);
498 exit(EXIT_SUCCESS); /* -v */
499 }
500 /* Tell kernel we are special (i.e. we get to see hidden entries) */
501 do_ioctl(DIE, fd, DEVFSDIOC_SET_EVENT_MASK, 0);
502
503 sigemptyset (&new_action.sa_mask);
504 new_action.sa_flags = 0;
505
506 /* Set up SIGHUP and SIGUSR1 handlers */
507 new_action.sa_handler = signal_handler;
508 if (sigaction (SIGHUP, &new_action, NULL) != 0 || sigaction (SIGUSR1, &new_action, NULL) != 0 )
509#ifdef CONFIG_DEVFSD_VERBOSE
510 bb_error_msg_and_die( "sigaction()");
511#else
512 exit(EXIT_FAILURE);
513#endif
514
515 bb_printf("%s v%s started for %s\n",bb_applet_name, DEVFSD_VERSION, mount_point);
516
517 /* Set umask so that mknod(2), open(2) and mkdir(2) have complete control over permissions */
518 umask (0);
519 read_config_file (CONFIG_FILE, FALSE, &event_mask);
520 /* Do the scan before forking, so that boot scripts see the finished product */
521 dir_operation(SERVICE,mount_point,0,NULL);
522#ifdef CONFIG_DEVFSD_FG_NP
523 if (no_polling)
524 exit (0);
525 if (do_daemon)
526 {
527#endif
528 /* Release so that the child can grab it */
529 do_ioctl(DIE, fd, DEVFSDIOC_RELEASE_EVENT_QUEUE, 0);
530 fork_and_execute(DIE, NULL, NULL);
531 setsid (); /* Prevent hangups and become pgrp leader */
532#ifdef CONFIG_DEVFSD_FG_NP
533 }
534 else
535 setpgid (0, 0); /* Become process group leader */
536#endif
537
538 while (TRUE)
539 {
540 do_scan = do_servicing (fd, event_mask);
541
542 free_config ();
543 read_config_file (CONFIG_FILE, FALSE, &event_mask);
544 if (do_scan)
545 dir_operation(SERVICE,mount_point,0,NULL);
546 }
547} /* End Function main */
548
549
550/* Private functions follow */
551
552static void read_config_file (char *path, int optional, unsigned long *event_mask)
553/* [SUMMARY] Read a configuration database.
554 <path> The path to read the database from. If this is a directory, all
555 entries in that directory will be read (except hidden entries).
556 <optional> If TRUE, the routine will silently ignore a missing config file.
557 <event_mask> The event mask is written here. This is not initialised.
558 [RETURNS] Nothing.
559*/
560{
561 struct stat statbuf;
562 FILE *fp;
563 char buf[STRING_LENGTH];
564 char *line=NULL;
565
566#ifdef CONFIG_DEBUG
567 msg_logger( NO_DIE, LOG_INFO, "read_config_file(): %s\n", path);
568#endif
569 if (stat (path, &statbuf) != 0 || statbuf.st_size == 0 )
570 goto read_config_file_err;
571
572 if ( S_ISDIR (statbuf.st_mode) )
573 {
574 /* strip last / from dirname so we don't need to check for it later */
575 while( path && path[1]!='\0' && path[strlen(path)-1] == '/')
576 path[strlen(path) -1] = '\0';
577
578 dir_operation(READ_CONFIG, path, 0, event_mask);
579 return;
580 }
581
582 if ( ( fp = fopen (path, "r") ) != NULL )
583 {
584 while (fgets (buf, STRING_LENGTH, fp) != NULL)
585 {
586 /* GETS(3) Linux Programmer's Manual
587 fgets() reads in at most one less than size characters from stream and
588 stores them into the buffer pointed to by s. Reading stops after an
589 EOF or a newline. If a newline is read, it is stored into the buffer.
590 A '\0' is stored after the last character in the buffer.
591 */
592 /*buf[strlen (buf) - 1] = '\0';*/
593 /* Skip whitespace */
594 for (line = buf; isspace (*line); ++line)
595 /*VOID*/;
596 if (line[0] == '\0' || line[0] == '#' )
597 continue;
598 process_config_line (line, event_mask);
599 }
600 fclose (fp);
601 errno=0;
602 }
603read_config_file_err:
604#ifdef CONFIG_DEVFSD_VERBOSE
605 msg_logger(((optional == 0 ) && (errno == ENOENT))? DIE : NO_DIE, LOG_ERR, "read config file: %s: %m\n", path);
606#else
607 if(optional == 0 && errno == ENOENT)
608 exit(EXIT_FAILURE);
609#endif
610 return;
611} /* End Function read_config_file */
612
613static void process_config_line (const char *line, unsigned long *event_mask)
614/* [SUMMARY] Process a line from a configuration file.
615 <line> The configuration line.
616 <event_mask> The event mask is written here. This is not initialised.
617 [RETURNS] Nothing.
618*/
619{
620 int num_args, count;
621 struct config_entry_struct *new;
622 char p[MAX_ARGS][STRING_LENGTH];
623 char when[STRING_LENGTH], what[STRING_LENGTH];
624 char name[STRING_LENGTH];
625 char * msg="";
626 char *ptr;
627
628 /* !!!! Only Uppercase Keywords in devsfd.conf */
629 const char *options[] = { "CLEAR_CONFIG", "INCLUDE", "OPTIONAL_INCLUDE", "RESTORE",
630 "PERMISSIONS", "MODLOAD", "EXECUTE", "COPY", "IGNORE",
631 "MKOLDCOMPAT", "MKNEWCOMPAT","RMOLDCOMPAT", "RMNEWCOMPAT", 0 };
632
633 short int i;
634
635#ifdef CONFIG_DEBUG
636 msg_logger( NO_DIE, LOG_INFO, "process_config_line()\n");
637#endif
638
639 for (count = 0; count < MAX_ARGS; ++count) p[count][0] = '\0';
640 num_args = sscanf (line, "%s %s %s %s %s %s %s %s %s %s",
641 when, name, what,
642 p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
643
644 i = compare_string_array(options, when );
645
646 /*"CLEAR_CONFIG"*/
647 if( i == 0)
648 {
649 free_config ();
650 *event_mask = 0;
651 return;
652 }
653
654 if ( num_args < 2)
655 goto process_config_line_err;
656
657 /* "INCLUDE" & "OPTIONAL_INCLUDE" */
658 if( i == 1 || i == 2 )
659 {
660 st_expr_expand (name, STRING_LENGTH, name, get_variable, NULL );
661#ifdef CONFIG_DEBUG
662 msg_logger( NO_DIE, LOG_INFO, "%sinclude: %s\n",(toupper (when[0]) == 'I') ? "": "optional_", name);
663#endif
664 read_config_file (name, (toupper (when[0]) == 'I') ? FALSE : TRUE, event_mask);
665 return;
666 }
667 /* "RESTORE" */
668 if( i == 3)
669 {
670 dir_operation(RESTORE,name, strlen (name),NULL);
671 return;
672 }
673 if (num_args < 3)
674 goto process_config_line_err;
675
676 new = xmalloc (sizeof *new);
677 memset (new, 0, sizeof *new);
678
679 for (count = 0; event_types[count].config_name != NULL; ++count)
680 {
681 if (strcasecmp (when, event_types[count].config_name) != 0)
682 continue;
683 new->action.when = event_types[count].type;
684 break;
685 }
686 if (event_types[count].config_name == NULL)
687 {
688 msg="WHEN in";
689 goto process_config_line_err;
690 }
691
692 i = compare_string_array(options, what );
693
694 switch(i)
695 {
696 case 4: /* "PERMISSIONS" */
697 new->action.what = AC_PERMISSIONS;
698 /* Get user and group */
699 if ( ( ptr = strchr (p[0], '.') ) == NULL )
700 {
701 msg="UID.GID";
702 goto process_config_line_err; /*"missing '.' in UID.GID */
703 }
704
705 *ptr++ = '\0';
706 new->u.permissions.uid = get_uid_gid (UID, p[0]);
707 new->u.permissions.gid = get_uid_gid (GID, ptr);
708 /* Get mode */
709 new->u.permissions.mode = get_mode (p[1]);
710 break;
711#ifdef CONFIG_DEVFSD_MODLOAD
712 case 5: /* MODLOAD */
713 /*This action will pass "/dev/$devname" (i.e. "/dev/" prefixed to
714 the device name) to the module loading facility. In addition,
715 the /etc/modules.devfs configuration file is used.*/
716 new->action.what = AC_MODLOAD;
717 break;
718#endif
719 case 6: /* EXECUTE */
720 new->action.what = AC_EXECUTE;
721 num_args -= 3;
722
723 for (count = 0; count < num_args; ++count)
724 new->u.execute.argv[count] = bb_xstrdup (p[count]);
725
726 new->u.execute.argv[num_args] = NULL;
727 break;
728 case 7: /* COPY */
729 new->action.what = AC_COPY;
730 num_args -= 3;
731 if (num_args != 2)
732 goto process_config_line_err; /* missing path and function in line */
733
734 new->u.copy.source = bb_xstrdup (p[0]);
735 new->u.copy.destination = bb_xstrdup (p[1]);
736 break;
737 case 8: /* IGNORE */
738 /* FALLTROUGH */
739 case 9: /* MKOLDCOMPAT */
740 /* FALLTROUGH */
741 case 10: /* MKNEWCOMPAT */
742 /* FALLTROUGH */
743 case 11:/* RMOLDCOMPAT */
744 /* FALLTROUGH */
745 case 12: /* RMNEWCOMPAT */
746 /* AC_IGNORE 6
747 AC_MKOLDCOMPAT 7
748 AC_MKNEWCOMPAT 8
749 AC_RMOLDCOMPAT 9
750 AC_RMNEWCOMPAT 10*/
751 new->action.what = i - 2;
752 break;
753 default:
754 msg ="WHAT in";
755 goto process_config_line_err;
756 /*esac*/
757 } /* switch (i) */
758
759 xregcomp( &new->preg, name, REG_EXTENDED);
760
761 *event_mask |= 1 << new->action.when;
762 new->next = NULL;
763 if (first_config == NULL)
764 first_config = new;
765 else
766 last_config->next = new;
767 last_config = new;
768 return;
769process_config_line_err:
770#ifdef CONFIG_DEVFSD_VERBOSE
771 msg_logger( DIE, LOG_ERR, bb_msg_bad_config, msg , line);
772#else
773 exit(EXIT_FAILURE);
774#endif
775} /* End Function process_config_line */
776
777static int do_servicing (int fd, unsigned long event_mask)
778/* [SUMMARY] Service devfs changes until a signal is received.
779 <fd> The open control file.
780 <event_mask> The event mask.
781 [RETURNS] TRUE if SIGHUP was caught, else FALSE.
782*/
783{
784 ssize_t bytes;
785 struct devfsd_notify_struct info;
786 unsigned long tmp_event_mask;
787
788#ifdef CONFIG_DEBUG
789 msg_logger( NO_DIE, LOG_INFO, "do_servicing()\n");
790#endif
791 /* Tell devfs what events we care about */
792 tmp_event_mask = event_mask;
793 do_ioctl(DIE, fd, DEVFSDIOC_SET_EVENT_MASK, tmp_event_mask);
794 while (!caught_signal)
795 {
796 errno = 0;
797 bytes = read (fd, (char *) &info, sizeof info);
798 if (caught_signal)
799 break; /* Must test for this first */
800 if (errno == EINTR)
801 continue; /* Yes, the order is important */
802 if (bytes < 1)
803 break;
804 service_name (&info);
805 }
806 if (caught_signal)
807 {
808 int c_sighup = caught_sighup;
809
810 caught_signal = FALSE;
811 caught_sighup = FALSE;
812 return (c_sighup);
813 }
814#ifdef CONFIG_DEVFSD_VERBOSE
815 msg_logger( NO_DIE, LOG_ERR, "read error on control file: %m\n");
816#endif
817 /* This is to shut up a compiler warning */
818 exit(EXIT_FAILURE);
819} /* End Function do_servicing */
820
821static void service_name (const struct devfsd_notify_struct *info)
822/* [SUMMARY] Service a single devfs change.
823 <info> The devfs change.
824 [RETURNS] Nothing.
825*/
826{
827 unsigned int n;
828 regmatch_t mbuf[MAX_SUBEXPR];
829 struct config_entry_struct *entry;
830
831#ifdef CONFIG_DEBUG
832 msg_logger( NO_DIE, LOG_INFO, "service_name()\n");
833 if (info->overrun_count > 0)
834 msg_logger( NO_DIE, LOG_ERR, "lost %u events\n", info->overrun_count);
835#endif
836
837 /* Discard lookups on "/dev/log" and "/dev/initctl" */
838 if( info->type == DEVFSD_NOTIFY_LOOKUP &&
839 ((info->devname[0]=='l' && info->devname[1]=='o' &&
840 info->devname[2]=='g' && !info->devname[3]) ||
841 ( info->devname[0]=='i' && info->devname[1]=='n' &&
842 info->devname[2]=='i' && info->devname[3]=='t' &&
843 info->devname[4]=='c' && info->devname[5]=='t' &&
844 info->devname[6]=='l' && !info->devname[7])))
845 return;
846 for (entry = first_config; entry != NULL; entry = entry->next)
847 {
848 /* First check if action matches the type, then check if name matches */
849 if (info->type != entry->action.when || regexec (&entry->preg, info->devname, MAX_SUBEXPR, mbuf, 0) != 0 )
850 continue;
851 for (n = 0; (n < MAX_SUBEXPR) && (mbuf[n].rm_so != -1); ++n)
852 /* VOID */;
853#ifdef CONFIG_DEBUG
854 msg_logger( NO_DIE, LOG_INFO, "service_name(): action.what %d\n", entry->action.what);
855#endif
856 switch (entry->action.what)
857 {
858 case AC_PERMISSIONS:
859 action_permissions (info, entry);
860 break;
861#ifdef CONFIG_DEVFSD_MODLOAD
862 case AC_MODLOAD:
863 action_modload (info, entry);
864 break;
865#endif
866 case AC_EXECUTE:
867 action_execute (info, entry, mbuf, n);
868 break;
869 case AC_COPY:
870 action_copy (info, entry, mbuf, n);
871 break;
872 case AC_IGNORE:
873 return;
874 /*break;*/
875 case AC_MKOLDCOMPAT:
876 case AC_MKNEWCOMPAT:
877 case AC_RMOLDCOMPAT:
878 case AC_RMNEWCOMPAT:
879 action_compat (info, entry->action.what);
880 break;
881 default:
882#ifdef CONFIG_DEVFSD_VERBOSE
883 msg_logger( DIE, LOG_ERR, "Unknown action\n");
884#else
885 exit(EXIT_FAILURE);
886#endif
887 /*break;*/
888 }
889 }
890} /* End Function service_name */
891
892static void action_permissions (const struct devfsd_notify_struct *info,
893 const struct config_entry_struct *entry)
894/* [SUMMARY] Update permissions for a device entry.
895 <info> The devfs change.
896 <entry> The config file entry.
897 [RETURNS] Nothing.
898*/
899{
900 struct stat statbuf;
901
902#ifdef CONFIG_DEBUG
903 msg_logger( NO_DIE, LOG_INFO, "action_permission()\n");
904#endif
905
906 if ( stat (info->devname, &statbuf) != 0 ||
907 chmod (info->devname,(statbuf.st_mode & S_IFMT) | (entry->u.permissions.mode & ~S_IFMT)) != 0 ||
908 chown (info->devname, entry->u.permissions.uid, entry->u.permissions.gid) != 0)
909 {
910#ifdef CONFIG_DEVFSD_VERBOSE
911 msg_logger( NO_DIE, LOG_ERR, "chmod() or chown(): %s: %m\n",info->devname);
912#endif
913 return;
914 }
915
916} /* End Function action_permissions */
917
918#ifdef CONFIG_DEVFSD_MODLOAD
919static void action_modload (const struct devfsd_notify_struct *info,
920 const struct config_entry_struct *entry)
921/* [SUMMARY] Load a module.
922 <info> The devfs change.
923 <entry> The config file entry.
924 [RETURNS] Nothing.
925*/
926{
927 char *argv[6];
928 char device[STRING_LENGTH];
929
930 argv[0] = MODPROBE;
931 argv[1] = MODPROBE_SWITCH_1; /* "-k" */
932 argv[2] = MODPROBE_SWITCH_2; /* "-C" */
933 argv[3] = CONFIG_MODULES_DEVFS;
934 argv[4] = device;
935 argv[5] = NULL;
936
937 snprintf (device, sizeof (device), "/dev/%s", info->devname);
938 #ifdef CONFIG_DEBUG
939 msg_logger( NO_DIE, LOG_INFO, "action_modload():%s %s %s %s %s\n",argv[0],argv[1],argv[2],argv[3],argv[4]);
940 #endif
941 fork_and_execute(DIE, argv[0], argv);
942} /* End Function action_modload */
943#endif
944
945static void action_execute (const struct devfsd_notify_struct *info,
946 const struct config_entry_struct *entry,
947 const regmatch_t *regexpr, unsigned int numexpr)
948/* [SUMMARY] Execute a programme.
949 <info> The devfs change.
950 <entry> The config file entry.
951 <regexpr> The number of subexpression (start, end) offsets within the
952 device name.
953 <numexpr> The number of elements within <<regexpr>>.
954 [RETURNS] Nothing.
955*/
956{
957 unsigned int count;
958 struct get_variable_info gv_info;
959 char *argv[MAX_ARGS + 1];
960 char largv[MAX_ARGS + 1][STRING_LENGTH];
961
962#ifdef CONFIG_DEBUG
963 int i;
964 char buff[512];
965#endif
966
967 gv_info.info = info;
968 gv_info.devname = info->devname;
969 snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname);
970 for (count = 0; entry->u.execute.argv[count] != NULL; ++count)
971 {
972 expand_expression (largv[count], STRING_LENGTH,
973 entry->u.execute.argv[count],
974 get_variable, &gv_info,
975 gv_info.devname, regexpr, numexpr );
976 argv[count] = largv[count];
977 }
978 argv[count] = NULL;
979
980#ifdef CONFIG_DEBUG
981 buff[0]='\0';
982 for(i=0;argv[i]!=NULL;i++) /* argv[i] < MAX_ARGS + 1 */
983 {
984 strcat(buff," ");
985 if( (strlen(buff)+ 1 + strlen(argv[i])) >= 512)
986 break;
987 strcat(buff,argv[i]);
988 }
989 strcat(buff,"\n");
990 msg_logger( NO_DIE, LOG_INFO, "action_execute(): %s",buff);
991#endif
992
993 fork_and_execute(NO_DIE, argv[0], argv);
994} /* End Function action_execute */
995
996
997static void action_copy (const struct devfsd_notify_struct *info,
998 const struct config_entry_struct *entry,
999 const regmatch_t *regexpr, unsigned int numexpr)
1000/* [SUMMARY] Copy permissions.
1001 <info> The devfs change.
1002 <entry> The config file entry.
1003 <regexpr> This list of subexpression (start, end) offsets within the
1004 device name.
1005 <numexpr> The number of elements in <<regexpr>>.
1006 [RETURNS] Nothing.
1007*/
1008{
1009 mode_t new_mode;
1010 struct get_variable_info gv_info;
1011 struct stat source_stat, dest_stat;
1012 char source[STRING_LENGTH], destination[STRING_LENGTH];
1013 dest_stat.st_mode = 0;
1014
1015#ifdef CONFIG_DEBUG
1016 msg_logger( NO_DIE, LOG_INFO, "action_copy()\n");
1017#endif
1018
1019 if ( (info->type == DEVFSD_NOTIFY_CHANGE) && S_ISLNK (info->mode) )
1020 return;
1021 gv_info.info = info;
1022 gv_info.devname = info->devname;
1023
1024 snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname);
1025 expand_expression (source, STRING_LENGTH, entry->u.copy.source,
1026 get_variable, &gv_info, gv_info.devname,
1027 regexpr, numexpr);
1028
1029 expand_expression (destination, STRING_LENGTH, entry->u.copy.destination,
1030 get_variable, &gv_info, gv_info.devname,
1031 regexpr, numexpr);
1032
1033 if ( !make_dir_tree (destination) || lstat (source, &source_stat) != 0)
1034 return;
1035 lstat (destination, &dest_stat);
1036 new_mode = source_stat.st_mode & ~S_ISVTX;
1037 if (info->type == DEVFSD_NOTIFY_CREATE)
1038 new_mode |= S_ISVTX;
1039 else if ( (info->type == DEVFSD_NOTIFY_CHANGE) && (dest_stat.st_mode & S_ISVTX) )
1040 new_mode |= S_ISVTX;
1041#ifdef CONFIG_DEBUG
1042 if ( !copy_inode (destination, &dest_stat, new_mode, source, &source_stat) && (errno != EEXIST))
1043 msg_logger( NO_DIE, LOG_ERR, "copy_inode(): %s to %s: %m\n", source, destination);
1044#else
1045 copy_inode (destination, &dest_stat, new_mode, source, &source_stat);
1046#endif
1047 return;
1048} /* End Function action_copy */
1049
1050static void action_compat (const struct devfsd_notify_struct *info, unsigned int action)
1051/* [SUMMARY] Process a compatibility request.
1052 <info> The devfs change.
1053 <action> The action to take.
1054 [RETURNS] Nothing.
1055*/
1056{
1057 const char *compat_name = NULL;
1058 const char *dest_name = info->devname;
1059 char *ptr=NULL;
1060 char compat_buf[STRING_LENGTH], dest_buf[STRING_LENGTH];
1061 int mode, host, bus, target, lun;
1062 unsigned int i;
1063 char rewind_;
1064 /* 1 to 5 "scsi/" , 6 to 9 "ide/host" */
1065 const char *fmt[] = { NULL ,
1066 "sg/c%db%dt%du%d", /* scsi/generic */
1067 "sd/c%db%dt%du%d", /* scsi/disc */
1068 "sr/c%db%dt%du%d", /* scsi/cd */
1069 "sd/c%db%dt%du%dp%d", /* scsi/part */
1070 "st/c%db%dt%du%dm%d%c", /* scsi/mt */
1071 "ide/hd/c%db%dt%du%d", /* ide/host/disc */
1072 "ide/cd/c%db%dt%du%d", /* ide/host/cd */
1073 "ide/hd/c%db%dt%du%dp%d", /* ide/host/part */
1074 "ide/mt/c%db%dt%du%d%s", /* ide/host/mt */
1075 NULL };
1076
1077 /* First construct compatibility name */
1078 switch (action)
1079 {
1080 case AC_MKOLDCOMPAT:
1081 case AC_RMOLDCOMPAT:
1082 compat_name = get_old_name (info->devname, info->namelen, compat_buf, info->major, info->minor);
1083 break;
1084 case AC_MKNEWCOMPAT:
1085 case AC_RMNEWCOMPAT:
1086 ptr = strrchr (info->devname, '/') + 1;
1087 i=scan_dev_name(info->devname, info->namelen, ptr);
1088
1089#ifdef CONFIG_DEBUG
1090 msg_logger( NO_DIE, LOG_INFO, "action_compat(): scan_dev_name() returned %d\n", i);
1091#endif
1092
1093 /* nothing found */
1094 if(i==0 || i > 9)
1095 return;
1096
1097 sscanf (info->devname +((i<6)?5:4), "host%d/bus%d/target%d/lun%d/", &host, &bus, &target, &lun);
1098 snprintf (dest_buf, sizeof (dest_buf), "../%s", info->devname + ((i>5)?4:0));
1099 dest_name = dest_buf;
1100 compat_name = compat_buf;
1101
1102
1103 /* 1 == scsi/generic 2 == scsi/disc 3 == scsi/cd 6 == ide/host/disc 7 == ide/host/cd */
1104 if( i == 1 || i == 2 || i == 3 || i == 6 || i ==7 )
1105 sprintf ( compat_buf, fmt[i], host, bus, target, lun);
1106
1107 /* 4 == scsi/part 8 == ide/host/part */
1108 if( i == 4 || i == 8)
1109 sprintf ( compat_buf, fmt[i], host, bus, target, lun, atoi (ptr + 4) );
1110
1111 /* 5 == scsi/mt */
1112 if( i == 5)
1113 {
1114 rewind_ = info->devname[info->namelen - 1];
1115 if (rewind_ != 'n')
1116 rewind_ = '\0';
1117 mode=0;
1118 if(ptr[2] == 'l' /*108*/ || ptr[2] == 'm'/*109*/)
1119 mode = ptr[2] - 107; /* 1 or 2 */
1120 if(ptr[2] == 'a')
1121 mode = 3;
1122 sprintf (compat_buf, fmt [i], host, bus, target, lun, mode, rewind_);
1123 }
1124
1125 /* 9 == ide/host/mt */
1126 if( i == 9 )
1127 snprintf (compat_buf, sizeof (compat_buf), fmt[i], host, bus, target, lun, ptr + 2);
1128 /* esac */
1129 } /* switch(action) */
1130
1131 if(compat_name == NULL )
1132 return;
1133
1134#ifdef CONFIG_DEBUG
1135 msg_logger( NO_DIE, LOG_INFO, "action_compat(): %s\n", compat_name);
1136#endif
1137
1138 /* Now decide what to do with it */
1139 switch (action)
1140 {
1141 case AC_MKOLDCOMPAT:
1142 case AC_MKNEWCOMPAT:
1143 mksymlink (dest_name, compat_name);
1144 break;
1145 case AC_RMOLDCOMPAT:
1146 case AC_RMNEWCOMPAT:
1147#ifdef CONFIG_DEBUG
1148 if (unlink (compat_name) != 0)
1149 msg_logger( NO_DIE, LOG_ERR, "unlink(): %s: %m\n", compat_name);
1150#else
1151 unlink (compat_name);
1152#endif
1153 break;
1154 /*esac*/
1155 } /* switch(action) */
1156} /* End Function action_compat */
1157
1158static void restore(char *spath, struct stat source_stat, int rootlen)
1159{
1160 char dpath[STRING_LENGTH];
1161 struct stat dest_stat;
1162
1163#ifdef CONFIG_DEBUG
1164 msg_logger( NO_DIE, LOG_INFO, "restore()\n");
1165#endif
1166
1167 dest_stat.st_mode = 0;
1168 snprintf (dpath, sizeof dpath, "%s%s", mount_point, spath + rootlen);
1169 lstat (dpath, &dest_stat);
1170
1171 if ( S_ISLNK (source_stat.st_mode) || (source_stat.st_mode & S_ISVTX) )
1172 copy_inode (dpath, &dest_stat, (source_stat.st_mode & ~S_ISVTX) , spath, &source_stat);
1173
1174 if ( S_ISDIR (source_stat.st_mode) )
1175 dir_operation(RESTORE, spath, rootlen,NULL);
1176}
1177
1178
1179static int copy_inode (const char *destpath, const struct stat *dest_stat,
1180 mode_t new_mode,
1181 const char *sourcepath, const struct stat *source_stat)
1182/* [SUMMARY] Copy an inode.
1183 <destpath> The destination path. An existing inode may be deleted.
1184 <dest_stat> The destination stat(2) information.
1185 <new_mode> The desired new mode for the destination.
1186 <sourcepath> The source path.
1187 <source_stat> The source stat(2) information.
1188 [RETURNS] TRUE on success, else FALSE.
1189*/
1190{
1191 int source_len, dest_len;
1192 char source_link[STRING_LENGTH], dest_link[STRING_LENGTH];
1193 int fd, val;
1194 struct sockaddr_un un_addr;
1195 char symlink_val[STRING_LENGTH];
1196
1197#ifdef CONFIG_DEBUG
1198 msg_logger( NO_DIE, LOG_INFO, "copy_inode()\n");
1199#endif
1200
1201 if ( (source_stat->st_mode & S_IFMT) == (dest_stat->st_mode & S_IFMT) )
1202 {
1203 /* Same type */
1204 if ( S_ISLNK (source_stat->st_mode) )
1205 {
1206 if (( source_len = readlink (sourcepath, source_link, STRING_LENGTH - 1) ) < 0 ||
1207 ( dest_len = readlink (destpath , dest_link , STRING_LENGTH - 1) ) < 0 )
1208 return (FALSE);
1209 source_link[source_len] = '\0';
1210 dest_link[dest_len] = '\0';
1211 if ( (source_len != dest_len) || (strcmp (source_link, dest_link) != 0) )
1212 {
1213 unlink (destpath);
1214 symlink (source_link, destpath);
1215 }
1216 return (TRUE);
1217 } /* Else not a symlink */
1218 chmod (destpath, new_mode & ~S_IFMT);
1219 chown (destpath, source_stat->st_uid, source_stat->st_gid);
1220 return (TRUE);
1221 }
1222 /* Different types: unlink and create */
1223 unlink (destpath);
1224 switch (source_stat->st_mode & S_IFMT)
1225 {
1226 case S_IFSOCK:
1227 if ( ( fd = socket (AF_UNIX, SOCK_STREAM, 0) ) < 0 )
1228 break;
1229 un_addr.sun_family = AF_UNIX;
1230 snprintf (un_addr.sun_path, sizeof (un_addr.sun_path), "%s", destpath);
1231 val = bind (fd, (struct sockaddr *) &un_addr, (int) sizeof un_addr);
1232 close (fd);
1233 if (val != 0 || chmod (destpath, new_mode & ~S_IFMT) != 0)
1234 break;
1235 goto do_chown;
1236 case S_IFLNK:
1237 if ( ( val = readlink (sourcepath, symlink_val, STRING_LENGTH - 1) ) < 0 )
1238 break;
1239 symlink_val[val] = '\0';
1240 if (symlink (symlink_val, destpath) == 0)
1241 return (TRUE);
1242 break;
1243 case S_IFREG:
1244 if ( ( fd = open (destpath, O_RDONLY | O_CREAT, new_mode & ~S_IFMT) ) < 0 )
1245 break;
1246 close (fd);
1247 if (chmod (destpath, new_mode & ~S_IFMT) != 0)
1248 break;
1249 goto do_chown;
1250 case S_IFBLK:
1251 case S_IFCHR:
1252 case S_IFIFO:
1253 if (mknod (destpath, new_mode, source_stat->st_rdev) != 0)
1254 break;
1255 goto do_chown;
1256 case S_IFDIR:
1257 if (mkdir (destpath, new_mode & ~S_IFMT) != 0)
1258 break;
1259do_chown:
1260 if (chown (destpath, source_stat->st_uid, source_stat->st_gid) == 0)
1261 return (TRUE);
1262 /*break;*/
1263 }
1264 return (FALSE);
1265} /* End Function copy_inode */
1266
1267static void free_config ()
1268/* [SUMMARY] Free the configuration information.
1269 [RETURNS] Nothing.
1270*/
1271{
1272 struct config_entry_struct *c_entry;
1273 void *next;
1274
1275#ifdef CONFIG_DEBUG
1276 msg_logger( NO_DIE, LOG_INFO, "free_config()\n");
1277#endif
1278
1279 for (c_entry = first_config; c_entry != NULL; c_entry = next)
1280 {
1281 unsigned int count;
1282
1283 next = c_entry->next;
1284 regfree (&c_entry->preg);
1285 if (c_entry->action.what == AC_EXECUTE)
1286 {
1287 for (count = 0; count < MAX_ARGS; ++count)
1288 {
1289 if (c_entry->u.execute.argv[count] == NULL)
1290 break;
1291 free (c_entry->u.execute.argv[count]);
1292 }
1293 }
1294 free (c_entry);
1295 }
1296 first_config = NULL;
1297 last_config = NULL;
1298} /* End Function free_config */
1299
1300static int get_uid_gid (int flag, const char *string)
1301/* [SUMMARY] Convert a string to a UID or GID value.
1302 <flag> "UID" or "GID".
1303 <string> The string.
1304 [RETURNS] The UID or GID value.
1305*/
1306{
1307 struct passwd *pw_ent;
1308 struct group *grp_ent;
1309#ifdef CONFIG_DEVFSD_VERBOSE
1310 char * msg="user";
1311#endif
1312
1313#ifdef CONFIG_DEBUG
1314 msg_logger( NO_DIE, LOG_INFO, "get_uid_gid()\n");
1315
1316
1317 if(flag != UID && flag != GID )
1318 msg_logger( DIE, LOG_ERR,"get_uid_gid(): flag != UID && flag != GID\n");
1319#endif
1320
1321 if ( isdigit (string[0]) || ( (string[0] == '-') && isdigit (string[1]) ) )
1322 return atoi (string);
1323
1324 if ( flag == UID && ( pw_ent = getpwnam (string) ) != NULL )
1325 return (pw_ent->pw_uid);
1326
1327 if ( flag == GID && ( grp_ent = getgrnam (string) ) != NULL )
1328 return (grp_ent->gr_gid);
1329#ifdef CONFIG_DEVFSD_VERBOSE
1330 else
1331 msg="group";
1332
1333 msg_logger( NO_DIE, LOG_ERR,"unknown %s: %s, defaulting to %cID=0\n", msg, string, msg[0] - 32);
1334#endif
1335 return (0);
1336}/* End Function get_uid_gid */
1337
1338static mode_t get_mode (const char *string)
1339/* [SUMMARY] Convert a string to a mode value.
1340 <string> The string.
1341 [RETURNS] The mode value.
1342*/
1343{
1344 mode_t mode;
1345 int i;
1346#ifdef CONFIG_DEBUG
1347 msg_logger( NO_DIE, LOG_INFO, "get_mode()\n");
1348#endif
1349
1350 if ( isdigit (string[0]) )
1351 return strtoul (string, NULL, 8);
1352 if (strlen (string) != 9)
1353#ifdef CONFIG_DEVFSD_VERBOSE
1354 msg_logger( DIE, LOG_ERR, "bad mode: %s\n", string);
1355#else
1356 exit(EXIT_FAILURE);
1357#endif
1358 mode = 0;
1359 i= S_IRUSR;
1360 while(i>0)
1361 {
1362 if(string[0]=='r'||string[0]=='w'||string[0]=='x')
1363 mode+=i;
1364 i=i/2;
1365 string++;
1366 }
1367 return (mode);
1368} /* End Function get_mode */
1369
1370static void signal_handler (int sig)
1371{
1372#ifdef CONFIG_DEBUG
1373 msg_logger( NO_DIE, LOG_INFO, "signal_handler()\n");
1374#endif
1375
1376 caught_signal = TRUE;
1377 if (sig == SIGHUP)
1378 caught_sighup = TRUE;
1379#ifdef CONFIG_DEVFSD_VERBOSE
1380 msg_logger( NO_DIE, LOG_INFO, "Caught %s\n",(sig == SIGHUP)?"SIGHUP" : "SIGUSR1");
1381#endif
1382} /* End Function signal_handler */
1383
1384static const char *get_variable (const char *variable, void *info)
1385{
1386 struct get_variable_info *gv_info = info;
1387 static char hostname[STRING_LENGTH], sbuf[STRING_LENGTH];
1388 const char *field_names[] = { "hostname", "mntpt", "devpath", "devname",
1389 "uid", "gid", "mode", hostname, mount_point,
1390 gv_info->devpath, gv_info->devname, 0 };
1391 short int i;
1392#ifdef CONFIG_DEBUG
1393 msg_logger( NO_DIE, LOG_INFO, "get_variable()\n");
1394#endif
1395
1396 if (gethostname (hostname, STRING_LENGTH - 1) != 0)
1397#ifdef CONFIG_DEVFSD_VERBOSE
1398 msg_logger( DIE, LOG_ERR, "gethostname(): %m\n");
1399#else
1400 exit(EXIT_FAILURE);
1401#endif
1402 /* Here on error we should do exit(RV_SYS_ERROR), instead we do exit(EXIT_FAILURE) */
1403 hostname[STRING_LENGTH - 1] = '\0';
1404
1405 /* compare_string_array returns i>=0 */
1406 i=compare_string_array(field_names, variable);
1407
1408 if ( i > 6 && (i > 1 && gv_info == NULL))
1409 return (NULL);
1410 if( i >= 0 || i <= 3)
1411 {
1412#ifdef CONFIG_DEBUG
1413 msg_logger( NO_DIE, LOG_INFO, "get_variable(): i=%d %s\n",i ,field_names[i+7]);
1414#endif
1415 return(field_names[i+7]);
1416 }
1417
1418 if(i == 4 )
1419 sprintf (sbuf, "%u", gv_info->info->uid);
1420 else if(i == 5)
1421 sprintf (sbuf, "%u", gv_info->info->gid);
1422 else if(i == 6)
1423 sprintf (sbuf, "%o", gv_info->info->mode);
1424#ifdef CONFIG_DEBUG
1425 msg_logger( NO_DIE, LOG_INFO, "get_variable(): %s\n", sbuf);
1426#endif
1427 return (sbuf);
1428} /* End Function get_variable */
1429
1430static void service(struct stat statbuf, char *path)
1431{
1432 struct devfsd_notify_struct info;
1433
1434#ifdef CONFIG_DEBUG
1435 msg_logger( NO_DIE, LOG_INFO, "service()\n");
1436#endif
1437
1438 memset (&info, 0, sizeof info);
1439 info.type = DEVFSD_NOTIFY_REGISTERED;
1440 info.mode = statbuf.st_mode;
1441 info.major = major (statbuf.st_rdev);
1442 info.minor = minor (statbuf.st_rdev);
1443 info.uid = statbuf.st_uid;
1444 info.gid = statbuf.st_gid;
1445 snprintf (info.devname, sizeof (info.devname), "%s", path + strlen (mount_point) + 1);
1446 info.namelen = strlen (info.devname);
1447 service_name (&info);
1448 if ( S_ISDIR (statbuf.st_mode) )
1449 dir_operation(SERVICE,path,0,NULL);
1450}
1451
1452static void dir_operation(int type, const char * dir_name, int var, unsigned long *event_mask)
1453/* [SUMMARY] Scan a directory tree and generate register events on leaf nodes.
1454 <flag> To choose which function to perform
1455 <dp> The directory pointer. This is closed upon completion.
1456 <dir_name> The name of the directory.
1457 <rootlen> string length parameter.
1458 [RETURNS] Nothing.
1459*/
1460{
1461 struct stat statbuf;
1462 DIR *dp;
1463 struct dirent *de;
1464 char path[STRING_LENGTH];
1465
1466
1467#ifdef CONFIG_DEBUG
1468 msg_logger( NO_DIE, LOG_INFO, "dir_operation()\n");
1469#endif
1470
1471 if((dp = opendir( dir_name))==NULL)
1472 {
1473#ifdef CONFIG_DEBUG
1474 msg_logger( NO_DIE, LOG_ERR, "opendir(): %s: %m\n", dir_name);
1475#endif
1476 return;
1477 }
1478
1479 while ( (de = readdir (dp) ) != NULL )
1480 {
1481
1482 if(de->d_name && *de->d_name == '.' && (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])))
1483 continue;
1484 snprintf (path, sizeof (path), "%s/%s", dir_name, de->d_name);
1485#ifdef CONFIG_DEBUG
1486 msg_logger( NO_DIE, LOG_ERR, "dir_operation(): %s\n", path);
1487#endif
1488
1489 if (lstat (path, &statbuf) != 0)
1490 {
1491#ifdef CONFIG_DEBUG
1492 msg_logger( NO_DIE, LOG_ERR, "%s: %m\n", path);
1493#endif
1494 continue;
1495 }
1496 switch(type)
1497 {
1498 case SERVICE:
1499 service(statbuf,path);
1500 break;
1501 case RESTORE:
1502 restore(path, statbuf, var);
1503 break;
1504 case READ_CONFIG:
1505 read_config_file (path, var, event_mask);
1506 break;
1507 }
1508 }
1509 closedir (dp);
1510} /* End Function do_scan_and_service */
1511
1512static int mksymlink (const char *oldpath, const char *newpath)
1513/* [SUMMARY] Create a symlink, creating intervening directories as required.
1514 <oldpath> The string contained in the symlink.
1515 <newpath> The name of the new symlink.
1516 [RETURNS] 0 on success, else -1.
1517*/
1518{
1519#ifdef CONFIG_DEBUG
1520 msg_logger( NO_DIE, LOG_INFO, "mksymlink()\n", newpath);
1521#endif
1522
1523
1524 if ( !make_dir_tree (newpath) )
1525 return (-1);
1526
1527 if (symlink (oldpath, newpath) != 0)
1528 {
1529 if (errno != EEXIST)
1530 {
1531#ifdef CONFIG_DEBUG
1532 msg_logger( NO_DIE, LOG_ERR, "mksymlink(): %s to %s: %m\n", oldpath, newpath);
1533#endif
1534 return (-1);
1535 }
1536 }
1537 return (0);
1538} /* End Function mksymlink */
1539
1540
1541static int make_dir_tree (const char *path)
1542/* [SUMMARY] Creating intervening directories for a path as required.
1543 <path> The full pathname (including the leaf node).
1544 [RETURNS] TRUE on success, else FALSE.
1545*/
1546{
1547#ifdef CONFIG_DEBUG
1548 msg_logger( NO_DIE, LOG_INFO, "make_dir_tree()\n");
1549#endif
1550 if (bb_make_directory( dirname((char *)path), -1, FILEUTILS_RECUR )==-1)
1551 {
1552#ifdef CONFIG_DEBUG
1553 msg_logger( NO_DIE, LOG_ERR, "make_dir_tree(): %s: %m\n", path);
1554#endif
1555 return (FALSE);
1556 }
1557 return(TRUE);
1558} /* End Function make_dir_tree */
1559
1560static int expand_expression(char *output, unsigned int outsize,
1561 const char *input,
1562 const char *(*get_variable_func)(const char *variable, void *info),
1563 void *info,
1564 const char *devname,
1565 const regmatch_t *ex, unsigned int numexp)
1566/* [SUMMARY] Expand environment variables and regular subexpressions in string.
1567 <output> The output expanded expression is written here.
1568 <length> The size of the output buffer.
1569 <input> The input expression. This may equal <<output>>.
1570 <get_variable> A function which will be used to get variable values. If
1571 this returns NULL, the environment is searched instead. If this is NULL,
1572 only the environment is searched.
1573 <info> An arbitrary pointer passed to <<get_variable>>.
1574 <devname> Device name; specifically, this is the string that contains all
1575 of the regular subexpressions.
1576 <ex> Array of start / end offsets into info->devname for each subexpression
1577 <numexp> Number of regular subexpressions found in <<devname>>.
1578 [RETURNS] TRUE on success, else FALSE.
1579*/
1580{
1581 char temp[STRING_LENGTH];
1582
1583#ifdef CONFIG_DEBUG
1584 msg_logger( NO_DIE, LOG_INFO, "expand_expression()\n");
1585#endif
1586
1587 if ( !st_expr_expand (temp, STRING_LENGTH, input, get_variable_func, info) )
1588 return (FALSE);
1589 expand_regexp (output, outsize, temp, devname, ex, numexp);
1590 return (TRUE);
1591} /* End Function expand_expression */
1592
1593static void expand_regexp (char *output, size_t outsize, const char *input,
1594 const char *devname,
1595 const regmatch_t *ex, unsigned int numex )
1596/* [SUMMARY] Expand all occurrences of the regular subexpressions \0 to \9.
1597 <output> The output expanded expression is written here.
1598 <outsize> The size of the output buffer.
1599 <input> The input expression. This may NOT equal <<output>>, because
1600 supporting that would require yet another string-copy. However, it's not
1601 hard to write a simple wrapper function to add this functionality for those
1602 few cases that need it.
1603 <devname> Device name; specifically, this is the string that contains all
1604 of the regular subexpressions.
1605 <ex> An array of start and end offsets into <<devname>>, one for each
1606 subexpression
1607 <numex> Number of subexpressions in the offset-array <<ex>>.
1608 [RETURNS] Nothing.
1609*/
1610{
1611 const char last_exp = '0' - 1 + numex;
1612 int c = -1;
1613
1614#ifdef CONFIG_DEBUG
1615 msg_logger( NO_DIE, LOG_INFO, "expand_regexp()\n");
1616#endif
1617
1618 /* Guarantee NULL termination by writing an explicit '\0' character into
1619 the very last byte */
1620 if (outsize)
1621 output[--outsize] = '\0';
1622 /* Copy the input string into the output buffer, replacing '\\' with '\'
1623 and '\0' .. '\9' with subexpressions 0 .. 9, if they exist. Other \x
1624 codes are deleted */
1625 while ( (c != '\0') && (outsize != 0) )
1626 {
1627 c = *input;
1628 ++input;
1629 if (c == '\\')
1630 {
1631 c = *input;
1632 ++input;
1633 if (c != '\\')
1634 {
1635 if ((c >= '0') && (c <= last_exp))
1636 {
1637 const regmatch_t *subexp = ex + (c - '0');
1638 unsigned int sublen = subexp->rm_eo - subexp->rm_so;
1639
1640 /* Range checking */
1641 if (sublen > outsize)
1642 sublen = outsize;
1643 strncpy (output, devname + subexp->rm_so, sublen);
1644 output += sublen;
1645 outsize -= sublen;
1646 }
1647 continue;
1648 }
1649 }
1650 *output = c;
1651 ++output;
1652 --outsize;
1653 } /* while */
1654} /* End Function expand_regexp */
1655
1656
1657/* from compat_name.c */
1658
1659struct translate_struct
1660{
1661 char *match; /* The string to match to (up to length) */
1662 char *format; /* Format of output, "%s" takes data past match string,
1663 NULL is effectively "%s" (just more efficient) */
1664};
1665
1666static struct translate_struct translate_table[] =
1667{
1668 {"sound/", NULL},
1669 {"printers/", "lp%s"},
1670 {"v4l/", NULL},
1671 {"parports/", "parport%s"},
1672 {"fb/", "fb%s"},
1673 {"netlink/", NULL},
1674 {"loop/", "loop%s"},
1675 {"floppy/", "fd%s"},
1676 {"rd/", "ram%s"},
1677 {"md/", "md%s"}, /* Meta-devices */
1678 {"vc/", "tty%s"},
1679 {"misc/", NULL},
1680 {"isdn/", NULL},
1681 {"pg/", "pg%s"}, /* Parallel port generic ATAPI interface*/
1682 {"i2c/", "i2c-%s"},
1683 {"staliomem/", "staliomem%s"}, /* Stallion serial driver control */
1684 {"tts/E", "ttyE%s"}, /* Stallion serial driver */
1685 {"cua/E", "cue%s"}, /* Stallion serial driver callout */
1686 {"tts/R", "ttyR%s"}, /* Rocketport serial driver */
1687 {"cua/R", "cur%s"}, /* Rocketport serial driver callout */
1688 {"ip2/", "ip2%s"}, /* Computone serial driver control */
1689 {"tts/F", "ttyF%s"}, /* Computone serial driver */
1690 {"cua/F", "cuf%s"}, /* Computone serial driver callout */
1691 {"tts/C", "ttyC%s"}, /* Cyclades serial driver */
1692 {"cua/C", "cub%s"}, /* Cyclades serial driver callout */
1693 {"tts/", "ttyS%s"}, /* Generic serial: must be after others */
1694 {"cua/", "cua%s"}, /* Generic serial: must be after others */
1695 {"input/js", "js%s"}, /* Joystick driver */
1696 {NULL, NULL}
1697};
1698
1699const char *get_old_name (const char *devname, unsigned int namelen,
1700 char *buffer, unsigned int major, unsigned int minor)
1701/* [SUMMARY] Translate a kernel-supplied name into an old name.
1702 <devname> The device name provided by the kernel.
1703 <namelen> The length of the name.
1704 <buffer> A buffer that may be used. This should be at least 128 bytes long.
1705 <major> The major number for the device.
1706 <minor> The minor number for the device.
1707 [RETURNS] A pointer to the old name if known, else NULL.
1708*/
1709{
1710 const char *compat_name = NULL;
1711 char *ptr;
1712 struct translate_struct *trans;
1713 unsigned int i;
1714 char mode;
1715 int indexx;
1716 const char *pty1;
1717 const char *pty2;
1718 size_t len;
1719 /* 1 to 5 "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */
1720 const char *fmt[] = { NULL ,
1721 "sg%u", /* scsi/generic */
1722 NULL, /* scsi/disc */
1723 "sr%u", /* scsi/cd */
1724 NULL, /* scsi/part */
1725 "nst%u%c", /* scsi/mt */
1726 "hd%c" , /* ide/host/disc */
1727 "hd%c" , /* ide/host/cd */
1728 "hd%c%s", /* ide/host/part */
1729 "%sht%d", /* ide/host/mt */
1730 "sbpcd%u", /* sbp/ */
1731 "vcs%s", /* vcc/ */
1732 "%cty%c%c", /* pty/ */
1733 NULL };
1734
1735#ifdef CONFIG_DEBUG
1736 msg_logger( NO_DIE, LOG_INFO, "get_old_name()\n");
1737#endif
1738
1739 for (trans = translate_table; trans->match != NULL; ++trans)
1740 {
1741 len = strlen (trans->match);
1742
1743 if (strncmp (devname, trans->match, len) == 0)
1744 {
1745 if (trans->format == NULL)
1746 return (devname + len);
1747 sprintf (buffer, trans->format, devname + len);
1748 return (buffer);
1749 }
1750 }
1751
1752 ptr = (strrchr (devname, '/') + 1);
1753 i = scan_dev_name(devname, namelen, ptr);
1754
1755 if( i > 0 && i < 13)
1756 compat_name = buffer;
1757 else
1758 return NULL;
1759
1760#ifdef CONFIG_DEBUG
1761 msg_logger( NO_DIE, LOG_INFO, "get_old_name(): scan_dev_name() returned %d\n", i);
1762#endif
1763
1764 /* 1 == scsi/generic, 3 == scsi/cd, 10 == sbp/ */
1765 if( i == 1 || i == 3 || i == 10 )
1766 sprintf (buffer, fmt[i], minor);
1767
1768 /* 2 ==scsi/disc, 4 == scsi/part */
1769 if( i == 2 || i == 4)
1770 compat_name = write_old_sd_name (buffer, major, minor,((i == 2)?"":(ptr + 4)));
1771
1772 /* 5 == scsi/mt */
1773 if( i == 5)
1774 {
1775 mode = ptr[2];
1776 if (mode == 'n')
1777 mode = '\0';
1778 sprintf (buffer, fmt[i], minor & 0x1f, mode);
1779 if (devname[namelen - 1] != 'n')
1780 ++compat_name;
1781 }
1782 /* 6 == ide/host/disc, 7 == ide/host/cd, 8 == ide/host/part */
1783 if( i == 6 || i == 7 || i == 8 )
1784 sprintf (buffer, fmt[i] , get_old_ide_name (major, minor), ptr + 4); /* last arg should be ignored for i == 6 or i== 7 */
1785
1786 /* 9 == ide/host/mt */
1787 if( i == 9 )
1788 sprintf (buffer, fmt[i], ptr + 2, minor & 0x7f);
1789
1790 /* 11 == vcc/ */
1791 if( i == 11 )
1792 {
1793 sprintf (buffer, fmt[i], devname + 4);
1794 if (buffer[3] == '0')
1795 buffer[3] = '\0';
1796 }
1797 /* 12 == pty/ */
1798 if( i == 12 )
1799 {
1800 pty1 = "pqrstuvwxyzabcde";
1801 pty2 = "0123456789abcdef";
1802 indexx = atoi (devname + 5);
1803 sprintf (buffer, fmt[i], (devname[4] == 'm') ? 'p' : 't', pty1[indexx >> 4], pty2[indexx & 0x0f]);
1804 }
1805#ifdef CONFIG_DEBUG
1806 if(compat_name!=NULL)
1807 msg_logger( NO_DIE, LOG_INFO, "get_old_name(): compat_name %s\n", compat_name);
1808#endif
1809 return (compat_name);
1810} /* End Function get_old_name */
1811
1812static char get_old_ide_name (unsigned int major, unsigned int minor)
1813/* [SUMMARY] Get the old IDE name for a device.
1814 <major> The major number for the device.
1815 <minor> The minor number for the device.
1816 [RETURNS] The drive letter.
1817*/
1818{
1819 char letter='y'; /* 121 */
1820 char c='a'; /* 97 */
1821 int i=IDE0_MAJOR;
1822
1823#ifdef CONFIG_DEBUG
1824 msg_logger( NO_DIE, LOG_INFO, "get_old_ide_name()\n");
1825#endif
1826
1827 /* I hope it works like the previous code as it saves a few bytes. Tito ;P */
1828 do {
1829 if( i==IDE0_MAJOR || i==IDE1_MAJOR || i==IDE2_MAJOR ||
1830 i==IDE3_MAJOR || i==IDE4_MAJOR || i==IDE5_MAJOR ||
1831 i==IDE6_MAJOR || i==IDE7_MAJOR || i==IDE8_MAJOR ||
1832 i==IDE9_MAJOR )
1833 {
1834 if(i==major)
1835 {
1836 letter=c;
1837 break;
1838 }
1839 c+=2;
1840 }
1841 i++;
1842 } while(i<=IDE9_MAJOR);
1843
1844 if (minor > 63)
1845 ++letter;
1846 return (letter);
1847} /* End Function get_old_ide_name */
1848
1849static char *write_old_sd_name (char *buffer,
1850 unsigned int major, unsigned int minor,
1851 char *part)
1852/* [SUMMARY] Write the old SCSI disc name to a buffer.
1853 <buffer> The buffer to write to.
1854 <major> The major number for the device.
1855 <minor> The minor number for the device.
1856 <part> The partition string. Must be "" for a whole-disc entry.
1857 [RETURNS] A pointer to the buffer on success, else NULL.
1858*/
1859{
1860 unsigned int disc_index;
1861
1862#ifdef CONFIG_DEBUG
1863 msg_logger( NO_DIE, LOG_INFO, "write_old_sd_name()\n");
1864#endif
1865
1866 if (major == 8)
1867 {
1868 sprintf (buffer, "sd%c%s", 'a' + (minor >> 4), part);
1869 return (buffer);
1870 }
1871 if ( (major > 64) && (major < 72) )
1872 {
1873 disc_index = ( (major - 64) << 4 ) + (minor >> 4);
1874 if (disc_index < 26)
1875 sprintf (buffer, "sd%c%s", 'a' + disc_index, part);
1876 else
1877 sprintf (buffer, "sd%c%c%s", 'a' + (disc_index / 26) - 1, 'a' + disc_index % 26,part);
1878 return (buffer);
1879 }
1880 return (NULL);
1881} /* End Function write_old_sd_name */
1882
1883
1884/* expression.c */
1885
1886/*EXPERIMENTAL_FUNCTION*/
1887
1888int st_expr_expand (char *output, unsigned int length, const char *input,
1889 const char *(*get_variable_func) (const char *variable,
1890 void *info),
1891 void *info)
1892/* [SUMMARY] Expand an expression using Borne Shell-like unquoted rules.
1893 <output> The output expanded expression is written here.
1894 <length> The size of the output buffer.
1895 <input> The input expression. This may equal <<output>>.
1896 <get_variable> A function which will be used to get variable values. If
1897 this returns NULL, the environment is searched instead. If this is NULL,
1898 only the environment is searched.
1899 <info> An arbitrary pointer passed to <<get_variable>>.
1900 [RETURNS] TRUE on success, else FALSE.
1901*/
1902{
1903 char ch;
1904 unsigned int len;
1905 unsigned int out_pos = 0;
1906 const char *env;
1907 const char *ptr;
1908 struct passwd *pwent;
1909 char buffer[BUFFER_SIZE], tmp[STRING_LENGTH];
1910
1911#ifdef CONFIG_DEBUG
1912 msg_logger( NO_DIE, LOG_INFO, "st_expr_expand()\n");
1913#endif
1914
1915 if (length > BUFFER_SIZE)
1916 length = BUFFER_SIZE;
1917 for (; TRUE; ++input)
1918 {
1919 switch (ch = *input)
1920 {
1921 case '$':
1922 /* Variable expansion */
1923 input = expand_variable (buffer, length, &out_pos, ++input, get_variable_func, info);
1924 if (input == NULL)
1925 return (FALSE);
1926 break;
1927 case '~':
1928 /* Home directory expansion */
1929 ch = input[1];
1930 if ( isspace (ch) || (ch == '/') || (ch == '\0') )
1931 {
1932 /* User's own home directory: leave separator for next time */
1933 if ( ( env = getenv ("HOME") ) == NULL )
1934 {
1935#ifdef CONFIG_DEVFSD_VERBOSE
1936 msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, "HOME");
1937#endif
1938 return (FALSE);
1939 }
1940 len = strlen (env);
1941 if (len + out_pos >= length)
1942 goto st_expr_expand_out;
1943 memcpy (buffer + out_pos, env, len + 1);
1944 out_pos += len;
1945 continue;
1946 }
1947 /* Someone else's home directory */
1948 for (ptr = ++input; !isspace (ch) && (ch != '/') && (ch != '\0'); ch = *++ptr)
1949 /* VOID */ ;
1950 len = ptr - input;
1951 if (len >= sizeof tmp)
1952 goto st_expr_expand_out;
1953 safe_memcpy (tmp, input, len);
1954 input = ptr - 1;
1955 if ( ( pwent = getpwnam (tmp) ) == NULL )
1956 {
1957#ifdef CONFIG_DEVFSD_VERBOSE
1958 msg_logger( NO_DIE, LOG_INFO, "no pwent for: %s\n", tmp);
1959#endif
1960 return (FALSE);
1961 }
1962 len = strlen (pwent->pw_dir);
1963 if (len + out_pos >= length)
1964 goto st_expr_expand_out;
1965 memcpy (buffer + out_pos, pwent->pw_dir, len + 1);
1966 out_pos += len;
1967 break;
1968 case '\0':
1969 /* Falltrough */
1970 default:
1971 if (out_pos >= length)
1972 goto st_expr_expand_out;
1973 buffer[out_pos++] = ch;
1974 if (ch == '\0')
1975 {
1976 memcpy (output, buffer, out_pos);
1977 return (TRUE);
1978 }
1979 break;
1980 /* esac */
1981 }
1982 }
1983 return (FALSE);
1984st_expr_expand_out:
1985#ifdef CONFIG_DEVFSD_VERBOSE
1986 msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer);
1987#endif
1988 return (FALSE);
1989} /* End Function st_expr_expand */
1990
1991
1992/* Private functions follow */
1993
1994static const char *expand_variable (char *buffer, unsigned int length,
1995 unsigned int *out_pos, const char *input,
1996 const char *(*func) (const char *variable,
1997 void *info),
1998 void *info)
1999/* [SUMMARY] Expand a variable.
2000 <buffer> The buffer to write to.
2001 <length> The length of the output buffer.
2002 <out_pos> The current output position. This is updated.
2003 <input> A pointer to the input character pointer.
2004 <func> A function which will be used to get variable values. If this
2005 returns NULL, the environment is searched instead. If this is NULL, only
2006 the environment is searched.
2007 <info> An arbitrary pointer passed to <<func>>.
2008 <errfp> Diagnostic messages are written here.
2009 [RETURNS] A pointer to the end of this subexpression on success, else NULL.
2010*/
2011{
2012 char ch;
2013 int len;
2014 unsigned int open_braces;
2015 const char *env, *ptr;
2016 char tmp[STRING_LENGTH];
2017
2018#ifdef CONFIG_DEBUG
2019 msg_logger( NO_DIE, LOG_INFO, "expand_variable()\n");
2020#endif
2021
2022 ch = input[0];
2023 if (ch == '$')
2024 {
2025 /* Special case for "$$": PID */
2026 sprintf ( tmp, "%d", (int) getpid () );
2027 len = strlen (tmp);
2028 if (len + *out_pos >= length)
2029 goto expand_variable_out;
2030
2031 memcpy (buffer + *out_pos, tmp, len + 1);
2032 out_pos += len;
2033 return (input);
2034 }
2035 /* Ordinary variable expansion, possibly in braces */
2036 if (ch != '{')
2037 {
2038 /* Simple variable expansion */
2039 for (ptr = input; isalnum (ch) || (ch == '_') || (ch == ':');ch = *++ptr)
2040 /* VOID */ ;
2041 len = ptr - input;
2042 if (len >= sizeof tmp)
2043 goto expand_variable_out;
2044
2045 safe_memcpy (tmp, input, len);
2046 input = ptr - 1;
2047 if ( ( env = get_variable_v2 (tmp, func, info) ) == NULL )
2048 {
2049#ifdef CONFIG_DEVFSD_VERBOSE
2050 msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, tmp);
2051#endif
2052 return (NULL);
2053 }
2054 len = strlen (env);
2055 if (len + *out_pos >= length)
2056 goto expand_variable_out;
2057
2058 memcpy (buffer + *out_pos, env, len + 1);
2059 *out_pos += len;
2060 return (input);
2061 }
2062 /* Variable in braces: check for ':' tricks */
2063 ch = *++input;
2064 for (ptr = input; isalnum (ch) || (ch == '_'); ch = *++ptr)
2065 /* VOID */;
2066 if (ch == '}')
2067 {
2068 /* Must be simple variable expansion with "${var}" */
2069 len = ptr - input;
2070 if (len >= sizeof tmp)
2071 goto expand_variable_out;
2072
2073 safe_memcpy (tmp, input, len);
2074 ptr = expand_variable (buffer, length, out_pos, tmp, func, info );
2075 if (ptr == NULL)
2076 return (NULL);
2077 return (input + len);
2078 }
2079 if (ch != ':' || ptr[1] != '-' )
2080 {
2081#ifdef CONFIG_DEVFSD_VERBOSE
2082 msg_logger( NO_DIE, LOG_INFO,"illegal char in var name\n");
2083#endif
2084 return (NULL);
2085 }
2086 /* It's that handy "${var:-word}" expression. Check if var is defined */
2087 len = ptr - input;
2088 if (len >= sizeof tmp)
2089 goto expand_variable_out;
2090
2091 safe_memcpy (tmp, input, len);
2092 /* Move input pointer to ':' */
2093 input = ptr;
2094 /* First skip to closing brace, taking note of nested expressions */
2095 ptr += 2;
2096 ch = ptr[0];
2097 for (open_braces = 1; open_braces > 0; ch = *++ptr)
2098 {
2099 switch (ch)
2100 {
2101 case '{':
2102 ++open_braces;
2103 break;
2104 case '}':
2105 --open_braces;
2106 break;
2107 case '\0':
2108#ifdef CONFIG_DEVFSD_VERBOSE
2109 msg_logger( NO_DIE, LOG_INFO,"\"}\" not found in: %s\n", input);
2110#endif
2111 return (NULL);
2112 default:
2113 break;
2114 }
2115 }
2116 --ptr;
2117 /* At this point ptr should point to closing brace of "${var:-word}" */
2118 if ( ( env = get_variable_v2 (tmp, func, info) ) != NULL )
2119 {
2120 /* Found environment variable, so skip the input to the closing brace
2121 and return the variable */
2122 input = ptr;
2123 len = strlen (env);
2124 if (len + *out_pos >= length)
2125 goto expand_variable_out;
2126
2127 memcpy (buffer + *out_pos, env, len + 1);
2128 *out_pos += len;
2129 return (input);
2130 }
2131 /* Environment variable was not found, so process word. Advance input
2132 pointer to start of word in "${var:-word}" */
2133 input += 2;
2134 len = ptr - input;
2135 if (len >= sizeof tmp)
2136 goto expand_variable_out;
2137
2138 safe_memcpy (tmp, input, len);
2139 input = ptr;
2140 if ( !st_expr_expand (tmp, STRING_LENGTH, tmp, func, info ) )
2141 return (NULL);
2142 len = strlen (tmp);
2143 if (len + *out_pos >= length)
2144 goto expand_variable_out;
2145
2146 memcpy (buffer + *out_pos, tmp, len + 1);
2147 *out_pos += len;
2148 return (input);
2149expand_variable_out:
2150#ifdef CONFIG_DEVFSD_VERBOSE
2151 msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer);
2152#endif
2153 return (NULL);
2154} /* End Function expand_variable */
2155
2156
2157static const char *get_variable_v2 (const char *variable,
2158 const char *(*func) (const char *variable, void *info),
2159 void *info)
2160/* [SUMMARY] Get a variable from the environment or .
2161 <variable> The variable name.
2162 <func> A function which will be used to get the variable. If this returns
2163 NULL, the environment is searched instead. If this is NULL, only the
2164 environment is searched.
2165 [RETURNS] The value of the variable on success, else NULL.
2166*/
2167{
2168 const char *value;
2169
2170#ifdef CONFIG_DEBUG
2171 msg_logger( NO_DIE, LOG_INFO, "get_variable_v2()\n");
2172#endif
2173
2174 if (func != NULL)
2175 {
2176 value = (*func) (variable, info);
2177 if (value != NULL)
2178 return (value);
2179 }
2180 return getenv (variable);
2181} /* End Function get_variable */
2182
2183/* 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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * hdparm implementation for busybox
4 *
5 *
6 * Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it>
7 *
8 * Hacked by Tito <farmatito@tiscali.it> for size optimization.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23 * 02111-1307 USA
24 *
25 *
26 * This program is based on the source code of hdparm: see below...
27 * hdparm.c - Command line interface to get/set hard disk parameters
28 * - by Mark Lord (C) 1994-2002 -- freely distributable
29 */
30
31#include <unistd.h>
32#include <string.h>
33#include <stdlib.h>
34#include <fcntl.h>
35#include <errno.h>
36#include <ctype.h>
37#include <endian.h>
38#include <sys/ioctl.h>
39#include <sys/shm.h>
40#include <sys/sysmacros.h>
41#include <sys/time.h>
42#include <sys/times.h>
43#include <sys/mount.h>
44#include "busybox.h"
45#include <linux/types.h>
46#include <linux/hdreg.h>
47#include <linux/major.h>
48#include <asm/byteorder.h>
49
50
51#if (__BYTE_ORDER == __BIG_ENDIAN) && !defined(__USE_XOPEN)
52#define __USE_XOPEN
53#endif
54
55/* device types */
56/* ------------ */
57#define NO_DEV 0xffff
58#define ATA_DEV 0x0000
59#define ATAPI_DEV 0x0001
60
61/* word definitions */
62/* ---------------- */
63#define GEN_CONFIG 0 /* general configuration */
64#define LCYLS 1 /* number of logical cylinders */
65#define CONFIG 2 /* specific configuration */
66#define LHEADS 3 /* number of logical heads */
67#define TRACK_BYTES 4 /* number of bytes/track (ATA-1) */
68#define SECT_BYTES 5 /* number of bytes/sector (ATA-1) */
69#define LSECTS 6 /* number of logical sectors/track */
70#define START_SERIAL 10 /* ASCII serial number */
71#define LENGTH_SERIAL 10 /* 10 words (20 bytes or characters) */
72#define BUF_TYPE 20 /* buffer type (ATA-1) */
73#define BUFFER__SIZE 21 /* buffer size (ATA-1) */
74#define RW_LONG 22 /* extra bytes in R/W LONG cmd ( < ATA-4)*/
75#define START_FW_REV 23 /* ASCII firmware revision */
76#define LENGTH_FW_REV 4 /* 4 words (8 bytes or characters) */
77#define START_MODEL 27 /* ASCII model number */
78#define LENGTH_MODEL 20 /* 20 words (40 bytes or characters) */
79#define SECTOR_XFER_MAX 47 /* r/w multiple: max sectors xfered */
80#define DWORD_IO 48 /* can do double-word IO (ATA-1 only) */
81#define CAPAB_0 49 /* capabilities */
82#define CAPAB_1 50
83#define PIO_MODE 51 /* max PIO mode supported (obsolete)*/
84#define DMA_MODE 52 /* max Singleword DMA mode supported (obs)*/
85#define WHATS_VALID 53 /* what fields are valid */
86#define LCYLS_CUR 54 /* current logical cylinders */
87#define LHEADS_CUR 55 /* current logical heads */
88#define LSECTS_CUR 56 /* current logical sectors/track */
89#define CAPACITY_LSB 57 /* current capacity in sectors */
90#define CAPACITY_MSB 58
91#define SECTOR_XFER_CUR 59 /* r/w multiple: current sectors xfered */
92#define LBA_SECTS_LSB 60 /* LBA: total number of user */
93#define LBA_SECTS_MSB 61 /* addressable sectors */
94#define SINGLE_DMA 62 /* singleword DMA modes */
95#define MULTI_DMA 63 /* multiword DMA modes */
96#define ADV_PIO_MODES 64 /* advanced PIO modes supported */
97 /* multiword DMA xfer cycle time: */
98#define DMA_TIME_MIN 65 /* - minimum */
99#define DMA_TIME_NORM 66 /* - manufacturer's recommended */
100 /* minimum PIO xfer cycle time: */
101#define PIO_NO_FLOW 67 /* - without flow control */
102#define PIO_FLOW 68 /* - with IORDY flow control */
103#define PKT_REL 71 /* typical #ns from PKT cmd to bus rel */
104#define SVC_NBSY 72 /* typical #ns from SERVICE cmd to !BSY */
105#define CDR_MAJOR 73 /* CD ROM: major version number */
106#define CDR_MINOR 74 /* CD ROM: minor version number */
107#define QUEUE_DEPTH 75 /* queue depth */
108#define MAJOR 80 /* major version number */
109#define MINOR 81 /* minor version number */
110#define CMDS_SUPP_0 82 /* command/feature set(s) supported */
111#define CMDS_SUPP_1 83
112#define CMDS_SUPP_2 84
113#define CMDS_EN_0 85 /* command/feature set(s) enabled */
114#define CMDS_EN_1 86
115#define CMDS_EN_2 87
116#define ULTRA_DMA 88 /* ultra DMA modes */
117 /* time to complete security erase */
118#define ERASE_TIME 89 /* - ordinary */
119#define ENH_ERASE_TIME 90 /* - enhanced */
120#define ADV_PWR 91 /* current advanced power management level
121 in low byte, 0x40 in high byte. */
122#define PSWD_CODE 92 /* master password revision code */
123#define HWRST_RSLT 93 /* hardware reset result */
124#define ACOUSTIC 94 /* acoustic mgmt values ( >= ATA-6) */
125#define LBA_LSB 100 /* LBA: maximum. Currently only 48 */
126#define LBA_MID 101 /* bits are used, but addr 103 */
127#define LBA_48_MSB 102 /* has been reserved for LBA in */
128#define LBA_64_MSB 103 /* the future. */
129#define RM_STAT 127 /* removable media status notification feature set support */
130#define SECU_STATUS 128 /* security status */
131#define CFA_PWR_MODE 160 /* CFA power mode 1 */
132#define START_MEDIA 176 /* media serial number */
133#define LENGTH_MEDIA 20 /* 20 words (40 bytes or characters)*/
134#define START_MANUF 196 /* media manufacturer I.D. */
135#define LENGTH_MANUF 10 /* 10 words (20 bytes or characters) */
136#define INTEGRITY 255 /* integrity word */
137
138/* bit definitions within the words */
139/* -------------------------------- */
140
141/* many words are considered valid if bit 15 is 0 and bit 14 is 1 */
142#define VALID 0xc000
143#define VALID_VAL 0x4000
144/* many words are considered invalid if they are either all-0 or all-1 */
145#define NOVAL_0 0x0000
146#define NOVAL_1 0xffff
147
148/* word 0: gen_config */
149#define NOT_ATA 0x8000
150#define NOT_ATAPI 0x4000 /* (check only if bit 15 == 1) */
151#define MEDIA_REMOVABLE 0x0080
152#define DRIVE_NOT_REMOVABLE 0x0040 /* bit obsoleted in ATA 6 */
153#define INCOMPLETE 0x0004
154#define CFA_SUPPORT_VAL 0x848a /* 848a=CFA feature set support */
155#define DRQ_RESPONSE_TIME 0x0060
156#define DRQ_3MS_VAL 0x0000
157#define DRQ_INTR_VAL 0x0020
158#define DRQ_50US_VAL 0x0040
159#define PKT_SIZE_SUPPORTED 0x0003
160#define PKT_SIZE_12_VAL 0x0000
161#define PKT_SIZE_16_VAL 0x0001
162#define EQPT_TYPE 0x1f00
163#define SHIFT_EQPT 8
164
165#define CDROM 0x0005
166
167#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
168static const char *pkt_str[] = {
169 "Direct-access device", /* word 0, bits 12-8 = 00 */
170 "Sequential-access device", /* word 0, bits 12-8 = 01 */
171 "Printer", /* word 0, bits 12-8 = 02 */
172 "Processor", /* word 0, bits 12-8 = 03 */
173 "Write-once device", /* word 0, bits 12-8 = 04 */
174 "CD-ROM", /* word 0, bits 12-8 = 05 */
175 "Scanner", /* word 0, bits 12-8 = 06 */
176 "Optical memory", /* word 0, bits 12-8 = 07 */
177 "Medium changer", /* word 0, bits 12-8 = 08 */
178 "Communications device", /* word 0, bits 12-8 = 09 */
179 "ACS-IT8 device", /* word 0, bits 12-8 = 0a */
180 "ACS-IT8 device", /* word 0, bits 12-8 = 0b */
181 "Array controller", /* word 0, bits 12-8 = 0c */
182 "Enclosure services", /* word 0, bits 12-8 = 0d */
183 "Reduced block command device", /* word 0, bits 12-8 = 0e */
184 "Optical card reader/writer", /* word 0, bits 12-8 = 0f */
185 "", /* word 0, bits 12-8 = 10 */
186 "", /* word 0, bits 12-8 = 11 */
187 "", /* word 0, bits 12-8 = 12 */
188 "", /* word 0, bits 12-8 = 13 */
189 "", /* word 0, bits 12-8 = 14 */
190 "", /* word 0, bits 12-8 = 15 */
191 "", /* word 0, bits 12-8 = 16 */
192 "", /* word 0, bits 12-8 = 17 */
193 "", /* word 0, bits 12-8 = 18 */
194 "", /* word 0, bits 12-8 = 19 */
195 "", /* word 0, bits 12-8 = 1a */
196 "", /* word 0, bits 12-8 = 1b */
197 "", /* word 0, bits 12-8 = 1c */
198 "", /* word 0, bits 12-8 = 1d */
199 "", /* word 0, bits 12-8 = 1e */
200 "Unknown", /* word 0, bits 12-8 = 1f */
201};
202static const char *ata1_cfg_str[] = { /* word 0 in ATA-1 mode */
203 "reserved", /* bit 0 */
204 "hard sectored", /* bit 1 */
205 "soft sectored", /* bit 2 */
206 "not MFM encoded ", /* bit 3 */
207 "head switch time > 15us", /* bit 4 */
208 "spindle motor control option", /* bit 5 */
209 "fixed drive", /* bit 6 */
210 "removable drive", /* bit 7 */
211 "disk xfer rate <= 5Mbs", /* bit 8 */
212 "disk xfer rate > 5Mbs, <= 10Mbs", /* bit 9 */
213 "disk xfer rate > 5Mbs", /* bit 10 */
214 "rotational speed tol.", /* bit 11 */
215 "data strobe offset option", /* bit 12 */
216 "track offset option", /* bit 13 */
217 "format speed tolerance gap reqd", /* bit 14 */
218 "ATAPI" /* bit 14 */
219};
220#endif
221
222/* word 1: number of logical cylinders */
223#define LCYLS_MAX 0x3fff /* maximum allowable value */
224
225/* word 2: specific configuration
226 * (a) require SET FEATURES to spin-up
227 * (b) require spin-up to fully reply to IDENTIFY DEVICE
228 */
229#define STBY_NID_VAL 0x37c8 /* (a) and (b) */
230#define STBY_ID_VAL 0x738c /* (a) and not (b) */
231#define PWRD_NID_VAL 0x8c73 /* not (a) and (b) */
232#define PWRD_ID_VAL 0xc837 /* not (a) and not (b) */
233
234/* words 47 & 59: sector_xfer_max & sector_xfer_cur */
235#define SECTOR_XFER 0x00ff /* sectors xfered on r/w multiple cmds*/
236#define MULTIPLE_SETTING_VALID 0x0100 /* 1=multiple sector setting is valid */
237
238/* word 49: capabilities 0 */
239#define STD_STBY 0x2000 /* 1=standard values supported (ATA);
240 0=vendor specific values */
241#define IORDY_SUP 0x0800 /* 1=support; 0=may be supported */
242#define IORDY_OFF 0x0400 /* 1=may be disabled */
243#define LBA_SUP 0x0200 /* 1=Logical Block Address support */
244#define DMA_SUP 0x0100 /* 1=Direct Memory Access support */
245#define DMA_IL_SUP 0x8000 /* 1=interleaved DMA support (ATAPI) */
246#define CMD_Q_SUP 0x4000 /* 1=command queuing support (ATAPI) */
247#define OVLP_SUP 0x2000 /* 1=overlap operation support (ATAPI) */
248#define SWRST_REQ 0x1000 /* 1=ATA SW reset required (ATAPI, obsolete */
249
250/* word 50: capabilities 1 */
251#define MIN_STANDBY_TIMER 0x0001 /* 1=device specific standby timer value minimum */
252
253/* words 51 & 52: PIO & DMA cycle times */
254#define MODE 0xff00 /* the mode is in the MSBs */
255
256/* word 53: whats_valid */
257#define OK_W88 0x0004 /* the ultra_dma info is valid */
258#define OK_W64_70 0x0002 /* see above for word descriptions */
259#define OK_W54_58 0x0001 /* current cyl, head, sector, cap. info valid */
260
261/*word 63,88: dma_mode, ultra_dma_mode*/
262#define MODE_MAX 7 /* bit definitions force udma <=7 (when
263 * udma >=8 comes out it'll have to be
264 * defined in a new dma_mode word!) */
265
266/* word 64: PIO transfer modes */
267#define PIO_SUP 0x00ff /* only bits 0 & 1 are used so far, */
268#define PIO_MODE_MAX 8 /* but all 8 bits are defined */
269
270/* word 75: queue_depth */
271#define DEPTH_BITS 0x001f /* bits used for queue depth */
272
273/* words 80-81: version numbers */
274/* NOVAL_0 or NOVAL_1 means device does not report version */
275
276/* word 81: minor version number */
277#define MINOR_MAX 0x1C
278#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
279static const char *minor_str[] = { /* word 81 value: */
280 "device does not report version", /* 0x0000 */
281 "ATA-1 X3T9.2 781D prior to revision 4", /* 0x0001 */
282 "ATA-1 published, ANSI X3.221-1994", /* 0x0002 */
283 "ATA-1 X3T9.2 781D revision 4", /* 0x0003 */
284 "ATA-2 published, ANSI X3.279-1996", /* 0x0004 */
285 "ATA-2 X3T10 948D prior to revision 2k", /* 0x0005 */
286 "ATA-3 X3T10 2008D revision 1", /* 0x0006 */
287 "ATA-2 X3T10 948D revision 2k", /* 0x0007 */
288 "ATA-3 X3T10 2008D revision 0", /* 0x0008 */
289 "ATA-2 X3T10 948D revision 3", /* 0x0009 */
290 "ATA-3 published, ANSI X3.298-199x", /* 0x000a */
291 "ATA-3 X3T10 2008D revision 6", /* 0x000b */
292 "ATA-3 X3T13 2008D revision 7 and 7a", /* 0x000c */
293 "ATA/ATAPI-4 X3T13 1153D revision 6", /* 0x000d */
294 "ATA/ATAPI-4 T13 1153D revision 13", /* 0x000e */
295 "ATA/ATAPI-4 X3T13 1153D revision 7", /* 0x000f */
296 "ATA/ATAPI-4 T13 1153D revision 18", /* 0x0010 */
297 "ATA/ATAPI-4 T13 1153D revision 15", /* 0x0011 */
298 "ATA/ATAPI-4 published, ANSI NCITS 317-1998", /* 0x0012 */
299 "ATA/ATAPI-5 T13 1321D revision 3",
300 "ATA/ATAPI-4 T13 1153D revision 14", /* 0x0014 */
301 "ATA/ATAPI-5 T13 1321D revision 1", /* 0x0015 */
302 "ATA/ATAPI-5 published, ANSI NCITS 340-2000", /* 0x0016 */
303 "ATA/ATAPI-4 T13 1153D revision 17", /* 0x0017 */
304 "ATA/ATAPI-6 T13 1410D revision 0", /* 0x0018 */
305 "ATA/ATAPI-6 T13 1410D revision 3a", /* 0x0019 */
306 "Reserved", /* 0x001a */
307 "ATA/ATAPI-6 T13 1410D revision 2", /* 0x001b */
308 "ATA/ATAPI-6 T13 1410D revision 1", /* 0x001c */
309 "reserved" /* 0x001d */
310 "reserved" /* 0x001e */
311 "reserved" /* 0x001f-0xfffe*/
312};
313#endif
314static const char actual_ver[] = {
315 /* word 81 value: */
316 0, /* 0x0000 WARNING: */
317 1, /* 0x0001 WARNING: */
318 1, /* 0x0002 WARNING: */
319 1, /* 0x0003 WARNING: */
320 2, /* 0x0004 WARNING: This array */
321 2, /* 0x0005 WARNING: corresponds */
322 3, /* 0x0006 WARNING: *exactly* */
323 2, /* 0x0007 WARNING: to the ATA/ */
324 3, /* 0x0008 WARNING: ATAPI version */
325 2, /* 0x0009 WARNING: listed in */
326 3, /* 0x000a WARNING: the */
327 3, /* 0x000b WARNING: minor_str */
328 3, /* 0x000c WARNING: array */
329 4, /* 0x000d WARNING: above. */
330 4, /* 0x000e WARNING: */
331 4, /* 0x000f WARNING: if you change */
332 4, /* 0x0010 WARNING: that one, */
333 4, /* 0x0011 WARNING: change this one */
334 4, /* 0x0012 WARNING: too!!! */
335 5, /* 0x0013 WARNING: */
336 4, /* 0x0014 WARNING: */
337 5, /* 0x0015 WARNING: */
338 5, /* 0x0016 WARNING: */
339 4, /* 0x0017 WARNING: */
340 6, /* 0x0018 WARNING: */
341 6, /* 0x0019 WARNING: */
342 0, /* 0x001a WARNING: */
343 6, /* 0x001b WARNING: */
344 6, /* 0x001c WARNING: */
345 0 /* 0x001d-0xfffe */
346};
347
348/* words 82-84: cmds/feats supported */
349#define CMDS_W82 0x77ff /* word 82: defined command locations*/
350#define CMDS_W83 0x3fff /* word 83: defined command locations*/
351#define CMDS_W84 0x002f /* word 83: defined command locations*/
352#define SUPPORT_48_BIT 0x0400
353#define NUM_CMD_FEAT_STR 48
354
355#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
356static const char *cmd_feat_str[] = {
357 "", /* word 82 bit 15: obsolete */
358 "NOP cmd", /* word 82 bit 14 */
359 "READ BUFFER cmd", /* word 82 bit 13 */
360 "WRITE BUFFER cmd", /* word 82 bit 12 */
361 "", /* word 82 bit 11: obsolete */
362 "Host Protected Area feature set", /* word 82 bit 10 */
363 "DEVICE RESET cmd", /* word 82 bit 9 */
364 "SERVICE interrupt", /* word 82 bit 8 */
365 "Release interrupt", /* word 82 bit 7 */
366 "Look-ahead", /* word 82 bit 6 */
367 "Write cache", /* word 82 bit 5 */
368 "PACKET command feature set", /* word 82 bit 4 */
369 "Power Management feature set", /* word 82 bit 3 */
370 "Removable Media feature set", /* word 82 bit 2 */
371 "Security Mode feature set", /* word 82 bit 1 */
372 "SMART feature set", /* word 82 bit 0 */
373 /* --------------*/
374 "", /* word 83 bit 15: !valid bit */
375 "", /* word 83 bit 14: valid bit */
376 "FLUSH CACHE EXT command", /* word 83 bit 13 */
377 "Mandatory FLUSH CACHE command ", /* word 83 bit 12 */
378 "Device Configuration Overlay feature set ",
379 "48-bit Address feature set ", /* word 83 bit 10 */
380 "",
381 "SET MAX security extension", /* word 83 bit 8 */
382 "Address Offset Reserved Area Boot", /* word 83 bit 7 */
383 "SET FEATURES subcommand required to spinup after power up",
384 "Power-Up In Standby feature set", /* word 83 bit 5 */
385 "Removable Media Status Notification feature set",
386 "Adv. Power Management feature set",/* word 83 bit 3 */
387 "CFA feature set", /* word 83 bit 2 */
388 "READ/WRITE DMA QUEUED", /* word 83 bit 1 */
389 "DOWNLOAD MICROCODE cmd", /* word 83 bit 0 */
390 /* --------------*/
391 "", /* word 84 bit 15: !valid bit */
392 "", /* word 84 bit 14: valid bit */
393 "", /* word 84 bit 13: reserved */
394 "", /* word 84 bit 12: reserved */
395 "", /* word 84 bit 11: reserved */
396 "", /* word 84 bit 10: reserved */
397 "", /* word 84 bit 9: reserved */
398 "", /* word 84 bit 8: reserved */
399 "", /* word 84 bit 7: reserved */
400 "", /* word 84 bit 6: reserved */
401 "General Purpose Logging feature set", /* word 84 bit 5 */
402 "", /* word 84 bit 4: reserved */
403 "Media Card Pass Through Command feature set ",
404 "Media serial number ", /* word 84 bit 2 */
405 "SMART self-test ", /* word 84 bit 1 */
406 "SMART error logging " /* word 84 bit 0 */
407};
408#endif
409
410
411/* words 85-87: cmds/feats enabled */
412/* use cmd_feat_str[] to display what commands and features have
413 * been enabled with words 85-87
414 */
415
416/* words 89, 90, SECU ERASE TIME */
417#define ERASE_BITS 0x00ff
418
419/* word 92: master password revision */
420/* NOVAL_0 or NOVAL_1 means no support for master password revision */
421
422/* word 93: hw reset result */
423#define CBLID 0x2000 /* CBLID status */
424#define RST0 0x0001 /* 1=reset to device #0 */
425#define DEV_DET 0x0006 /* how device num determined */
426#define JUMPER_VAL 0x0002 /* device num determined by jumper */
427#define CSEL_VAL 0x0004 /* device num determined by CSEL_VAL */
428
429/* word 127: removable media status notification feature set support */
430#define RM_STAT_BITS 0x0003
431#define RM_STAT_SUP 0x0001
432
433/* word 128: security */
434#define SECU_ENABLED 0x0002
435#define SECU_LEVEL 0x0010
436#define NUM_SECU_STR 6
437#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
438static const char *secu_str[] = {
439 "supported", /* word 128, bit 0 */
440 "enabled", /* word 128, bit 1 */
441 "locked", /* word 128, bit 2 */
442 "frozen", /* word 128, bit 3 */
443 "expired: security count", /* word 128, bit 4 */
444 "supported: enhanced erase" /* word 128, bit 5 */
445};
446#endif
447
448/* word 160: CFA power mode */
449#define VALID_W160 0x8000 /* 1=word valid */
450#define PWR_MODE_REQ 0x2000 /* 1=CFA power mode req'd by some cmds*/
451#define PWR_MODE_OFF 0x1000 /* 1=CFA power moded disabled */
452#define MAX_AMPS 0x0fff /* value = max current in ma */
453
454/* word 255: integrity */
455#define SIG 0x00ff /* signature location */
456#define SIG_VAL 0x00A5 /* signature value */
457
458#define VERSION "v5.4"
459
460#define TIMING_MB 64
461#define TIMING_BUF_MB 1
462#define TIMING_BUF_BYTES (TIMING_BUF_MB * 1024 * 1024)
463#define TIMING_BUF_COUNT (timing_MB / TIMING_BUF_MB)
464#define BUFCACHE_FACTOR 2
465
466#undef DO_FLUSHCACHE /* under construction: force cache flush on -W0 */
467
468/* Busybox messages and functions */
469
470const char * const bb_msg_shared_mem ="could not %s sharedmem buf";
471const char * const bb_msg_op_not_supp =" operation not supported on %s disks";
472
473static void bb_ioctl(int fd, int request, void *argp, const char *string)
474{
475 if (ioctl (fd, request, argp) != 0)
476 bb_perror_msg(" %s", string);
477}
478
479static void if_printf(unsigned long i, char *fmt, ... )
480{
481 va_list ap;
482 va_start(ap, fmt);
483 if (i)
484 vprintf(fmt, ap);
485 va_end(ap);
486}
487
488static void on_off (unsigned int value);
489
490static void if_printf_on_off(unsigned long get_arg,const char *fmt, unsigned long arg)
491{
492 if (get_arg)
493 {
494 printf(fmt, arg);
495 on_off(arg);
496 }
497}
498
499static void bb_ioctl_on_off(int fd, int request, void *argp, const char *string,
500 const char * fmt)
501{
502 if (ioctl (fd, request, &argp) != 0)
503 bb_perror_msg(" %s", string);
504 else
505 {
506 printf(fmt, (unsigned long) argp);
507 on_off((unsigned long) argp);
508 }
509}
510
511#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
512static void if_else_printf(unsigned long i, char *fmt1, char *fmt2, ... )
513{
514 va_list ap;
515 va_start(ap, fmt2);
516 if (i)
517 vprintf(fmt1, ap);
518 else
519 vprintf(fmt2, ap);
520 va_end(ap);
521}
522
523static void print_ascii(uint16_t *p, uint8_t length);
524
525static void xprint_ascii(uint16_t *val ,int i, char * string, int n)
526{
527 if(val[i])
528 {
529 printf("\t%-20s",string);
530 print_ascii(&val[i], n);
531 }
532}
533
534static void if_strcat(unsigned long test, char *modes, char *string)
535{
536 if (test)
537 strcat(modes,string);
538}
539#endif
540
541static void sync_and_sleep(int i)
542{
543 sync();
544 sleep(i);
545}
546
547static uint16_t check_if_min_and_set_val(uint16_t a, uint16_t b)
548{
549 if( a < b)
550 a = b;
551 return a;
552}
553
554static uint16_t check_if_maj_and_set_val(uint16_t a, uint16_t b)
555{
556 if( a > b)
557 a = b;
558 return a;
559}
560
561static unsigned long int set_flag(char *p, char max)
562{
563 if (*p >= '0' && *p <= max )
564 return 1;
565 return 0;
566}
567
568/* end of busybox specific stuff */
569
570#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
571static uint8_t mode_loop(uint16_t mode_sup, uint16_t mode_sel, int cc, uint8_t *have_mode)
572{
573 uint16_t ii;
574 uint8_t err_dma = 0;
575
576 for(ii = 0; ii <= MODE_MAX; ii++)
577 {
578 if(mode_sel & 0x0001)
579 {
580 printf("*%cdma%u ",cc,ii);
581 if(*have_mode)
582 err_dma = 1;
583 *have_mode = 1;
584 }
585 else if(mode_sup & 0x0001)
586 printf("%cdma%u ",cc,ii);
587
588 mode_sup >>=1;
589 mode_sel >>=1;
590 }
591 return err_dma;
592}
593
594static void print_ascii(uint16_t *p, uint8_t length) {
595 uint8_t ii;
596 char cl;
597
598 /* find first non-space & print it */
599 for(ii = 0; ii< length; ii++)
600 {
601 if(((char) 0x00ff&((*p)>>8)) != ' ')
602 break;
603 if((cl = (char) 0x00ff&(*p)) != ' ')
604 {
605 if_printf((cl != '\0'),"%c",cl);
606 p++;
607 ii++;
608 break;
609 }
610 p++;
611 }
612 /* print the rest */
613 for(; ii< length; ii++)
614 {
615 if(!(*p))
616 break; /* some older devices have NULLs */
617 printf("%c%c",(char)0x00ff&((*p)>>8),(char)(*p)&0x00ff);
618 p++;
619 }
620 printf("\n");
621}
622
623/* identify() is the only extern function used across two source files. The
624 others, though, were declared in hdparm.c with global scope; since other
625 functions in that file have static (file) scope, I assume the difference is
626 intentional. */
627static void identify (uint16_t *id_supplied, const char *devname)
628{
629
630 char *id_file = NULL;
631 FILE *fl;
632 uint16_t val[256], ii, jj, kk;
633 uint16_t like_std = 1, std = 0, min_std = 0xffff;
634 uint16_t dev = NO_DEV, eqpt = NO_DEV;
635 uint8_t have_mode = 0, err_dma = 0;
636 uint8_t chksum = 0;
637 uint32_t ll, mm, nn, oo;
638 __u64 bbbig; /* (:) */
639
640 if (id_supplied)
641 {
642#if __BYTE_ORDER == __BIG_ENDIAN
643 swab(id_supplied, val, sizeof(val));
644#else
645 memcpy(val, id_supplied, sizeof(val));
646#endif
647 }
648 else
649 {
650 id_file = xcalloc(1, strlen(devname)+22);
651 sprintf(id_file, "/proc/ide/%s/identify", devname);
652 /* open the file, read in all the info and close it */
653 if (id_file == NULL)
654 fl = stdin;
655 else
656 fl = bb_xfopen(id_file, "r");
657
658 /* calculate checksum over all bytes */
659 for(ii = GEN_CONFIG; ii<=INTEGRITY; ii++)
660 {
661 unsigned int scratch;
662 if(1 != fscanf(fl,"%04x",&scratch))
663 break;
664 val[ii] = (uint16_t)scratch;
665 chksum += val[ii] + (val[ii] >> 8);
666 }
667 fclose(fl);
668 /*bb_fclose_nonstdin(fl);*/
669 if(ii < (INTEGRITY+1))
670 bb_error_msg_and_die("Input file wrong format or length");
671 }
672 chksum &= 0xff;
673
674 /* check if we recognise the device type */
675 printf("\n");
676 if(!(val[GEN_CONFIG] & NOT_ATA))
677 {
678 dev = ATA_DEV;
679 printf("ATA device, with ");
680 }
681 else if(val[GEN_CONFIG]==CFA_SUPPORT_VAL)
682 {
683 dev = ATA_DEV;
684 like_std = 4;
685 printf("CompactFlash ATA device, with ");
686 }
687 else if(!(val[GEN_CONFIG] & NOT_ATAPI))
688 {
689 dev = ATAPI_DEV;
690 eqpt = (val[GEN_CONFIG] & EQPT_TYPE) >> SHIFT_EQPT;
691 printf("ATAPI %s, with ", pkt_str[eqpt]);
692 like_std = 3;
693 }
694 else
695 /*"Unknown device type:\n\tbits 15&14 of general configuration word 0 both set to 1.\n"*/
696 bb_error_msg_and_die("Unknown device type");
697
698 if_printf(!(val[GEN_CONFIG] & MEDIA_REMOVABLE),"non-");
699 printf("removable media\n");
700
701 /* Info from the specific configuration word says whether or not the
702 * ID command completed correctly. It is only defined, however in
703 * ATA/ATAPI-5 & 6; it is reserved (value theoretically 0) in prior
704 * standards. Since the values allowed for this word are extremely
705 * specific, it should be safe to check it now, even though we don't
706 * know yet what standard this device is using.
707 */
708 if((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL) ||
709 (val[CONFIG]==PWRD_NID_VAL) || (val[CONFIG]==PWRD_ID_VAL) )
710 {
711 like_std = 5;
712 if_printf(((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL)),
713 "powers-up in standby; SET FEATURES subcmd spins-up.\n");
714 if_printf((((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==PWRD_NID_VAL)) && (val[GEN_CONFIG] & INCOMPLETE)),
715 "\n\tWARNING: ID response incomplete.\n\tFollowing data may be incorrect.\n\n");
716 }
717
718 /* output the model and serial numbers and the fw revision */
719 xprint_ascii(val, START_MODEL, "Model Number:", LENGTH_MODEL);
720 xprint_ascii(val, START_SERIAL, "Serial Number:", LENGTH_SERIAL);
721 xprint_ascii(val, START_FW_REV, "Firmware Revision:", LENGTH_FW_REV);
722 xprint_ascii(val, START_MEDIA, "Media Serial Num:", LENGTH_MEDIA);
723 xprint_ascii(val, START_MANUF, "Media Manufacturer:", LENGTH_MANUF);
724
725 /* major & minor standards version number (Note: these words were not
726 * defined until ATA-3 & the CDROM std uses different words.) */
727 printf("Standards:");
728 if(eqpt != CDROM)
729 {
730 if(val[MINOR] && (val[MINOR] <= MINOR_MAX))
731 {
732 like_std=check_if_min_and_set_val(like_std, 3);
733 std = actual_ver[val[MINOR]];
734 if_printf(std,"\n\tUsed: %s ",minor_str[val[MINOR]]);
735
736 }
737 /* looks like when they up-issue the std, they obsolete one;
738 * thus, only the newest 4 issues need be supported. (That's
739 * what "kk" and "min_std" are all about.) */
740 if(val[MAJOR] && (val[MAJOR] !=NOVAL_1))
741 {
742 printf("\n\tSupported: ");
743 jj = val[MAJOR] << 1;
744 kk = like_std >4 ? like_std-4: 0;
745 for(ii = 14; (ii >0)&&(ii>kk); ii--)
746 {
747 if(jj & 0x8000)
748 {
749 printf("%u ", ii);
750 if(like_std < ii)
751 {
752 like_std = ii;
753 kk = like_std >4 ? like_std-4: 0;
754 }
755 min_std=check_if_maj_and_set_val(min_std, ii);
756 }
757 jj <<= 1;
758 }
759 like_std=check_if_min_and_set_val(like_std, 3);
760 }
761 /* Figure out what standard the device is using if it hasn't told
762 * us. If we know the std, check if the device is using any of
763 * the words from the next level up. It happens.
764 */
765 like_std=check_if_min_and_set_val(like_std, std);
766
767 if(((std == 5) || (!std && (like_std < 6))) &&
768 ((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
769 (( val[CMDS_SUPP_1] & CMDS_W83) > 0x00ff)) ||
770 ((( val[CMDS_SUPP_2] & VALID) == VALID_VAL) &&
771 ( val[CMDS_SUPP_2] & CMDS_W84) ) ) )
772 {
773 like_std = 6;
774 }
775 else if(((std == 4) || (!std && (like_std < 5))) &&
776 ((((val[INTEGRITY] & SIG) == SIG_VAL) && !chksum) ||
777 (( val[HWRST_RSLT] & VALID) == VALID_VAL) ||
778 ((( val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
779 (( val[CMDS_SUPP_1] & CMDS_W83) > 0x001f)) ) )
780 {
781 like_std = 5;
782 }
783 else if(((std == 3) || (!std && (like_std < 4))) &&
784 ((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
785 ((( val[CMDS_SUPP_1] & CMDS_W83) > 0x0000) ||
786 (( val[CMDS_SUPP_0] & CMDS_W82) > 0x000f))) ||
787 (( val[CAPAB_1] & VALID) == VALID_VAL) ||
788 (( val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) ||
789 (( val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP) ) )
790 {
791 like_std = 4;
792 }
793 else if(((std == 2) || (!std && (like_std < 3))) &&
794 ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) )
795 {
796 like_std = 3;
797 }
798 else if(((std == 1) || (!std && (like_std < 2))) &&
799 ((val[CAPAB_0] & (IORDY_SUP | IORDY_OFF)) ||
800 (val[WHATS_VALID] & OK_W64_70)) )
801 {
802 like_std = 2;
803 }
804 if(!std)
805 printf("\n\tLikely used: %u\n",like_std);
806 else if(like_std > std)
807 printf("& some of %u\n",like_std);
808 else
809 printf("\n");
810 }
811 else
812 {
813 /* TBD: do CDROM stuff more thoroughly. For now... */
814 kk = 0;
815 if(val[CDR_MINOR] == 9)
816 {
817 kk = 1;
818 printf("\n\tUsed: ATAPI for CD-ROMs, SFF-8020i, r2.5");
819 }
820 if(val[CDR_MAJOR] && (val[CDR_MAJOR] !=NOVAL_1))
821 {
822 kk = 1;
823 printf("\n\tSupported: CD-ROM ATAPI");
824 jj = val[CDR_MAJOR] >> 1;
825 for(ii = 1; ii <15; ii++)
826 {
827 if_printf((jj & 0x0001),"-%u ", ii);
828 jj >>= 1;
829 }
830 }
831 if_else_printf((!kk),"\n\tLikely used CD-ROM ATAPI-1\n","\n");
832 /* the cdrom stuff is more like ATA-2 than anything else, so: */
833 like_std = 2;
834 }
835
836 if(min_std == 0xffff)
837 min_std = like_std > 4 ? like_std - 3 : 1;
838
839 printf("Configuration:\n");
840 /* more info from the general configuration word */
841 if((eqpt != CDROM) && (like_std == 1))
842 {
843 jj = val[GEN_CONFIG] >> 1;
844 for(ii = 1; ii < 15; ii++)
845 {
846 if_printf((jj & 0x0001),"\t%s\n",ata1_cfg_str[ii]);
847 jj >>=1;
848 }
849 }
850 if(dev == ATAPI_DEV)
851 {
852 printf("\tDRQ response: "); /* Data Request (DRQ) */
853 switch(val[GEN_CONFIG] & DRQ_RESPONSE_TIME)
854 {
855 case DRQ_3MS_VAL :
856 printf("3ms.\n");
857 break;
858 case DRQ_INTR_VAL :
859 printf("<=10ms with INTRQ\n");
860 break;
861 case DRQ_50US_VAL :
862 printf("50us.\n");
863 break;
864 default :
865 printf("unknown.\n");
866 break;
867 }
868 printf("\tPacket size: ");
869 switch(val[GEN_CONFIG] & PKT_SIZE_SUPPORTED)
870 {
871 case PKT_SIZE_12_VAL :
872 printf("12 bytes\n");
873 break;
874 case PKT_SIZE_16_VAL :
875 printf("16 bytes\n");
876 break;
877 default :
878 printf("Unknown\n");
879 break;
880 }
881 }
882 else
883 {
884 /* addressing...CHS? See section 6.2 of ATA specs 4 or 5 */
885 ll = (uint32_t)val[LBA_SECTS_MSB] << 16 | val[LBA_SECTS_LSB];
886 mm = 0; bbbig = 0;
887 if ( (ll > 0x00FBFC10) && (!val[LCYLS]))
888 printf("\tCHS addressing not supported\n");
889 else
890 {
891 jj = val[WHATS_VALID] & OK_W54_58;
892 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",
893 val[LCYLS],jj?val[LCYLS_CUR]:0, val[LHEADS],jj?val[LHEADS_CUR]:0, val[LSECTS],jj?val[LSECTS_CUR]:0);
894
895 if_printf(((min_std == 1) && (val[TRACK_BYTES] || val[SECT_BYTES])),
896 "\tbytes/track: %u\tbytes/sector: %u\n",val[TRACK_BYTES], val[SECT_BYTES]);
897
898 if(jj)
899 {
900 mm = (uint32_t)val[CAPACITY_MSB] << 16 | val[CAPACITY_LSB];
901 if(like_std < 3)
902 {
903 /* check Endian of capacity bytes */
904 nn = val[LCYLS_CUR] * val[LHEADS_CUR] * val[LSECTS_CUR];
905 oo = (uint32_t)val[CAPACITY_LSB] << 16 | val[CAPACITY_MSB];
906 if(abs(mm - nn) > abs(oo - nn))
907 mm = oo;
908 }
909 printf("\tCHS current addressable sectors:%11u\n",mm);
910 }
911 }
912 /* LBA addressing */
913 printf("\tLBA user addressable sectors:%11u\n",ll);
914 if( ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
915 (val[CMDS_SUPP_1] & SUPPORT_48_BIT) )
916 {
917 bbbig = (__u64)val[LBA_64_MSB] << 48 |
918 (__u64)val[LBA_48_MSB] << 32 |
919 (__u64)val[LBA_MID] << 16 |
920 val[LBA_LSB] ;
921 printf("\tLBA48 user addressable sectors:%11llu\n",bbbig);
922 }
923
924 if (!bbbig)
925 bbbig = (__u64)(ll>mm ? ll : mm); /* # 512 byte blocks */
926 printf("\tdevice size with M = 1024*1024: %11llu MBytes\n",bbbig>>11);
927 bbbig = (bbbig<<9)/1000000;
928 printf("\tdevice size with M = 1000*1000: %11llu MBytes ",bbbig);
929
930 if_else_printf((bbbig > 1000),"(%llu GB)\n","\n",bbbig/1000);
931
932 }
933
934 /* hw support of commands (capabilities) */
935 printf("Capabilities:\n\t");
936
937 if(dev == ATAPI_DEV)
938 {
939 if(eqpt != CDROM)
940 if_printf((val[CAPAB_0] & CMD_Q_SUP),"Cmd queuing, ");
941
942 if_printf((val[CAPAB_0] & OVLP_SUP),"Cmd overlap, ");
943 }
944 if_printf((val[CAPAB_0] & LBA_SUP),"LBA, ");
945
946 if(like_std != 1)
947 {
948 printf("IORDY");
949 if_printf((!(val[CAPAB_0] & IORDY_SUP)),"(may be)");
950 if_else_printf((val[CAPAB_0] & IORDY_OFF),"(can","(cannot");
951 printf(" be disabled)\n");
952 }
953 else
954 printf("no IORDY\n");
955
956 if((like_std == 1) && val[BUF_TYPE])
957 {
958 kk = val[BUF_TYPE];
959 printf("\tBuffer type: %04x: ",kk);
960 if_else_printf((kk < 2),"single port, single-sector","dual port, multi-sector");
961 if_printf((kk > 2)," with read caching ability");
962 printf("\n");
963 }
964 jj = 0;
965 if((min_std == 1) && (val[BUFFER__SIZE] && (val[BUFFER__SIZE] != NOVAL_1)))
966 {
967 printf("\tBuffer size: %.1fkB",(float)val[BUFFER__SIZE]/2);
968 jj = 1;
969 }
970 if((min_std < 4) && (val[RW_LONG]))
971 {
972 printf("\tbytes avail on r/w long: %u",val[RW_LONG]);
973 jj = 1;
974 }
975 if((eqpt != CDROM) && (like_std > 3))
976 {
977 printf("\tQueue depth: %u",(val[QUEUE_DEPTH] & DEPTH_BITS)+1);
978 jj = 1;
979 }
980 if_printf(jj,"\n");
981
982 if(dev == ATA_DEV)
983 {
984 if(like_std == 1)
985 printf("\tCan%s perform double-word IO\n",(!val[DWORD_IO]) ?"not":"");
986 else
987 {
988 printf("\tStandby timer values: spec'd by ");
989 if_else_printf((val[CAPAB_0] & STD_STBY),"Standard","Vendor");
990 if((like_std > 3) && ((val[CAPAB_1] & VALID) == VALID_VAL))
991 printf(", %s device specific minimum\n",(val[CAPAB_1] & MIN_STANDBY_TIMER)?"with":"no");
992 else
993 printf("\n");
994 }
995 printf("\tR/W multiple sector transfer: ");
996 if((like_std < 3) && !(val[SECTOR_XFER_MAX] & SECTOR_XFER))
997 printf("not supported\n");
998 else
999 {
1000 printf("Max = %u\tCurrent = ",val[SECTOR_XFER_MAX] & SECTOR_XFER);
1001 if_else_printf((val[SECTOR_XFER_CUR] & MULTIPLE_SETTING_VALID),
1002 "%u\n","?\n",val[SECTOR_XFER_CUR] & SECTOR_XFER);
1003 }
1004 if((like_std > 3) && (val[CMDS_SUPP_1] & 0x0008))
1005 {
1006 /* We print out elsewhere whether the APM feature is enabled or
1007 not. If it's not enabled, let's not repeat the info; just print
1008 nothing here. */
1009 printf("\tAdvancedPM level: ");
1010 if ( (val[ADV_PWR] & 0xFF00) == 0x4000 )
1011 {
1012 uint8_t apm_level = val[ADV_PWR] & 0x00FF;
1013 printf("%u (0x%x)\n", apm_level, apm_level);
1014 }
1015 else
1016 printf("unknown setting (0x%04x)\n", val[ADV_PWR]);
1017 }
1018 if(like_std > 5)
1019 {
1020 if_printf(val[ACOUSTIC],"\tRecommended acoustic management value: %u, current value: %u\n",
1021 (val[ACOUSTIC] >> 8) & 0x00ff, val[ACOUSTIC] & 0x00ff);
1022 }
1023 }
1024 else
1025 {
1026 /* ATAPI */
1027 if(eqpt != CDROM)
1028 if_printf((val[CAPAB_0] & SWRST_REQ),"\tATA sw reset required\n");
1029
1030 if(val[PKT_REL] || val[SVC_NBSY])
1031 {
1032 printf("\tOverlap support:");
1033 if_printf(val[PKT_REL]," %uus to release bus.",val[PKT_REL]);
1034 if_printf(val[SVC_NBSY]," %uus to clear BSY after SERVICE cmd.",val[SVC_NBSY]);
1035 printf("\n");
1036 }
1037 }
1038
1039 /* DMA stuff. Check that only one DMA mode is selected. */
1040 printf("\tDMA: ");
1041 if(!(val[CAPAB_0] & DMA_SUP))
1042 printf("not supported\n");
1043 else
1044 {
1045 if_printf((val[DMA_MODE] && !val[SINGLE_DMA] && !val[MULTI_DMA]),
1046 " sdma%u\n",(val[DMA_MODE] & MODE) >> 8);
1047 if(val[SINGLE_DMA])
1048 {
1049 jj = val[SINGLE_DMA];
1050 kk = val[SINGLE_DMA] >> 8;
1051 err_dma += mode_loop(jj,kk,'s',&have_mode);
1052 }
1053 if(val[MULTI_DMA])
1054 {
1055 jj = val[MULTI_DMA];
1056 kk = val[MULTI_DMA] >> 8;
1057 err_dma += mode_loop(jj,kk,'m',&have_mode);
1058 }
1059 if((val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA])
1060 {
1061 jj = val[ULTRA_DMA];
1062 kk = val[ULTRA_DMA] >> 8;
1063 err_dma += mode_loop(jj,kk,'u',&have_mode);
1064 }
1065 if_printf((err_dma || !have_mode),"(?)");
1066 printf("\n");
1067
1068 if_printf(((dev == ATAPI_DEV) && (eqpt != CDROM) && (val[CAPAB_0] & DMA_IL_SUP)),
1069 "\t Interleaved DMA support\n");
1070
1071 if((val[WHATS_VALID] & OK_W64_70) &&
1072 (val[DMA_TIME_MIN] || val[DMA_TIME_NORM]))
1073 {
1074 printf("\t Cycle time:");
1075 if_printf(val[DMA_TIME_MIN]," min=%uns",val[DMA_TIME_MIN]);
1076 if_printf(val[DMA_TIME_NORM]," recommended=%uns",val[DMA_TIME_NORM]);
1077 printf("\n");
1078 }
1079 }
1080
1081 /* Programmed IO stuff */
1082 printf("\tPIO: ");
1083 /* If a drive supports mode n (e.g. 3), it also supports all modes less
1084 * than n (e.g. 3, 2, 1 and 0). Print all the modes. */
1085 if((val[WHATS_VALID] & OK_W64_70) && (val[ADV_PIO_MODES] & PIO_SUP))
1086 {
1087 jj = ((val[ADV_PIO_MODES] & PIO_SUP) << 3) | 0x0007;
1088 for(ii = 0; ii <= PIO_MODE_MAX ; ii++)
1089 {
1090 if_printf((jj & 0x0001),"pio%d ",ii);
1091 jj >>=1;
1092 }
1093 printf("\n");
1094 }
1095 else if(((min_std < 5) || (eqpt == CDROM)) && (val[PIO_MODE] & MODE) )
1096 {
1097 for(ii = 0; ii <= val[PIO_MODE]>>8; ii++)
1098 printf("pio%d ",ii);
1099 printf("\n");
1100 }
1101 else
1102 printf("unknown\n");
1103
1104 if(val[WHATS_VALID] & OK_W64_70)
1105 {
1106 if(val[PIO_NO_FLOW] || val[PIO_FLOW])
1107 {
1108 printf("\t Cycle time:");
1109 if_printf(val[PIO_NO_FLOW]," no flow control=%uns", val[PIO_NO_FLOW]);
1110 if_printf(val[PIO_FLOW]," IORDY flow control=%uns", val[PIO_FLOW]);
1111 printf("\n");
1112 }
1113 }
1114
1115 if((val[CMDS_SUPP_1] & VALID) == VALID_VAL)
1116 {
1117 printf("Commands/features:\n\tEnabled\tSupported:\n");
1118 jj = val[CMDS_SUPP_0];
1119 kk = val[CMDS_EN_0];
1120 for(ii = 0; ii < NUM_CMD_FEAT_STR; ii++)
1121 {
1122 if((jj & 0x8000) && (*cmd_feat_str[ii] != '\0'))
1123 {
1124 if_else_printf((kk & 0x8000),"\t *","\t");
1125 printf("\t%s\n",cmd_feat_str[ii]);
1126 }
1127 jj <<=1; kk<<=1;
1128 if(ii%16 == 15)
1129 {
1130 jj = val[CMDS_SUPP_0+1+(ii/16)];
1131 kk = val[CMDS_EN_0+1+(ii/16)];
1132 }
1133 if(ii == 31)
1134 {
1135 if((val[CMDS_SUPP_2] & VALID) != VALID_VAL)
1136 ii +=16;
1137 }
1138 }
1139 }
1140 if_printf(((val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP),
1141 "\tRemovable Media Status Notification feature set supported\n");
1142
1143
1144 /* security */
1145 if((eqpt != CDROM) && (like_std > 3) &&
1146 (val[SECU_STATUS] || val[ERASE_TIME] || val[ENH_ERASE_TIME]))
1147 {
1148 printf("Security: \n");
1149 if_printf((val[PSWD_CODE] && (val[PSWD_CODE] != NOVAL_1)),
1150 "\tMaster password revision code = %u\n",val[PSWD_CODE]);
1151 jj = val[SECU_STATUS];
1152 if(jj)
1153 {
1154 for(ii = 0; ii < NUM_SECU_STR; ii++)
1155 {
1156 if_else_printf((!(jj & 0x0001)),"\tnot\t%s\n", "\t\t%s\n", secu_str[ii]);
1157 jj >>=1;
1158 }
1159 if(val[SECU_STATUS] & SECU_ENABLED)
1160 {
1161 printf("\tSecurity level ");
1162 if_else_printf((val[SECU_STATUS] & SECU_LEVEL),"maximum\n","high\n");
1163 }
1164 }
1165 jj = val[ERASE_TIME] & ERASE_BITS;
1166 kk = val[ENH_ERASE_TIME] & ERASE_BITS;
1167 if(jj || kk)
1168 {
1169 printf("\t");
1170 if_printf(jj,"%umin for SECURITY ERASE UNIT. ", jj==ERASE_BITS ? 508 : jj<<1);
1171 if_printf(kk,"%umin for ENHANCED SECURITY ERASE UNIT.", kk==ERASE_BITS ? 508 : kk<<1);
1172 printf("\n");
1173 }
1174 }
1175
1176 /* reset result */
1177 if((val[HWRST_RSLT] & VALID) == VALID_VAL)
1178 {
1179 printf("HW reset results:\n");
1180 if_else_printf((val[HWRST_RSLT] & CBLID),"\tCBLID- above Vih\n","\tCBLID- below Vih\n");
1181
1182 if(val[HWRST_RSLT] & RST0)
1183 {
1184 printf("\tDevice num = 0");
1185 jj = val[HWRST_RSLT];
1186 }
1187 else
1188 {
1189 printf("\tDevice num = 1");
1190 jj = val[HWRST_RSLT] >> 8;
1191 }
1192
1193 if((jj & DEV_DET) == JUMPER_VAL)
1194 printf(" determined by the jumper");
1195 else if((jj & DEV_DET) == CSEL_VAL)
1196 printf(" determined by CSEL");
1197 printf("\n");
1198 }
1199
1200 /* more stuff from std 5 */
1201 if((like_std > 4) && (eqpt != CDROM))
1202 {
1203 if(val[CFA_PWR_MODE] & VALID_W160)
1204 {
1205 printf("CFA power mode 1:\n\t");
1206 if_else_printf((val[CFA_PWR_MODE] & PWR_MODE_OFF),"disabled","enabled");
1207
1208 if_printf((val[CFA_PWR_MODE] & PWR_MODE_REQ)," and required by some commands");
1209 printf("\n");
1210
1211 if_printf((val[CFA_PWR_MODE] & MAX_AMPS),"\tMaximum current = %uma\n",val[CFA_PWR_MODE] & MAX_AMPS);
1212 }
1213 if((val[INTEGRITY] & SIG) == SIG_VAL)
1214 {
1215 printf("Checksum: ");
1216 if_printf(chksum,"in");
1217 printf("correct\n");
1218 }
1219 }
1220
1221 exit(0);
1222}
1223#endif
1224
1225static int verbose = 0, get_identity = 0, get_geom = 0, noisy = 1, quiet = 0;
1226static int flagcount = 0, do_flush = 0, is_scsi_hd = 0, is_xt_hd = 0;
1227static int do_ctimings, do_timings = 0;
1228
1229static unsigned long set_readahead= 0, get_readahead= 0, Xreadahead= 0;
1230static unsigned long set_readonly = 0, get_readonly = 0, readonly = 0;
1231static unsigned long set_unmask = 0, get_unmask = 0, unmask = 0;
1232static unsigned long set_mult = 0, get_mult = 0, mult = 0;
1233#ifdef CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA
1234static unsigned long set_dma = 0, get_dma = 0, dma = 0;
1235#endif
1236static unsigned long set_dma_q = 0, get_dma_q = 0, dma_q = 0;
1237static unsigned long set_nowerr = 0, get_nowerr = 0, nowerr = 0;
1238static unsigned long set_keep = 0, get_keep = 0, keep = 0;
1239static unsigned long set_io32bit = 0, get_io32bit = 0, io32bit = 0;
1240static unsigned long set_piomode = 0, noisy_piomode= 0;
1241static int piomode = 0;
1242#ifdef HDIO_DRIVE_CMD
1243static unsigned long set_dkeep = 0, get_dkeep = 0, dkeep = 0;
1244static unsigned long set_standby = 0, get_standby = 0, standby_requested= 0;
1245static unsigned long set_xfermode = 0, get_xfermode = 0;
1246static int xfermode_requested= 0;
1247static unsigned long set_lookahead= 0, get_lookahead= 0, lookahead= 0;
1248static unsigned long set_prefetch = 0, get_prefetch = 0, prefetch = 0;
1249static unsigned long set_defects = 0, get_defects = 0, defects = 0;
1250static unsigned long set_wcache = 0, get_wcache = 0, wcache = 0;
1251static unsigned long set_doorlock = 0, get_doorlock = 0, doorlock = 0;
1252static unsigned long set_seagate = 0, get_seagate = 0;
1253static unsigned long set_standbynow = 0, get_standbynow = 0;
1254static unsigned long set_sleepnow = 0, get_sleepnow = 0;
1255static unsigned long get_powermode = 0;
1256static unsigned long set_apmmode = 0, get_apmmode= 0, apmmode = 0;
1257#endif
1258#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
1259static int get_IDentity = 0;
1260#endif
1261#ifdef CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
1262static int unregister_hwif = 0;
1263static int hwif = 0;
1264#endif
1265#ifdef CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF
1266static int scan_hwif = 0;
1267static int hwif_data = 0;
1268static int hwif_ctrl = 0;
1269static int hwif_irq = 0;
1270#endif
1271#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
1272static int set_busstate = 0, get_busstate = 0, busstate = 0;
1273#endif
1274static int reread_partn = 0;
1275
1276#ifdef CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET
1277static int perform_reset = 0;
1278#endif /* CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET */
1279#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
1280static int perform_tristate = 0, tristate = 0;
1281#endif /* CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF */
1282
1283// Historically, if there was no HDIO_OBSOLETE_IDENTITY, then
1284// then the HDIO_GET_IDENTITY only returned 142 bytes.
1285// Otherwise, HDIO_OBSOLETE_IDENTITY returns 142 bytes,
1286// and HDIO_GET_IDENTITY returns 512 bytes. But the latest
1287// 2.5.xx kernels no longer define HDIO_OBSOLETE_IDENTITY
1288// (which they should, but they should just return -EINVAL).
1289//
1290// So.. we must now assume that HDIO_GET_IDENTITY returns 512 bytes.
1291// On a really old system, it will not, and we will be confused.
1292// Too bad, really.
1293
1294#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
1295static const char *cfg_str[] =
1296{ "", " HardSect", " SoftSect", " NotMFM",
1297 " HdSw>15uSec", " SpinMotCtl", " Fixed", " Removeable",
1298 " DTR<=5Mbs", " DTR>5Mbs", " DTR>10Mbs", " RotSpdTol>.5%",
1299 " dStbOff", " TrkOff", " FmtGapReq", " nonMagnetic"
1300};
1301
1302static const char *BuffType[] = {"unknown", "1Sect", "DualPort", "DualPortCache"};
1303
1304static void dump_identity (const struct hd_driveid *id)
1305{
1306 int i;
1307 char pmodes[64] = {0,}, dmodes[128]={0,}, umodes[128]={0,};
1308 const unsigned short int *id_regs= (const void*) id;
1309 unsigned long capacity;
1310
1311 printf("\n Model=%.40s, FwRev=%.8s, SerialNo=%.20s\n Config={",
1312 id->model, id->fw_rev, id->serial_no);
1313 for (i=0; i<=15; i++)
1314 if_printf((id->config & (1<<i)),"%s", cfg_str[i]);
1315
1316 printf(" }\n RawCHS=%u/%u/%u, TrkSize=%u, SectSize=%u, ECCbytes=%u\n",
1317 id->cyls, id->heads, id->sectors, id->track_bytes,
1318 id->sector_bytes, id->ecc_bytes);
1319
1320 if (id->buf_type > 3)
1321 printf("%s%u", " BuffType=", id->buf_type);
1322 else
1323 printf("%s%s", " BuffType=", BuffType[id->buf_type]);
1324
1325 printf(", BuffSize=%ukB, MaxMultSect=%u", id->buf_size/2, id->max_multsect);
1326 if (id->max_multsect)
1327 {
1328 printf(", MultSect=");
1329 if (!(id->multsect_valid&1))
1330 printf("?%u?", id->multsect);
1331 else if (id->multsect)
1332 printf("%u", id->multsect);
1333 else
1334 printf("off");
1335 }
1336 printf("\n");
1337 if (id->tPIO <= 5)
1338 {
1339 strcat(pmodes, "pio0 ");
1340 if_strcat((id->tPIO >= 1), pmodes, "pio1 ");
1341 if_strcat((id->tPIO >= 2), pmodes, "pio2 ");
1342
1343 }
1344 if_printf((!(id->field_valid&1))," (maybe):");
1345#if __BYTE_ORDER == __BIG_ENDIAN
1346 capacity = (id->cur_capacity0 << 16) | id->cur_capacity1;
1347#else
1348 capacity = (id->cur_capacity1 << 16) | id->cur_capacity0;
1349#endif
1350 printf(" CurCHS=%u/%u/%u, CurSects=%lu, LBA=%s",id->cur_cyls, id->cur_heads,
1351 id->cur_sectors, capacity ,
1352 ((id->capability&2)==0)?"no":"yes");
1353
1354 if_printf((id->capability&2),", LBAsects=%u", id->lba_capacity);
1355
1356 if (id->capability&1)
1357 {
1358 if (id->dma_1word | id->dma_mword)
1359 {
1360 if_strcat((id->dma_1word & 0x100), dmodes, "*");
1361 if_strcat((id->dma_1word & 1), dmodes, "sdma0 ");
1362 if_strcat((id->dma_1word & 0x200), dmodes, "*");
1363 if_strcat((id->dma_1word & 2), dmodes, "sdma1 ");
1364 if_strcat((id->dma_1word & 0x400), dmodes, "*");
1365 if_strcat((id->dma_1word & 4), dmodes, "sdma2 ");
1366 if_strcat((id->dma_1word & 0xf800), dmodes, "*");
1367 if_strcat((id->dma_1word & 0xf8), dmodes, "sdma? ");
1368 if_strcat((id->dma_mword & 0x100), dmodes, "*");
1369 if_strcat((id->dma_mword & 1), dmodes, "mdma0 ");
1370 if_strcat((id->dma_mword & 0x200), dmodes, "*");
1371 if_strcat((id->dma_mword & 2), dmodes, "mdma1 ");
1372 if_strcat((id->dma_mword & 0x400), dmodes, "*");
1373 if_strcat((id->dma_mword & 4), dmodes, "mdma2 ");
1374 if_strcat((id->dma_mword & 0xf800), dmodes, "*");
1375 if_strcat((id->dma_mword & 0xf8), dmodes, "mdma? ");
1376 }
1377 }
1378 printf("\n IORDY=");
1379 if (id->capability&8)
1380 printf((id->capability&4) ? "on/off" : "yes");
1381 else
1382 printf("no");
1383
1384 if ((id->capability&8) || (id->field_valid&2))
1385 {
1386 if (id->field_valid&2)
1387 {
1388 printf(", tPIO={min:%u,w/IORDY:%u}", id->eide_pio, id->eide_pio_iordy);
1389 if_strcat((id->eide_pio_modes & 1), pmodes, "pio3 ");
1390 if_strcat((id->eide_pio_modes & 2), pmodes, "pio4 ");
1391 if_strcat((id->eide_pio_modes &~3), pmodes, "pio? ");
1392 }
1393 if (id->field_valid&4)
1394 {
1395 if_strcat((id->dma_ultra & 0x100),umodes,"*");
1396 if_strcat((id->dma_ultra & 0x001),umodes,"udma0 ");
1397 if_strcat((id->dma_ultra & 0x200),umodes,"*");
1398 if_strcat((id->dma_ultra & 0x002),umodes,"udma1 ");
1399 if_strcat((id->dma_ultra & 0x400),umodes,"*");
1400 if_strcat((id->dma_ultra & 0x004),umodes,"udma2 ");
1401#ifdef __NEW_HD_DRIVE_ID
1402 if (id->hw_config & 0x2000)
1403 {
1404#else /* !__NEW_HD_DRIVE_ID */
1405 if (id->word93 & 0x2000)
1406 {
1407#endif /* __NEW_HD_DRIVE_ID */
1408 if_strcat((id->dma_ultra & 0x0800),umodes,"*");
1409 if_strcat((id->dma_ultra & 0x0008),umodes,"udma3 ");
1410 if_strcat((id->dma_ultra & 0x1000),umodes,"*");
1411 if_strcat((id->dma_ultra & 0x0010),umodes,"udma4 ");
1412 if_strcat((id->dma_ultra & 0x2000),umodes,"*");
1413 if_strcat((id->dma_ultra & 0x0020),umodes,"udma5 ");
1414 if_strcat((id->dma_ultra & 0x4000),umodes,"*");
1415 if_strcat((id->dma_ultra & 0x0040),umodes,"udma6 ");
1416 if_strcat((id->dma_ultra & 0x8000),umodes,"*");
1417 if_strcat((id->dma_ultra & 0x0080),umodes,"udma7 ");
1418 }
1419 }
1420 }
1421 if_printf(((id->capability&1) && (id->field_valid&2)),
1422 ", tDMA={min:%u,rec:%u}", id->eide_dma_min, id->eide_dma_time);
1423 printf("\n PIO modes: %s", pmodes);
1424 if_printf((*dmodes),"\n DMA modes: %s", dmodes);
1425 if_printf((*umodes),"\n UDMA modes: %s", umodes);
1426
1427 printf("\n AdvancedPM=%s",((id_regs[83]&8)==0)?"no":"yes");
1428 if (id_regs[83] & 8)
1429 {
1430 if (!(id_regs[86]&8))
1431 printf(": disabled (255)");
1432 else if ((id_regs[91]&0xFF00)!=0x4000)
1433 printf(": unknown setting");
1434 else
1435 printf(": mode=0x%02X (%u)",id_regs[91]&0xFF,id_regs[91]&0xFF);
1436 }
1437 if_printf( (id_regs[82]&0x20)," WriteCache=%s",(id_regs[85]&0x20) ? "enabled" : "disabled");
1438#ifdef __NEW_HD_DRIVE_ID
1439 if ((id->minor_rev_num && id->minor_rev_num <= 31) || (id->major_rev_num && id->minor_rev_num <= 31))
1440 {
1441 printf("\n Drive conforms to: ");
1442 if_else_printf((id->minor_rev_num <= 31),"%s: ","unknown: ", minor_str[id->minor_rev_num]);
1443 if (id->major_rev_num < 31)
1444 {
1445 for (i=0; i <= 15; i++)
1446 if_printf((id->major_rev_num & (1<<i))," %u", i);
1447 }
1448 }
1449#endif /* __NEW_HD_DRIVE_ID */
1450 printf("\n\n * signifies the current active mode\n\n");
1451}
1452#endif
1453
1454static void flush_buffer_cache (int fd)
1455{
1456 fsync (fd); /* flush buffers */
1457 bb_ioctl(fd, BLKFLSBUF, NULL,"BLKFLSBUF" ) ;/* do it again, big time */
1458#ifdef HDIO_DRIVE_CMD
1459 sleep(1);
1460 if (ioctl(fd, HDIO_DRIVE_CMD, NULL) && errno != EINVAL) /* await completion */
1461 bb_perror_msg("HDIO_DRIVE_CMD");
1462#endif
1463}
1464
1465static int seek_to_zero (int fd)
1466{
1467 if (lseek(fd, (off_t) 0, SEEK_SET))
1468 return 1;
1469 return 0;
1470}
1471
1472static int read_big_block (int fd, char *buf)
1473{
1474
1475 const char *string;
1476 int i, rc;
1477 if ((rc = read(fd, buf, TIMING_BUF_BYTES)) != TIMING_BUF_BYTES)
1478 {
1479 switch(rc)
1480 {
1481 case -1:
1482 string = "read()";
1483 break;
1484 case 0:
1485 string = "read() hit EOF - device too small";
1486 break;
1487 default:
1488 string = "read(%u) returned %u bytes";
1489 }
1490 bb_error_msg(string, TIMING_BUF_BYTES, rc);
1491 return 1;
1492 }
1493
1494 /* access all sectors of buf to ensure the read fully completed */
1495 for (i = 0; i < TIMING_BUF_BYTES; i += 512)
1496 buf[i] &= 1;
1497 return 0;
1498}
1499
1500static double correction = 0.0;
1501
1502static void do_time (int flag, int fd)
1503/*
1504 flag = 0 time_cache
1505 flag = 1 time_device
1506*/
1507{
1508 int i;
1509 char *buf;
1510 double elapsed;
1511 struct itimerval e1, e2;
1512 int shmid;
1513 int timing_MB = TIMING_MB;
1514
1515 if ((shmid = shmget(IPC_PRIVATE, TIMING_BUF_BYTES, 0600)) == -1)
1516 {
1517 bb_error_msg (bb_msg_shared_mem,"allocate"); /*"could not allocate sharedmem buf"*/
1518 return;
1519 }
1520 if (shmctl(shmid, SHM_LOCK, NULL) == -1)
1521 {
1522 bb_error_msg (bb_msg_shared_mem,"lock"); /*"could not lock sharedmem buf"*/
1523 (void) shmctl(shmid, IPC_RMID, NULL);
1524 return;
1525 }
1526 if ((buf = shmat(shmid, (char *) 0, 0)) == (char *) -1)
1527 {
1528 bb_error_msg (bb_msg_shared_mem,"attach"); /*"could not attach sharedmem buf"*/
1529 (void) shmctl(shmid, IPC_RMID, NULL);
1530 return;
1531 }
1532 if (shmctl(shmid, IPC_RMID, NULL) == -1)
1533 bb_error_msg ("shmctl(,IPC_RMID,)");
1534
1535 /* Clear out the device request queues & give them time to complete */
1536 sync_and_sleep(3);
1537
1538 if(flag == 0) /* Time cache */
1539 {
1540 /* Calculate a correction factor for the basic
1541 * overhead of doing a read() from the buffer cache.
1542 * To do this, we read the data once to "cache it" and
1543 * to force full preallocation of our timing buffer,
1544 * and then we re-read it 10 times while timing it.
1545 *
1546 * getitimer() is used rather than gettimeofday() because
1547 * it is much more consistent (on my machine, at least).
1548 */
1549 setitimer(ITIMER_REAL, &(struct itimerval){{1000,0},{1000,0}}, NULL);
1550 if (seek_to_zero (fd))
1551 return;
1552 if (read_big_block (fd, buf))
1553 return;
1554 printf(" Timing buffer-cache reads: ");
1555 fflush(stdout);
1556
1557 /* Clear out the device request queues & give them time to complete */
1558 sync_and_sleep(1);
1559
1560 /* Time re-reading from the buffer-cache */
1561 getitimer(ITIMER_REAL, &e1);
1562 for (i = (BUFCACHE_FACTOR * TIMING_BUF_COUNT) ; i > 0; --i)
1563 {
1564 if (seek_to_zero (fd))
1565 goto quit;
1566 if (read_big_block (fd, buf))
1567 goto quit;
1568 }
1569 getitimer(ITIMER_REAL, &e2);
1570 correction = (e1.it_value.tv_sec - e2.it_value.tv_sec) + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0);
1571
1572 /* Now remove the lseek() from the correction factor */
1573 getitimer(ITIMER_REAL, &e1);
1574 for (i = (BUFCACHE_FACTOR * TIMING_BUF_COUNT) ; i > 0; --i)
1575 {
1576 if (seek_to_zero (fd))
1577 goto quit;
1578 }
1579 getitimer(ITIMER_REAL, &e2);
1580 correction -= (e1.it_value.tv_sec - e2.it_value.tv_sec)
1581 + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0);
1582
1583 if ((BUFCACHE_FACTOR * timing_MB) >= correction) /* more than 1MB/s */
1584 printf("%2d MB in %5.2f seconds =%6.2f MB/sec\n",
1585 (BUFCACHE_FACTOR * timing_MB), correction,
1586 (BUFCACHE_FACTOR * timing_MB) / correction);
1587 else
1588 printf("%2d MB in %5.2f seconds =%6.2f kB/sec\n",
1589 (BUFCACHE_FACTOR * timing_MB), correction,
1590 (BUFCACHE_FACTOR * timing_MB) / correction * 1024);
1591 correction /= BUFCACHE_FACTOR;
1592
1593 flush_buffer_cache(fd);
1594 sleep(1);
1595 }
1596 else /* Time device */
1597 {
1598 printf(" Timing buffered disk reads: ");
1599 fflush(stdout);
1600
1601 /*
1602 * getitimer() is used rather than gettimeofday() because
1603 * it is much more consistent (on my machine, at least).
1604 */
1605 setitimer(ITIMER_REAL, &(struct itimerval){{1000,0},{1000,0}}, NULL);
1606
1607 /* Now do the timings for real */
1608 getitimer(ITIMER_REAL, &e1);
1609 for (i = TIMING_BUF_COUNT; i > 0; --i)
1610 {
1611 if (read_big_block (fd, buf))
1612 goto quit;
1613 }
1614 getitimer(ITIMER_REAL, &e2);
1615
1616 elapsed = (e1.it_value.tv_sec - e2.it_value.tv_sec) + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0);
1617
1618 if (timing_MB >= elapsed) /* more than 1MB/s */
1619 printf("%2d MB in %5.2f seconds =%6.2f MB/sec\n",timing_MB, elapsed, timing_MB / elapsed);
1620 else
1621 printf("%2d MB in %5.2f seconds =%6.2f kB/sec\n",timing_MB, elapsed, timing_MB / elapsed * 1024);
1622
1623 /*"Hmm.. suspicious results: probably not enough free memory for a proper test.");*/
1624 if (elapsed <= (correction * 2))
1625 bb_error_msg(bb_msg_memory_exhausted);
1626
1627#if 0 /* the "estimate" is just plain wrong for many systems.. */
1628 else if (correction != 0.0) {
1629 printf(" Estimating raw driver speed: ");
1630 elapsed -= correction;
1631 if (timing_MB >= elapsed) /* more than 1MB/s */
1632 printf("%2d MB in %5.2f seconds =%6.2f MB/sec\n",
1633 timing_MB, elapsed, timing_MB / elapsed);
1634 else
1635 printf("%2d MB in %5.2f seconds =%6.2f kB/sec\n",
1636 timing_MB, elapsed, timing_MB / elapsed * 1024);
1637 }
1638#endif
1639 }
1640quit:
1641 if (-1 == shmdt(buf))
1642 bb_error_msg (bb_msg_shared_mem,"detach"); /*"could not detach sharedmem buf"*/
1643}
1644
1645
1646static void no_scsi (void)
1647{
1648 /*" operation not supported on SCSI disks"*/
1649 if (is_scsi_hd)
1650 bb_error_msg_and_die(bb_msg_op_not_supp,"SCSI");
1651}
1652
1653static void no_xt (void)
1654{
1655 if (is_xt_hd)
1656 bb_error_msg_and_die(bb_msg_op_not_supp,"XT");
1657}
1658
1659static void on_off (unsigned int value)
1660{
1661 printf(value ? " (on)\n" : " (off)\n");
1662}
1663
1664#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
1665static void bus_state_value (unsigned int value)
1666{
1667 const char *string;
1668
1669 switch (value)
1670 {
1671 case BUSSTATE_ON:
1672 string = " (on)\n";
1673 break;
1674 case BUSSTATE_OFF:
1675 string = " (off)\n";
1676 break;
1677 case BUSSTATE_TRISTATE:
1678 string = " (tristate)\n";
1679 break;
1680 default:
1681 string = " (unknown: %d)\n";
1682 break;
1683 }
1684 printf(string, value);
1685}
1686#endif
1687
1688#ifdef HDIO_DRIVE_CMD
1689static void interpret_standby (unsigned int standby)
1690{
1691 printf(" (");
1692 switch(standby)
1693 {
1694 case 0:
1695 printf("off");
1696 break;
1697 case 252:
1698 printf("21 minutes");
1699 break;
1700 case 253:
1701 printf("vendor-specific");
1702 break;
1703 case 254:
1704 printf("?reserved");
1705 break;
1706 case 255:
1707 printf("21 minutes + 15 seconds");
1708 break;
1709 default:
1710 if (standby <= 240)
1711 {
1712 unsigned int secs = standby * 5;
1713 unsigned int mins = secs / 60;
1714 secs %= 60;
1715 if_printf(mins,"%u minutes", mins);
1716 if_printf((mins && secs)," + ");
1717 if_printf(secs,"%u seconds", secs);
1718 }
1719 else if (standby <= 251)
1720 {
1721 unsigned int mins = (standby - 240) * 30;
1722 unsigned int hrs = mins / 60;
1723 mins %= 60;
1724 if_printf(hrs,"%u hours", hrs);
1725 if_printf((hrs && mins)," + ");
1726 if_printf(mins,"%u minutes", mins);
1727 }
1728 else
1729 printf("illegal value");
1730 break;
1731 }
1732 printf(")\n");
1733}
1734
1735struct xfermode_entry {
1736 int val;
1737 const char *name;
1738};
1739
1740static const struct xfermode_entry xfermode_table[] = {
1741 { 8, "pio0" },
1742 { 9, "pio1" },
1743 { 10, "pio2" },
1744 { 11, "pio3" },
1745 { 12, "pio4" },
1746 { 13, "pio5" },
1747 { 14, "pio6" },
1748 { 15, "pio7" },
1749 { 16, "sdma0" },
1750 { 17, "sdma1" },
1751 { 18, "sdma2" },
1752 { 19, "sdma3" },
1753 { 20, "sdma4" },
1754 { 21, "sdma5" },
1755 { 22, "sdma6" },
1756 { 23, "sdma7" },
1757 { 32, "mdma0" },
1758 { 33, "mdma1" },
1759 { 34, "mdma2" },
1760 { 35, "mdma3" },
1761 { 36, "mdma4" },
1762 { 37, "mdma5" },
1763 { 38, "mdma6" },
1764 { 39, "mdma7" },
1765 { 64, "udma0" },
1766 { 65, "udma1" },
1767 { 66, "udma2" },
1768 { 67, "udma3" },
1769 { 68, "udma4" },
1770 { 69, "udma5" },
1771 { 70, "udma6" },
1772 { 71, "udma7" },
1773 { 0, NULL }
1774};
1775
1776static int translate_xfermode(char * name)
1777{
1778 const struct xfermode_entry *tmp;
1779 char *endptr;
1780 int val = -1;
1781
1782
1783 for (tmp = xfermode_table; tmp->name != NULL; ++tmp)
1784 {
1785 if (!strcmp(name, tmp->name))
1786 return tmp->val;
1787
1788 }
1789
1790 val = strtol(name, &endptr, 10);
1791 if (*endptr == '\0')
1792 return val;
1793
1794 return -1;
1795}
1796
1797static void interpret_xfermode (unsigned int xfermode)
1798{
1799 printf(" (");
1800 switch(xfermode) {
1801 case 0:
1802 printf("default PIO mode");
1803 break;
1804 case 1:
1805 printf("default PIO mode, disable IORDY");
1806 break;
1807 case 8:
1808 case 9:
1809 case 10:
1810 case 11:
1811 case 12:
1812 case 13:
1813 case 14:
1814 case 15:
1815 printf("PIO flow control mode%u", xfermode-8);
1816 break;
1817 case 16:
1818 case 17:
1819 case 18:
1820 case 19:
1821 case 20:
1822 case 21:
1823 case 22:
1824 case 23:
1825 printf("singleword DMA mode%u", xfermode-16);
1826 break;
1827 case 32:
1828 case 33:
1829 case 34:
1830 case 35:
1831 case 36:
1832 case 37:
1833 case 38:
1834 case 39:
1835 printf("multiword DMA mode%u", xfermode-32);
1836 break;
1837 case 64:
1838 case 65:
1839 case 66:
1840 case 67:
1841 case 68:
1842 case 69:
1843 case 70:
1844 case 71:
1845 printf("UltraDMA mode%u", xfermode-64);
1846 break;
1847 default:
1848 printf("unknown, probably not valid");
1849 break;
1850 }
1851 printf(")\n");
1852}
1853#endif /* HDIO_DRIVE_CMD */
1854
1855#ifndef VXVM_MAJOR
1856#define VXVM_MAJOR 199
1857#endif
1858
1859#ifndef CCISS_MAJOR
1860#define CCISS_MAJOR 104
1861#endif
1862
1863static void process_dev (char *devname)
1864{
1865 int fd;
1866 static long parm, multcount;
1867 struct stat stat_buf;
1868#ifndef HDIO_DRIVE_CMD
1869 int force_operation = 0;
1870#endif
1871 if (stat(devname,&stat_buf))
1872 bb_perror_msg_and_die(devname);
1873
1874 switch(major(stat_buf.st_rdev))
1875 {
1876#ifdef SCSI_DISK0_MAJOR
1877 case (SCSI_DISK0_MAJOR):
1878 case (SCSI_DISK1_MAJOR):
1879 case (SCSI_DISK2_MAJOR):
1880 case (SCSI_DISK3_MAJOR):
1881 case (SCSI_DISK4_MAJOR):
1882 case (SCSI_DISK5_MAJOR):
1883 case (SCSI_DISK6_MAJOR):
1884 case (SCSI_DISK7_MAJOR):
1885#else
1886 case (SCSI_DISK_MAJOR):
1887#endif
1888#ifdef MD_MAJOR
1889 case (MD_MAJOR):
1890#endif
1891 case (VXVM_MAJOR):
1892#ifdef LVM_BLK_MAJOR
1893 case (LVM_BLK_MAJOR):
1894#endif
1895 case (CCISS_MAJOR):
1896 is_scsi_hd = 1;
1897 break;
1898#ifdef XT_DISK_MAJOR
1899 case (XT_DISK_MAJOR):
1900 is_xt_hd = 1;
1901 break;
1902#endif
1903 case IDE0_MAJOR:
1904 case IDE1_MAJOR:
1905#ifdef IDE2_MAJOR
1906 case IDE2_MAJOR:
1907#endif
1908#ifdef IDE3_MAJOR
1909 case IDE3_MAJOR:
1910#endif
1911#ifdef IDE4_MAJOR
1912 case IDE4_MAJOR:
1913#endif
1914#ifdef IDE5_MAJOR
1915 case IDE5_MAJOR:
1916#endif
1917#ifdef IDE6_MAJOR
1918 case IDE6_MAJOR:
1919#endif
1920#ifdef IDE7_MAJOR
1921 case IDE7_MAJOR:
1922#endif
1923#ifdef IDE8_MAJOR
1924 case IDE8_MAJOR:
1925#endif
1926#ifdef IDE9_MAJOR
1927 case IDE9_MAJOR:
1928#endif
1929 break; /* do nothing */
1930 default:
1931 bb_error_msg_and_die("%s not supported",devname);
1932 }
1933
1934 fd = bb_xopen (devname, O_RDONLY|O_NONBLOCK);
1935 if_printf( (!quiet),"\n%s:\n", devname);
1936
1937 if (set_readahead)
1938 {
1939 if_printf(get_readahead," setting fs readahead to %ld\n", Xreadahead);
1940 bb_ioctl(fd, BLKRASET,(int *)Xreadahead,"BLKRASET");
1941 }
1942#ifdef CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
1943 if (unregister_hwif)
1944 {
1945 no_scsi();
1946 printf(" attempting to unregister hwif#%u\n", hwif);
1947 bb_ioctl(fd, HDIO_UNREGISTER_HWIF,(int *)hwif,"HDIO_UNREGISTER_HWIF");
1948 }
1949#endif
1950#ifdef CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF
1951 if (scan_hwif)
1952 {
1953 int args[3];
1954 no_scsi();
1955 printf(" attempting to scan hwif (0x%x, 0x%x, %u)\n", hwif_data, hwif_ctrl, hwif_irq);
1956 args[0] = hwif_data;
1957 args[1] = hwif_ctrl;
1958 args[2] = hwif_irq;
1959 bb_ioctl(fd, HDIO_SCAN_HWIF, args, "HDIO_SCAN_HWIF");
1960 }
1961#endif
1962 if (set_piomode)
1963 {
1964 no_scsi();
1965 no_xt();
1966
1967 if (noisy_piomode)
1968 {
1969 printf(" attempting to ");
1970 if (piomode == 255)
1971 printf("auto-tune PIO mode\n");
1972 else if (piomode < 100)
1973 printf("set PIO mode to %d\n", piomode);
1974 else if (piomode < 200)
1975 printf("set MDMA mode to %d\n", (piomode-100));
1976 else
1977 printf("set UDMA mode to %d\n", (piomode-200));
1978 }
1979 bb_ioctl(fd, HDIO_SET_PIO_MODE, (int *)piomode, "HDIO_SET_PIO_MODE");
1980 }
1981 if (set_io32bit)
1982 {
1983 no_scsi();
1984 no_xt();
1985 if_printf(get_io32bit," setting 32-bit IO_support flag to %ld\n", io32bit);
1986 bb_ioctl(fd, HDIO_SET_32BIT, (int *)io32bit, "HDIO_SET_32BIT");
1987 }
1988 if (set_mult)
1989 {
1990 no_scsi();
1991 no_xt();
1992 if_printf(get_mult, " setting multcount to %ld\n", mult);
1993 if(ioctl(fd, HDIO_SET_MULTCOUNT, mult))
1994 bb_perror_msg("HDIO_SET_MULTCOUNT");
1995#ifndef HDIO_DRIVE_CMD
1996 else
1997 force_operation = 1;
1998#endif
1999 }
2000 if (set_readonly)
2001 {
2002 if_printf_on_off(get_readonly," setting readonly to %ld", readonly);
2003 bb_ioctl(fd, BLKROSET, &readonly, "BLKROSET");
2004 }
2005 if (set_unmask)
2006 {
2007 no_scsi();
2008 no_xt();
2009 if_printf_on_off(get_unmask," setting unmaskirq to %ld", unmask);
2010 bb_ioctl(fd, HDIO_SET_UNMASKINTR, (int *)unmask, "HDIO_SET_UNMASKINTR");
2011 }
2012#ifdef CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA
2013 if (set_dma)
2014 {
2015 no_scsi();
2016 if_printf_on_off(get_dma," setting using_dma to %ld", dma);
2017 bb_ioctl(fd, HDIO_SET_DMA, (int *)dma, "HDIO_SET_DMA");
2018 }
2019#endif /* CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA */
2020 if (set_dma_q)
2021 {
2022 no_scsi();
2023 if_printf_on_off(get_dma_q," setting DMA queue_depth to %ld", dma_q);
2024 bb_ioctl(fd, HDIO_SET_QDMA, (int *)dma_q, "HDIO_SET_QDMA");
2025 }
2026 if (set_nowerr)
2027 {
2028 no_scsi();
2029 no_xt();
2030 if_printf_on_off(get_nowerr," setting nowerr to %ld", nowerr);
2031 bb_ioctl(fd, HDIO_SET_NOWERR, (int *)nowerr,"HDIO_SET_NOWERR");
2032 }
2033 if (set_keep)
2034 {
2035 no_scsi();
2036 no_xt();
2037 if_printf_on_off(get_keep," setting keep_settings to %ld", keep);
2038 bb_ioctl(fd, HDIO_SET_KEEPSETTINGS, (int *)keep,"HDIO_SET_KEEPSETTINGS");
2039 }
2040#ifdef HDIO_DRIVE_CMD
2041 if (set_doorlock)
2042 {
2043 unsigned char args[4] = {0,0,0,0};
2044 no_scsi();
2045 no_xt();
2046
2047 args[0] = doorlock ? WIN_DOORLOCK : WIN_DOORUNLOCK;
2048 if_printf_on_off(get_doorlock," setting drive doorlock to %ld", doorlock);
2049 bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(doorlock)");
2050 }
2051 if (set_dkeep)
2052 {
2053 /* lock/unlock the drive's "feature" settings */
2054 unsigned char args[4] = {WIN_SETFEATURES,0,0,0};
2055 no_scsi();
2056 no_xt();
2057
2058 if_printf_on_off(get_dkeep," setting drive keep features to %ld", dkeep);
2059 args[2] = dkeep ? 0x66 : 0xcc;
2060 bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(keepsettings)");
2061 }
2062 if (set_defects)
2063 {
2064 unsigned char args[4] = {WIN_SETFEATURES,0,0x04,0};
2065 no_scsi();
2066 args[2] = defects ? 0x04 : 0x84;
2067 if_printf(get_defects," setting drive defect-mgmt to %ld\n", defects);
2068 bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(defectmgmt)");
2069 }
2070 if (set_prefetch)
2071 {
2072 unsigned char args[4] = {WIN_SETFEATURES,0,0xab,0};
2073 no_scsi();
2074 no_xt();
2075
2076 args[1] = prefetch;
2077 if_printf(get_prefetch," setting drive prefetch to %ld\n", prefetch);
2078 bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setprefetch)");
2079 }
2080 if (set_xfermode)
2081 {
2082 unsigned char args[4] = {WIN_SETFEATURES,0,3,0};
2083 no_scsi();
2084 no_xt();
2085
2086 args[1] = xfermode_requested;
2087 if (get_xfermode)
2088 {
2089 printf(" setting xfermode to %d", xfermode_requested);
2090 interpret_xfermode(xfermode_requested);
2091 }
2092 bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD(setxfermode)");
2093 }
2094 if (set_lookahead)
2095 {
2096 unsigned char args[4] = {WIN_SETFEATURES,0,0,0};
2097 no_scsi();
2098 no_xt();
2099
2100 args[2] = lookahead ? 0xaa : 0x55;
2101 if_printf_on_off(get_lookahead," setting drive read-lookahead to %ld", lookahead);
2102 bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setreadahead)");
2103 }
2104 if (set_apmmode)
2105 {
2106 unsigned char args[4] = {WIN_SETFEATURES,0,0,0};
2107 no_scsi();
2108 apmmode=check_if_min_and_set_val(apmmode,1);
2109 apmmode=check_if_maj_and_set_val(apmmode,255);
2110 if_printf(get_apmmode," setting APM level to");
2111 if (apmmode==255)
2112 {
2113 /* disable Advanced Power Management */
2114 args[2] = 0x85; /* feature register */
2115 if_printf(get_apmmode," disabled\n");
2116 }
2117 else
2118 {
2119 /* set Advanced Power Management mode */
2120 args[2] = 0x05; /* feature register */
2121 args[1] = apmmode; /* sector count register */
2122 if_printf(get_apmmode," 0x%02lX (%ld)\n",apmmode,apmmode);
2123 }
2124 bb_ioctl(fd, HDIO_DRIVE_CMD, &args,"HDIO_DRIVE_CMD");
2125 }
2126 if (set_wcache)
2127 {
2128#ifdef DO_FLUSHCACHE
2129#ifndef WIN_FLUSHCACHE
2130#define WIN_FLUSHCACHE 0xe7
2131#endif
2132 unsigned char flushcache[4] = {WIN_FLUSHCACHE,0,0,0};
2133#endif /* DO_FLUSHCACHE */
2134 unsigned char args[4] = {WIN_SETFEATURES,0,0,0};
2135 no_scsi();
2136 no_xt();
2137 args[2] = wcache ? 0x02 : 0x82;
2138 if_printf_on_off(get_wcache," setting drive write-caching to %ld", wcache);
2139#ifdef DO_FLUSHCACHE
2140 if (!wcache && ioctl(fd, HDIO_DRIVE_CMD, &flushcache))
2141 bb_perror_msg ("HDIO_DRIVE_CMD(flushcache)");
2142#endif /* DO_FLUSHCACHE */
2143 bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setcache)");
2144#ifdef DO_FLUSHCACHE
2145 if (!wcache && ioctl(fd, HDIO_DRIVE_CMD, &flushcache))
2146 bb_perror_msg ("HDIO_DRIVE_CMD(flushcache)");
2147#endif /* DO_FLUSHCACHE */
2148 }
2149 if (set_standbynow)
2150 {
2151#ifndef WIN_STANDBYNOW1
2152#define WIN_STANDBYNOW1 0xE0
2153#endif
2154#ifndef WIN_STANDBYNOW2
2155#define WIN_STANDBYNOW2 0x94
2156#endif
2157 unsigned char args1[4] = {WIN_STANDBYNOW1,0,0,0};
2158 unsigned char args2[4] = {WIN_STANDBYNOW2,0,0,0};
2159 no_scsi();
2160 if_printf(get_standbynow," issuing standby command\n");
2161 if (ioctl(fd, HDIO_DRIVE_CMD, &args1)
2162 && ioctl(fd, HDIO_DRIVE_CMD, &args2))
2163 bb_perror_msg("HDIO_DRIVE_CMD(standby)");
2164 }
2165 if (set_sleepnow)
2166 {
2167#ifndef WIN_SLEEPNOW1
2168#define WIN_SLEEPNOW1 0xE6
2169#endif
2170#ifndef WIN_SLEEPNOW2
2171#define WIN_SLEEPNOW2 0x99
2172#endif
2173 unsigned char args1[4] = {WIN_SLEEPNOW1,0,0,0};
2174 unsigned char args2[4] = {WIN_SLEEPNOW2,0,0,0};
2175 no_scsi();
2176 if_printf(get_sleepnow," issuing sleep command\n");
2177 if (ioctl(fd, HDIO_DRIVE_CMD, &args1)
2178 && ioctl(fd, HDIO_DRIVE_CMD, &args2))
2179 bb_perror_msg("HDIO_DRIVE_CMD(sleep)");
2180 }
2181 if (set_seagate)
2182 {
2183 unsigned char args[4] = {0xfb,0,0,0};
2184 no_scsi();
2185 no_xt();
2186 if_printf(get_seagate," disabling Seagate auto powersaving mode\n");
2187 bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(seagatepwrsave)");
2188 }
2189 if (set_standby)
2190 {
2191 unsigned char args[4] = {WIN_SETIDLE1,standby_requested,0,0};
2192 no_scsi();
2193 no_xt();
2194 if (get_standby)
2195 {
2196 printf(" setting standby to %lu", standby_requested);
2197 interpret_standby(standby_requested);
2198 }
2199 bb_ioctl(fd, HDIO_DRIVE_CMD, &args, "HDIO_DRIVE_CMD(setidle1)");
2200 }
2201#else /* HDIO_DRIVE_CMD */
2202 if (force_operation)
2203 {
2204 char buf[512];
2205 flush_buffer_cache(fd);
2206 if (-1 == read(fd, buf, sizeof(buf)))
2207 bb_error_msg("access failed");
2208 }
2209#endif /* HDIO_DRIVE_CMD */
2210
2211 if (!flagcount)
2212 verbose = 1;
2213
2214 if ((verbose && !is_scsi_hd && !is_xt_hd) || get_mult || get_identity)
2215 {
2216 no_scsi();
2217 multcount = -1;
2218 if (ioctl(fd, HDIO_GET_MULTCOUNT, &multcount))
2219 {
2220 if ((verbose && !is_xt_hd) || get_mult)
2221 bb_perror_msg("HDIO_GET_MULTCOUNT");
2222 }
2223 else if (verbose | get_mult)
2224 {
2225 printf(" multcount = %2ld", multcount);
2226 on_off(multcount);
2227 }
2228 }
2229 if ((verbose && !is_scsi_hd && !is_xt_hd) || get_io32bit)
2230 {
2231 no_scsi();
2232 no_xt();
2233 if(ioctl(fd, HDIO_GET_32BIT, &parm))
2234 bb_perror_msg("HDIO_GET_32BIT");
2235 else
2236 {
2237 printf(" IO_support =%3ld (", parm);
2238 switch (parm)
2239 {
2240 case 0:
2241 printf("default ");
2242 case 2:
2243 printf("16-bit)\n");
2244 break;
2245 case 1:
2246 printf("32-bit)\n");
2247 break;
2248 case 3:
2249 printf("32-bit w/sync)\n");
2250 break;
2251 case 8:
2252 printf("Request-Queue-Bypass)\n");
2253 break;
2254 default:
2255 printf("\?\?\?)\n");
2256 /*esac*/
2257 }
2258 }
2259 }
2260 if ((verbose && !is_scsi_hd && !is_xt_hd) || get_unmask)
2261 {
2262 no_scsi();
2263 no_xt();
2264 bb_ioctl_on_off(fd, HDIO_GET_UNMASKINTR,(unsigned long *)parm,
2265 "HDIO_GET_UNMASKINTR"," unmaskirq = %2ld");
2266 }
2267
2268
2269#ifdef CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA
2270 if ((verbose && !is_scsi_hd) || get_dma) {
2271 no_scsi();
2272 if(ioctl(fd, HDIO_GET_DMA, &parm))
2273 bb_perror_msg("HDIO_GET_DMA");
2274 else
2275 {
2276 printf(" using_dma = %2ld", parm);
2277 if (parm == 8)
2278 printf(" (DMA-Assisted-PIO)\n");
2279 else
2280 on_off(parm);
2281 }
2282 }
2283#endif
2284 if (get_dma_q)
2285 {
2286 no_scsi();
2287 bb_ioctl_on_off (fd, HDIO_GET_QDMA,(unsigned long *)parm,
2288 "HDIO_GET_QDMA"," queue_depth = %2ld");
2289 }
2290 if ((verbose && !is_scsi_hd && !is_xt_hd) || get_keep)
2291 {
2292 no_scsi();
2293 no_xt();
2294 bb_ioctl_on_off (fd, HDIO_GET_KEEPSETTINGS,(unsigned long *)parm,
2295 "HDIO_GET_KEEPSETTINGS"," keepsettings = %2ld");
2296 }
2297
2298 if (get_nowerr)
2299 {
2300 no_scsi();
2301 no_xt();
2302 bb_ioctl_on_off (fd, HDIO_GET_NOWERR,(unsigned long *)&parm,
2303 " HDIO_GET_NOWERR"," nowerr = %2ld");
2304 }
2305 if (verbose || get_readonly)
2306 {
2307 bb_ioctl_on_off(fd, BLKROGET,(unsigned long *)parm,
2308 " BLKROGET"," readonly = %2ld");
2309 }
2310 if ((verbose && !is_scsi_hd) || get_readahead)
2311 {
2312 bb_ioctl_on_off (fd, BLKRAGET, (unsigned long *) parm,
2313 " BLKRAGET"," readahead = %2ld");
2314 }
2315 if (verbose || get_geom)
2316 {
2317 static const char msg[] = " geometry = %u/%u/%u, sectors = %ld, start = %ld\n";
2318 static struct hd_geometry g;
2319#ifdef HDIO_GETGEO_BIG
2320 static struct hd_big_geometry bg;
2321#endif
2322
2323 if (ioctl(fd, BLKGETSIZE, &parm))
2324 bb_perror_msg("BLKGETSIZE");
2325#ifdef HDIO_GETGEO_BIG
2326 else if (!ioctl(fd, HDIO_GETGEO_BIG, &bg))
2327 printf(msg, bg.cylinders, bg.heads, bg.sectors, parm, bg.start);
2328#endif
2329 else if (ioctl(fd, HDIO_GETGEO, &g))
2330 bb_perror_msg("HDIO_GETGEO");
2331 else
2332 printf(msg, g.cylinders, g.heads, g.sectors, parm, g.start);
2333 }
2334#ifdef HDIO_DRIVE_CMD
2335 if (get_powermode)
2336 {
2337#ifndef WIN_CHECKPOWERMODE1
2338#define WIN_CHECKPOWERMODE1 0xE5
2339#endif
2340#ifndef WIN_CHECKPOWERMODE2
2341#define WIN_CHECKPOWERMODE2 0x98
2342#endif
2343 unsigned char args[4] = {WIN_CHECKPOWERMODE1,0,0,0};
2344 const char *state;
2345 no_scsi();
2346 if (ioctl(fd, HDIO_DRIVE_CMD, &args)
2347 && (args[0] = WIN_CHECKPOWERMODE2) /* try again with 0x98 */
2348 && ioctl(fd, HDIO_DRIVE_CMD, &args))
2349 {
2350 if (errno != EIO || args[0] != 0 || args[1] != 0)
2351 state = "unknown";
2352 else
2353 state = "sleeping";
2354 }
2355 else
2356 state = (args[2] == 255) ? "active/idle" : "standby";
2357
2358 printf(" drive state is: %s\n", state);
2359 }
2360#endif
2361#ifdef CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET
2362 if (perform_reset)
2363 {
2364 no_scsi();
2365 no_xt();
2366 bb_ioctl(fd, HDIO_DRIVE_RESET, NULL, "HDIO_DRIVE_RESET");
2367 }
2368#endif /* CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET */
2369#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
2370 if (perform_tristate)
2371 {
2372 unsigned char args[4] = {0,tristate,0,0};
2373 no_scsi();
2374 no_xt();
2375 bb_ioctl(fd, HDIO_TRISTATE_HWIF, &args, "HDIO_TRISTATE_HWIF");
2376 }
2377#endif /* CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF */
2378#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
2379 if (get_identity)
2380 {
2381 static struct hd_driveid id;
2382
2383 no_scsi();
2384 no_xt();
2385
2386 if (!ioctl(fd, HDIO_GET_IDENTITY, &id))
2387 {
2388 if (multcount != -1)
2389 {
2390 id.multsect = multcount;
2391 id.multsect_valid |= 1;
2392 }
2393 else
2394 id.multsect_valid &= ~1;
2395 dump_identity(&id);
2396 }
2397 else if (errno == -ENOMSG)
2398 printf(" no identification info available\n");
2399 else
2400 bb_perror_msg("HDIO_GET_IDENTITY");
2401 }
2402
2403 if (get_IDentity)
2404 {
2405 unsigned char args[4+512] = {WIN_IDENTIFY,0,0,1,};
2406 unsigned i;
2407
2408 no_scsi();
2409 no_xt();
2410
2411 if (ioctl(fd, HDIO_DRIVE_CMD, &args))
2412 {
2413 args[0] = WIN_PIDENTIFY;
2414 if (ioctl(fd, HDIO_DRIVE_CMD, &args))
2415 {
2416 bb_perror_msg("HDIO_DRIVE_CMD(identify)");
2417 goto identify_abort;
2418 }
2419 }
2420 for(i=0; i<(sizeof args)/2; i+=2)
2421 __le16_to_cpus((uint16_t *)(&args[i]));
2422
2423 identify((void *)&args[4], NULL);
2424identify_abort:
2425 /* VOID */;
2426 }
2427#endif
2428#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
2429 if (set_busstate)
2430 {
2431 no_scsi();
2432 if (get_busstate)
2433 {
2434 printf(" setting bus state to %d", busstate);
2435 bus_state_value(busstate);
2436 }
2437 bb_ioctl(fd, HDIO_SET_BUSSTATE, (int *)busstate, "HDIO_SET_BUSSTATE");
2438 }
2439#endif
2440#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
2441 if (get_busstate)
2442 {
2443 no_scsi();
2444 if (ioctl(fd, HDIO_GET_BUSSTATE, &parm))
2445 bb_perror_msg("HDIO_GET_BUSSTATE");
2446 else
2447 {
2448 printf(" busstate = %2ld", parm);
2449 bus_state_value(parm);
2450 }
2451 }
2452#endif
2453 if (reread_partn)
2454 bb_ioctl(fd, BLKRRPART, NULL, "BLKRRPART");
2455
2456
2457 if (do_ctimings)
2458 do_time(0,fd); /*time cache */
2459 if (do_timings)
2460 do_time(1,fd); /*time device */
2461 if (do_flush)
2462 flush_buffer_cache (fd);
2463 close (fd);
2464}
2465
2466static char * GET_NUMBER(char *p, unsigned long *flag, unsigned long *num)
2467{
2468 *num = 0;
2469 while (isdigit(*p)) {
2470 *flag = 1;
2471 *num = (*num * 10) + (*p++ - '0');
2472 }
2473 return p;
2474}
2475
2476static char * GET_STRING(char *p, unsigned long *flag, int *num)
2477{
2478 char *tmpstr;
2479 char name[32];
2480 tmpstr = name;
2481 tmpstr[0] = '\0';
2482 while (isalnum(*p) && (tmpstr - name) < 31) {
2483 tmpstr[0] = *p++;
2484 tmpstr[1] = '\0';
2485 ++tmpstr;
2486 }
2487 *num = translate_xfermode(name);
2488 if (*num == -1)
2489 *flag = 0;
2490 else
2491 *flag = 1;
2492 return p;
2493}
2494
2495#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
2496static int fromhex (unsigned char c)
2497{
2498 if (c >= 'a' && c <= 'f')
2499 return 10 + (c - 'a');
2500 if (c >= '0' && c <= '9')
2501 return (c - '0');
2502 bb_error_msg_and_die("bad char: '%c' 0x%02x", c, c);
2503}
2504
2505static int identify_from_stdin (void)
2506{
2507 unsigned short sbuf[800];
2508 unsigned char buf[1600], *b = (unsigned char *)buf;
2509 int i, count = read(0, buf, 1280);
2510
2511 if (count != 1280)
2512 bb_error_msg_and_die("read(1280 bytes) failed (rc=%d)", count);
2513 for (i = 0; count >= 4; ++i)
2514 {
2515 sbuf[i] = (fromhex(b[0]) << 12) | (fromhex(b[1]) << 8) | (fromhex(b[2]) << 4) | fromhex(b[3]);
2516 __le16_to_cpus((uint16_t *)(&sbuf[i]));
2517 b += 5;
2518 count -= 5;
2519 }
2520 identify(sbuf, NULL);
2521 return 0;
2522}
2523#endif
2524
2525static void missing_arg(int arg, char c, char* add)
2526{
2527 if (!arg)
2528 bb_error_msg("-%c: missing value %s", c, (add!=NULL)? add :"");
2529}
2530
2531/* our main() routine: */
2532int hdparm_main(int argc, char **argv)
2533{
2534 char c, *p;
2535
2536 ++argv;
2537 if (!--argc)
2538 bb_show_usage();
2539
2540 while (argc--)
2541 {
2542#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
2543 if (!strcmp("-Istdin", *argv))
2544 {
2545 exit(identify_from_stdin());
2546 }
2547#endif
2548 p = *argv++;
2549 if (*p == '-')
2550 {
2551 if (!*++p)
2552 bb_show_usage();
2553 while ((c = *p++))
2554 {
2555 ++flagcount;
2556 switch (c)
2557 {
2558 case 'V':
2559 /*bb_error_msg_and_die("%s", VERSION);*/
2560 /* We have to return 0 here and not 1 */
2561 printf("%s %s\n",bb_applet_name, VERSION);
2562 exit(EXIT_SUCCESS);
2563 case 'v':
2564 verbose = 1;
2565 break;
2566#ifdef CONFIG_FEATURE_HDPARM_GET_IDENTITY
2567 case 'I':
2568 get_IDentity = 1;
2569 break;
2570 case 'i':
2571 get_identity = 1;
2572 break;
2573#endif
2574 case 'g':
2575 get_geom = 1;
2576 break;
2577 case 'f':
2578 do_flush = 1;
2579 break;
2580 case 'q':
2581 quiet = 1;
2582 noisy = 0;
2583 break;
2584 case 'u':
2585 get_unmask = noisy;
2586 noisy = 1;
2587 if (!*p && argc && isdigit(**argv))
2588 p = *argv++, --argc;
2589 if((set_unmask = set_flag(p,'1'))==1)
2590 unmask = *p++ - '0';
2591 break;
2592#ifdef CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA
2593 case 'd':
2594 get_dma = noisy;
2595 noisy = 1;
2596 if (!*p && argc && isdigit(**argv))
2597 p = *argv++, --argc;
2598 if((set_dma = set_flag(p,'9'))==1)
2599 dma = *p++ - '0';
2600 break;
2601#endif /* CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA */
2602 case 'n':
2603 get_nowerr = noisy;
2604 noisy = 1;
2605 if (!*p && argc && isdigit(**argv))
2606 p = *argv++, --argc;
2607 if((set_nowerr = set_flag(p,'1'))==1)
2608 nowerr = *p++ - '0';
2609 break;
2610 case 'p':
2611 noisy_piomode = noisy;
2612 noisy = 1;
2613 if (!*p && argc && isalnum(**argv))
2614 p = *argv++, --argc;
2615 p=GET_STRING(p,&set_piomode,&piomode);
2616 break;
2617 case 'r':
2618 get_readonly = noisy;
2619 noisy = 1;
2620 if (!*p && argc && isdigit(**argv))
2621 p = *argv++, --argc;
2622 if((set_readonly = set_flag(p,'1'))==1)
2623 readonly = *p++ - '0';
2624 break;
2625 case 'm':
2626 get_mult = noisy;
2627 noisy = 1;
2628 if (!*p && argc && isalnum(**argv))
2629 p = *argv++, --argc;
2630 p=GET_NUMBER(p,&set_mult,&mult);
2631 break;
2632 case 'c':
2633 get_io32bit = noisy;
2634 noisy = 1;
2635 if (!*p && argc && isalnum(**argv))
2636 p = *argv++, --argc;
2637 p=GET_NUMBER(p,&set_io32bit,&io32bit);
2638 break;
2639#ifdef HDIO_DRIVE_CMD
2640 case 'S':
2641 get_standby = noisy;
2642 noisy = 1;
2643 if (!*p && argc && isalnum(**argv))
2644 p = *argv++, --argc;
2645 p=GET_NUMBER(p,&set_standby,&standby_requested);
2646 missing_arg(set_standby, c, NULL);
2647 break;
2648
2649 case 'D':
2650 get_defects = noisy;
2651 noisy = 1;
2652 if (!*p && argc && isalnum(**argv))
2653 p = *argv++, --argc;
2654 p=GET_NUMBER(p,&set_defects,&defects);
2655 missing_arg(set_defects, c, NULL);
2656 break;
2657 case 'P':
2658 get_prefetch = noisy;
2659 noisy = 1;
2660 if (!*p && argc && isalnum(**argv))
2661 p = *argv++, --argc;
2662 p=GET_NUMBER(p,&set_prefetch,&prefetch);
2663 missing_arg(set_prefetch, c, NULL);
2664 break;
2665
2666 case 'X':
2667 get_xfermode = noisy;
2668 noisy = 1;
2669 if (!*p && argc && isalnum(**argv))
2670 p = *argv++, --argc;
2671 p=GET_STRING(p,&set_xfermode,&xfermode_requested);
2672 missing_arg(set_xfermode, c, NULL);
2673 break;
2674
2675 case 'K':
2676 get_dkeep = noisy;
2677 noisy = 1;
2678 if (!*p && argc && isdigit(**argv))
2679 p = *argv++, --argc;
2680 if((set_dkeep = set_flag(p,'1'))==1)
2681 dkeep = *p++ - '0';
2682 else
2683 goto missing_arg_error;
2684 break;
2685
2686 case 'A':
2687 get_lookahead = noisy;
2688 noisy = 1;
2689 if (!*p && argc && isdigit(**argv))
2690 p = *argv++, --argc;
2691 if((set_lookahead = set_flag(p,'1'))==1)
2692 lookahead = *p++ - '0';
2693 else
2694 goto missing_arg_error;
2695 break;
2696
2697 case 'L':
2698 get_doorlock = noisy;
2699 noisy = 1;
2700 if (!*p && argc && isdigit(**argv))
2701 p = *argv++, --argc;
2702 if((set_doorlock = set_flag(p,'1'))==1)
2703 doorlock = *p++ - '0';
2704 else
2705 goto missing_arg_error;
2706 break;
2707
2708 case 'W':
2709 get_wcache = noisy;
2710 noisy = 1;
2711 if (!*p && argc && isdigit(**argv))
2712 p = *argv++, --argc;
2713 if((set_wcache = set_flag(p,'1'))==1)
2714 wcache = *p++ - '0';
2715 else
2716missing_arg_error:
2717 missing_arg(1, c, "(0/1)");
2718 break;
2719
2720 case 'C':
2721 get_powermode = noisy;
2722 noisy = 1;
2723 break;
2724
2725 case 'y':
2726 get_standbynow = noisy;
2727 noisy = 1;
2728 set_standbynow = 1;
2729 break;
2730
2731 case 'Y':
2732 get_sleepnow = noisy;
2733 noisy = 1;
2734 set_sleepnow = 1;
2735 break;
2736
2737 case 'z':
2738 reread_partn = 1;
2739 break;
2740
2741 case 'Z':
2742 get_seagate = noisy;
2743 noisy = 1;
2744 set_seagate = 1;
2745 break;
2746#endif /* HDIO_DRIVE_CMD */
2747 case 'k':
2748 get_keep = noisy;
2749 noisy = 1;
2750 if (!*p && argc && isdigit(**argv))
2751 p = *argv++, --argc;
2752 if((set_keep = set_flag(p,'1'))==1)
2753 keep = *p++ - '0';
2754 break;
2755#ifdef CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
2756 case 'U':
2757 if (!*p && argc && isdigit(**argv))
2758 p = *argv++, --argc;
2759 if(! p)
2760 goto expected_hwif_error; /* "expected hwif_nr" */
2761
2762 sscanf(p++, "%i", &hwif);
2763
2764 unregister_hwif = 1;
2765 break;
2766#endif /* CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF */
2767#ifdef CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF
2768 case 'R':
2769 if (!*p && argc && isdigit(**argv))
2770 p = *argv++, --argc;
2771 if(! p)
2772 goto expected_hwif_error; /* "expected hwif_data" */
2773
2774 sscanf(p++, "%i", &hwif_data);
2775
2776 if (argc && isdigit(**argv))
2777 p = *argv++, --argc;
2778 else
2779 goto expected_hwif_error; /* "expected hwif_ctrl" */
2780
2781 sscanf(p, "%i", &hwif_ctrl);
2782
2783 if (argc && isdigit(**argv))
2784 p = *argv++, --argc;
2785 else
2786expected_hwif_error:
2787 bb_error_msg_and_die("expected hwif value"); /* "expected hwif_irq" */
2788
2789 sscanf(p, "%i", &hwif_irq);
2790
2791 *p = '\0';
2792
2793 scan_hwif = 1;
2794 break;
2795#endif /* CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF */
2796 case 'Q':
2797#ifdef HDIO_GET_QDMA
2798 get_dma_q = noisy;
2799 noisy = 1;
2800#ifdef HDIO_SET_QDMA
2801 if (!*p && argc && isalnum(**argv))
2802 p = *argv++, --argc;
2803 p=GET_NUMBER(p,&set_dma_q,&dma_q);
2804#ifdef HDIO_GET_QDMA
2805 dma_q = -dma_q;
2806#endif
2807#endif
2808#endif
2809 break;
2810
2811#ifdef CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET
2812 case 'w':
2813 perform_reset = 1;
2814 break;
2815#endif /* CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET */
2816#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
2817 case 'x':
2818 if (!*p && argc && isdigit(**argv))
2819 p = *argv++, --argc;
2820 if((perform_tristate = set_flag(p,'1'))==1)
2821 tristate = *p++ - '0';
2822 else
2823 missing_arg(1, c, "(0/1)");
2824 break;
2825
2826#endif /* CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF */
2827 case 'a':
2828 get_readahead = noisy;
2829 noisy = 1;
2830 if (!*p && argc && isalnum(**argv))
2831 p = *argv++, --argc;
2832 p=GET_NUMBER(p,&set_readahead,&Xreadahead);
2833 break;
2834 case 'B':
2835 get_apmmode = noisy;
2836 noisy = 1;
2837 if (!*p && argc && isalnum(**argv))
2838 p = *argv++, --argc;
2839 p=GET_NUMBER(p,&set_apmmode,&apmmode);
2840 missing_arg(set_apmmode, c, "(1-255)");
2841 break;
2842 case 't':
2843 do_timings = 1;
2844 do_flush = 1;
2845 break;
2846 case 'T':
2847 do_ctimings = 1;
2848 do_flush = 1;
2849 break;
2850#ifdef CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
2851 case 'b':
2852 get_busstate = noisy;
2853 noisy = 1;
2854 if (!*p && argc && isdigit(**argv))
2855 p = *argv++, --argc;
2856 if((set_busstate = set_flag(p,'2'))==1)
2857 busstate = *p++ - '0';
2858 break;
2859#endif
2860 case 'h':
2861 default:
2862 bb_show_usage();
2863 }
2864 }
2865 if (!argc)
2866 bb_show_usage();
2867 } else {
2868 process_dev (p);
2869 }
2870 }
2871 return 0 ;
2872}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * last implementation for busybox
4 *
5 * Copyright (C) 2003-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <sys/types.h>
24#include <fcntl.h>
25#include <unistd.h>
26#include <stdlib.h>
27#include <utmp.h>
28#include <sys/stat.h>
29#include <errno.h>
30#include <string.h>
31#include <time.h>
32#include "busybox.h"
33
34#ifndef SHUTDOWN_TIME
35# define SHUTDOWN_TIME 254
36#endif
37
38/* Grr... utmp char[] members do not have to be nul-terminated.
39 * Do what we can while still keeping this reasonably small.
40 * Note: We are assuming the ut_id[] size is fixed at 4. */
41
42#if (UT_LINESIZE != 32) || (UT_NAMESIZE != 32) || (UT_HOSTSIZE != 256)
43#error struct utmp member char[] size(s) have changed!
44#endif
45
46extern int last_main(int argc, char **argv)
47{
48 struct utmp ut;
49 int n, file = STDIN_FILENO;
50
51 if (argc > 1) {
52 bb_show_usage();
53 }
54 file = bb_xopen(_PATH_WTMP, O_RDONLY);
55
56 printf("%-10s %-14s %-18s %-12.12s %s\n", "USER", "TTY", "HOST", "LOGIN", "TIME");
57 while ((n = safe_read(file, (void*)&ut, sizeof(struct utmp))) != 0) {
58
59 if (n != sizeof(struct utmp)) {
60 bb_perror_msg_and_die("short read");
61 }
62
63 if (strncmp(ut.ut_line, "~", 1) == 0) {
64 if (strncmp(ut.ut_user, "shutdown", 8) == 0)
65 ut.ut_type = SHUTDOWN_TIME;
66 else if (strncmp(ut.ut_user, "reboot", 6) == 0)
67 ut.ut_type = BOOT_TIME;
68 else if (strncmp(ut.ut_user, "runlevel", 7) == 0)
69 ut.ut_type = RUN_LVL;
70 } else {
71 if (!ut.ut_name[0] || strcmp(ut.ut_name, "LOGIN") == 0 ||
72 ut.ut_name[0] == 0)
73 {
74 /* Don't bother. This means we can't find how long
75 * someone was logged in for. Oh well. */
76 continue;
77 }
78 if (ut.ut_type != DEAD_PROCESS &&
79 ut.ut_name[0] && ut.ut_line[0])
80 {
81 ut.ut_type = USER_PROCESS;
82 }
83 if (strcmp(ut.ut_name, "date") == 0) {
84 if (ut.ut_line[0] == '|') ut.ut_type = OLD_TIME;
85 if (ut.ut_line[0] == '{') ut.ut_type = NEW_TIME;
86 }
87 }
88
89 if (ut.ut_type!=USER_PROCESS) {
90 switch (ut.ut_type) {
91 case OLD_TIME:
92 case NEW_TIME:
93 case RUN_LVL:
94 case SHUTDOWN_TIME:
95 continue;
96 case BOOT_TIME:
97 strcpy(ut.ut_line, "system boot");
98 break;
99 }
100 }
101
102 printf("%-10s %-14s %-18s %-12.12s\n", ut.ut_user, ut.ut_line, ut.ut_host,
103 ctime(&(ut.ut_tv.tv_sec)) + 4);
104 }
105
106 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
107}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * public domain -- Dave 'Kill a Cop' Cinege <dcinege@psychosis.com>
4 *
5 * makedevs
6 * Make ranges of device files quickly.
7 * known bugs: can't deal with alpha ranges
8 */
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <fcntl.h>
14#include <unistd.h>
15#include <sys/types.h>
16#include <sys/sysmacros.h> /* major() and minor() */
17#include "busybox.h"
18
19int makedevs_main(int argc, char **argv)
20{
21 mode_t mode;
22 char *basedev, *type, *nodname, buf[255];
23 int Smajor, Sminor, S, E;
24
25 if (argc < 7 || *argv[1]=='-')
26 bb_show_usage();
27
28 basedev = argv[1];
29 type = argv[2];
30 Smajor = atoi(argv[3]);
31 Sminor = atoi(argv[4]);
32 S = atoi(argv[5]);
33 E = atoi(argv[6]);
34 nodname = argc == 8 ? basedev : buf;
35
36 mode = 0660;
37
38 switch (type[0]) {
39 case 'c':
40 mode |= S_IFCHR;
41 break;
42 case 'b':
43 mode |= S_IFBLK;
44 break;
45 case 'f':
46 mode |= S_IFIFO;
47 break;
48 default:
49 bb_show_usage();
50 }
51
52 while (S <= E) {
53 int sz;
54
55 sz = snprintf(buf, sizeof(buf), "%s%d", basedev, S);
56 if(sz<0 || sz>=sizeof(buf)) /* libc different */
57 bb_error_msg_and_die("%s too large", basedev);
58
59 /* if mode != S_IFCHR and != S_IFBLK third param in mknod() ignored */
60
61 if (mknod(nodname, mode, makedev(Smajor, Sminor)))
62 bb_error_msg("Failed to create: %s", nodname);
63
64 if (nodname == basedev) /* ex. /dev/hda - to /dev/hda1 ... */
65 nodname = buf;
66 S++;
67 Sminor++;
68 }
69
70 return 0;
71}
72
73/*
74And this is what this program replaces. The shell is too slow!
75
76makedev () {
77local basedev=$1; local S=$2; local E=$3
78local major=$4; local Sminor=$5; local type=$6
79local sbase=$7
80
81 if [ ! "$sbase" = "" ]; then
82 mknod "$basedev" $type $major $Sminor
83 S=`expr $S + 1`
84 Sminor=`expr $Sminor + 1`
85 fi
86
87 while [ $S -le $E ]; do
88 mknod "$basedev$S" $type $major $Sminor
89 S=`expr $S + 1`
90 Sminor=`expr $Sminor + 1`
91 done
92}
93*/
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 @@
1/* vi: set sw=4 ts=4: */
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <sys/mtio.h>
6#include <sys/fcntl.h>
7#include "busybox.h"
8
9struct mt_opcodes {
10 char *name;
11 short value;
12};
13
14/* missing: eod/seod, stoptions, stwrthreshold, densities */
15static const struct mt_opcodes opcodes[] = {
16 {"bsf", MTBSF},
17 {"bsfm", MTBSFM},
18 {"bsr", MTBSR},
19 {"bss", MTBSS},
20 {"datacompression", MTCOMPRESSION},
21 {"eom", MTEOM},
22 {"erase", MTERASE},
23 {"fsf", MTFSF},
24 {"fsfm", MTFSFM},
25 {"fsr", MTFSR},
26 {"fss", MTFSS},
27 {"load", MTLOAD},
28 {"lock", MTLOCK},
29 {"mkpart", MTMKPART},
30 {"nop", MTNOP},
31 {"offline", MTOFFL},
32 {"rewoffline", MTOFFL},
33 {"ras1", MTRAS1},
34 {"ras2", MTRAS2},
35 {"ras3", MTRAS3},
36 {"reset", MTRESET},
37 {"retension", MTRETEN},
38 {"rewind", MTREW},
39 {"seek", MTSEEK},
40 {"setblk", MTSETBLK},
41 {"setdensity", MTSETDENSITY},
42 {"drvbuffer", MTSETDRVBUFFER},
43 {"setpart", MTSETPART},
44 {"tell", MTTELL},
45 {"wset", MTWSM},
46 {"unload", MTUNLOAD},
47 {"unlock", MTUNLOCK},
48 {"eof", MTWEOF},
49 {"weof", MTWEOF},
50 {0, 0}
51};
52
53extern int mt_main(int argc, char **argv)
54{
55 const char *file = "/dev/tape";
56 const struct mt_opcodes *code = opcodes;
57 struct mtop op;
58 struct mtpos position;
59 int fd, mode;
60
61 if (argc < 2) {
62 bb_show_usage();
63 }
64
65 if (strcmp(argv[1], "-f") == 0) {
66 if (argc < 4) {
67 bb_show_usage();
68 }
69 file = argv[2];
70 argv += 2;
71 argc -= 2;
72 }
73
74 while (code->name != 0) {
75 if (strcmp(code->name, argv[1]) == 0)
76 break;
77 code++;
78 }
79
80 if (code->name == 0) {
81 bb_error_msg("unrecognized opcode %s.", argv[1]);
82 return EXIT_FAILURE;
83 }
84
85 op.mt_op = code->value;
86 if (argc >= 3)
87 op.mt_count = atoi(argv[2]);
88 else
89 op.mt_count = 1; /* One, not zero, right? */
90
91 switch (code->value) {
92 case MTWEOF:
93 case MTERASE:
94 case MTWSM:
95 case MTSETDRVBUFFER:
96 mode = O_WRONLY;
97 break;
98
99 default:
100 mode = O_RDONLY;
101 break;
102 }
103
104 if ((fd = open(file, mode, 0)) < 0)
105 bb_perror_msg_and_die("%s", file);
106
107 switch (code->value) {
108 case MTTELL:
109 if (ioctl(fd, MTIOCPOS, &position) < 0)
110 bb_perror_msg_and_die("%s", file);
111 printf ("At block %d.\n", (int) position.mt_blkno);
112 break;
113
114 default:
115 if (ioctl(fd, MTIOCTOP, &op) != 0)
116 bb_perror_msg_and_die("%s", file);
117 break;
118 }
119
120 return EXIT_SUCCESS;
121}
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 @@
1/*-------------------------------------------------------------------------
2 * Filename: xmodem.c
3 * Version: $Id: rx.c,v 1.2 2004/03/15 08:28:46 andersen Exp $
4 * Copyright: Copyright (C) 2001, Hewlett-Packard Company
5 * Author: Christopher Hoover <ch@hpl.hp.com>
6 * Description: xmodem functionality for uploading of kernels
7 * and the like
8 * Created at: Thu Dec 20 01:58:08 PST 2001
9 *-----------------------------------------------------------------------*/
10/*
11 * xmodem.c: xmodem functionality for uploading of kernels and
12 * the like
13 *
14 * Copyright (C) 2001 Hewlett-Packard Laboratories
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 *
30 *
31 * This was originally written for blob and then adapted for busybox.
32 *
33 */
34
35#include <stdlib.h>
36#include <stdarg.h>
37#include <stdio.h>
38#include <unistd.h>
39#include <errno.h>
40#include <termios.h>
41#include <signal.h>
42#include <sys/types.h>
43#include <sys/stat.h>
44#include <fcntl.h>
45#include <string.h>
46#include "busybox.h"
47
48
49#define SOH 0x01
50#define STX 0x02
51#define EOT 0x04
52#define ACK 0x06
53#define NAK 0x15
54#define CAN 0x18
55#define BS 0x08
56
57/*
58
59Cf:
60
61 http://www.textfiles.com/apple/xmodem
62 http://www.phys.washington.edu/~belonis/xmodem/docxmodem.txt
63 http://www.phys.washington.edu/~belonis/xmodem/docymodem.txt
64 http://www.phys.washington.edu/~belonis/xmodem/modmprot.col
65
66*/
67
68#define TIMEOUT 1
69#define TIMEOUT_LONG 10
70#define MAXERRORS 10
71
72static inline void write_byte(int fd, char cc) {
73 write(fd, &cc, 1);
74}
75
76static inline void write_flush(int fd) {
77 tcdrain(fd);
78}
79
80static inline void read_flush(int fd) {
81 tcflush(fd, TCIFLUSH);
82}
83
84static int read_byte(int fd, unsigned int timeout) {
85 char buf[1];
86 int n;
87
88 alarm(timeout);
89
90 n = read(fd, &buf, 1);
91
92 alarm(0);
93
94 if (n == 1)
95 return buf[0] & 0xff;
96 else
97 return -1;
98}
99
100static int receive(char *error_buf, size_t error_buf_size,
101 int ttyfd, int filefd)
102{
103 char blockBuf[1024];
104 unsigned int errors = 0;
105 unsigned int wantBlockNo = 1;
106 unsigned int length = 0;
107 int docrc = 1;
108 char nak = 'C';
109 unsigned int timeout = TIMEOUT_LONG;
110
111#define note_error(fmt,args...) \
112 snprintf(error_buf, error_buf_size, fmt,##args)
113
114 read_flush(ttyfd);
115
116 /* Ask for CRC; if we get errors, we will go with checksum */
117 write_byte(ttyfd, nak);
118 write_flush(ttyfd);
119
120 for (;;) {
121 int blockBegin;
122 int blockNo, blockNoOnesCompl;
123 int blockLength;
124 int cksum = 0;
125 int crcHi = 0;
126 int crcLo = 0;
127
128 blockBegin = read_byte(ttyfd, timeout);
129 if (blockBegin < 0)
130 goto timeout;
131
132 timeout = TIMEOUT;
133 nak = NAK;
134
135 switch (blockBegin) {
136 case SOH:
137 case STX:
138 break;
139
140 case EOT:
141 write_byte(ttyfd, ACK);
142 write_flush(ttyfd);
143 goto done;
144
145 default:
146 goto error;
147 }
148
149 /* block no */
150 blockNo = read_byte(ttyfd, TIMEOUT);
151 if (blockNo < 0)
152 goto timeout;
153
154 /* block no one's compliment */
155 blockNoOnesCompl = read_byte(ttyfd, TIMEOUT);
156 if (blockNoOnesCompl < 0)
157 goto timeout;
158
159 if (blockNo != (255 - blockNoOnesCompl)) {
160 note_error("bad block ones compl");
161 goto error;
162 }
163
164 blockLength = (blockBegin == SOH) ? 128 : 1024;
165
166 {
167 int i;
168
169 for (i = 0; i < blockLength; i++) {
170 int cc = read_byte(ttyfd, TIMEOUT);
171 if (cc < 0)
172 goto timeout;
173 blockBuf[i] = cc;
174 }
175 }
176
177 if (docrc) {
178 crcHi = read_byte(ttyfd, TIMEOUT);
179 if (crcHi < 0)
180 goto timeout;
181
182 crcLo = read_byte(ttyfd, TIMEOUT);
183 if (crcLo < 0)
184 goto timeout;
185 } else {
186 cksum = read_byte(ttyfd, TIMEOUT);
187 if (cksum < 0)
188 goto timeout;
189 }
190
191 if (blockNo == ((wantBlockNo - 1) & 0xff)) {
192 /* a repeat of the last block is ok, just ignore it. */
193 /* this also ignores the initial block 0 which is */
194 /* meta data. */
195 goto next;
196 } else if (blockNo != (wantBlockNo & 0xff)) {
197 note_error("unexpected block no, 0x%08x, expecting 0x%08x", blockNo, wantBlockNo);
198 goto error;
199 }
200
201 if (docrc) {
202 int crc = 0;
203 int i, j;
204 int expectedCrcHi;
205 int expectedCrcLo;
206
207 for (i = 0; i < blockLength; i++) {
208 crc = crc ^ (int) blockBuf[i] << 8;
209 for (j = 0; j < 8; j++)
210 if (crc & 0x8000)
211 crc = crc << 1 ^ 0x1021;
212 else
213 crc = crc << 1;
214 }
215
216 expectedCrcHi = (crc >> 8) & 0xff;
217 expectedCrcLo = crc & 0xff;
218
219 if ((crcHi != expectedCrcHi) ||
220 (crcLo != expectedCrcLo)) {
221 note_error("crc error, expected 0x%02x 0x%02x, got 0x%02x 0x%02x", expectedCrcHi, expectedCrcLo, crcHi, crcLo);
222 goto error;
223 }
224 } else {
225 unsigned char expectedCksum = 0;
226 int i;
227
228 for (i = 0; i < blockLength; i++)
229 expectedCksum += blockBuf[i];
230
231 if (cksum != expectedCksum) {
232 note_error("checksum error, expected 0x%02x, got 0x%02x", expectedCksum, cksum);
233 goto error;
234 }
235 }
236
237 wantBlockNo++;
238 length += blockLength;
239
240 if (bb_full_write(filefd, blockBuf, blockLength) < 0) {
241 note_error("write to file failed: %m");
242 goto fatal;
243 }
244
245 next:
246 errors = 0;
247 write_byte(ttyfd, ACK);
248 write_flush(ttyfd);
249 continue;
250
251 error:
252 timeout:
253 errors++;
254 if (errors == MAXERRORS) {
255 /* Abort */
256 int i;
257
258 // if using crc, try again w/o crc
259 if (nak == 'C') {
260 nak = NAK;
261 errors = 0;
262 docrc = 0;
263 goto timeout;
264 }
265
266 note_error("too many errors; giving up");
267
268 fatal:
269 for (i = 0; i < 5; i ++)
270 write_byte(ttyfd, CAN);
271 for (i = 0; i < 5; i ++)
272 write_byte(ttyfd, BS);
273 write_flush(ttyfd);
274 return -1;
275 }
276
277 read_flush(ttyfd);
278 write_byte(ttyfd, nak);
279 write_flush(ttyfd);
280 }
281
282 done:
283 return length;
284
285#undef note_error
286}
287
288static void sigalrm_handler(int signum)
289{
290}
291
292int rx_main(int argc, char **argv)
293{
294 char *fn;
295 int ttyfd, filefd;
296 struct termios tty, orig_tty;
297 struct sigaction act;
298 int n;
299 char error_buf[256];
300
301 if (argc != 2)
302 bb_show_usage();
303
304 fn = argv[1];
305 ttyfd = open("/dev/tty", O_RDWR);
306 if (ttyfd < 0)
307 bb_error_msg_and_die("%s: open on /dev/tty failed: %m\n", argv[0]);
308
309 filefd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0666);
310 if (filefd < 0)
311 bb_error_msg_and_die("%s: open on %s failed: %m\n", argv[0], fn);
312
313 if (tcgetattr(ttyfd, &tty) < 0)
314 bb_error_msg_and_die("%s: tcgetattr failed: %m\n", argv[0]);
315
316 orig_tty = tty;
317
318 cfmakeraw(&tty);
319 tcsetattr(ttyfd, TCSAFLUSH, &tty);
320
321 memset(&act, 0, sizeof(act));
322 act.sa_handler = sigalrm_handler;
323 sigaction(SIGALRM, &act, 0);
324
325 n = receive(error_buf, sizeof(error_buf), ttyfd, filefd);
326
327 close(filefd);
328
329 tcsetattr(ttyfd, TCSAFLUSH, &orig_tty);
330
331 if (n < 0)
332 bb_error_msg_and_die("\n%s: receive failed:\n %s\n",
333 argv[0], error_buf);
334
335 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
336}
337
338/*
339Local Variables:
340c-file-style: "linux"
341c-basic-offset: 4
342tab-width: 4
343End:
344*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * strings implementation for busybox
4 *
5 * Copyright (c) 1980, 1987
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * Original copyright notice is retained at the end of this file.
23 *
24 * Modified for BusyBox by Erik Andersen <andersen@codepoet.org>
25 * Badly hacked by Tito Ragusa <farmatito@tiscali.it>
26 */
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <getopt.h>
32#include <unistd.h>
33#include <ctype.h>
34#include "busybox.h"
35
36#define ISSTR(ch) (isprint(ch) || ch == '\t')
37
38int strings_main(int argc, char **argv)
39{
40 int n=4, c, i, opt=0, status=EXIT_SUCCESS;
41 long t=0, count;
42 FILE *file = stdin;
43 char *string=NULL;
44 const char *fmt="%s: ";
45
46 while ((i = getopt(argc, argv, "afon:")) > 0)
47 switch(i)
48 {
49 case 'a':
50 break;
51 case 'f':
52 opt+=1;
53 break;
54 case 'o':
55 opt+=2;
56 break;
57 case 'n':
58 n = bb_xgetlarg(optarg, 10, 1, INT_MAX);
59 break;
60 default:
61 bb_show_usage();
62 }
63
64 argc -= optind;
65 argv += optind;
66
67 i=0;
68
69 string=xmalloc(n+1);
70 string[n]='\0';
71 n-=1;
72
73 if(argc==0)
74 {
75 fmt="{%s}: ";
76 *argv=(char *)bb_msg_standard_input;
77 goto pipe;
78 }
79
80 for( ;*argv!=NULL && argc>0;argv++)
81 {
82 if((file=bb_wfopen(*argv,"r")))
83 {
84pipe:
85
86 count=0;
87 do{
88 c=fgetc(file);
89 if(ISSTR(c))
90 {
91 if(i==0)
92 t=count;
93 if(i<=n)
94 string[i]=c;
95 if(i==n)
96 {
97 if(opt == 1 || opt == 3 )
98 printf(fmt,*argv);
99 if(opt >= 2 )
100 printf("%7lo ", t);
101 printf("%s", string);
102 }
103 if(i>n)
104 putchar(c);
105 i++;
106 }
107 else
108 {
109 if(i>n)
110 putchar('\n');
111 i=0;
112 }
113 count++;
114 }while(c!=EOF);
115
116 bb_fclose_nonstdin(file);
117 }
118 else
119 status=EXIT_FAILURE;
120 }
121 /*free(string);*/
122 exit(status);
123}
124
125/*
126 * Copyright (c) 1980, 1987
127 * The Regents of the University of California. All rights reserved.
128 *
129 * Redistribution and use in source and binary forms, with or without
130 * modification, are permitted provided that the following conditions
131 * are met:
132 * 1. Redistributions of source code must retain the above copyright
133 * notice, this list of conditions and the following disclaimer.
134 * 2. Redistributions in binary form must reproduce the above copyright
135 * notice, this list of conditions and the following disclaimer in the
136 * documentation and/or other materials provided with the distribution.
137 *
138 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
139 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
140 *
141 * 4. Neither the name of the University nor the names of its contributors
142 * may be used to endorse or promote products derived from this software
143 * without specific prior written permission.
144 *
145 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
146 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
147 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
148 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
149 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
150 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
151 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
152 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
153 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
154 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
155 * SUCH DAMAGE.
156 */
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 @@
1/* `time' utility to display resource usage of processes.
2 Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 02111-1307, USA. */
18
19/* Originally written by David Keppel <pardo@cs.washington.edu>.
20 Heavily modified by David MacKenzie <djm@gnu.ai.mit.edu>.
21 Heavily modified for busybox by Erik Andersen <andersen@codepoet.org>
22 */
23
24#include <stdlib.h>
25#include <stdio.h>
26#include <signal.h>
27#include <errno.h>
28#include <getopt.h>
29#include <string.h>
30#include <limits.h>
31#include <unistd.h>
32#include <sys/time.h>
33#include <sys/types.h> /* For pid_t. */
34#include <sys/wait.h>
35#include <sys/param.h> /* For getpagesize, maybe. */
36
37#define TV_MSEC tv_usec / 1000
38#include <sys/resource.h>
39#include "busybox.h"
40
41/* Information on the resources used by a child process. */
42typedef struct
43{
44 int waitstatus;
45 struct rusage ru;
46 struct timeval start, elapsed; /* Wallclock time of process. */
47} resource_t;
48
49/* msec = milliseconds = 1/1,000 (1*10e-3) second.
50 usec = microseconds = 1/1,000,000 (1*10e-6) second. */
51
52#ifndef TICKS_PER_SEC
53#define TICKS_PER_SEC 100
54#endif
55
56/* The number of milliseconds in one `tick' used by the `rusage' structure. */
57#define MSEC_PER_TICK (1000 / TICKS_PER_SEC)
58
59/* Return the number of clock ticks that occur in M milliseconds. */
60#define MSEC_TO_TICKS(m) ((m) / MSEC_PER_TICK)
61
62#define UL unsigned long
63
64static const char *const default_format = "real\t%E\nuser\t%u\nsys\t%T";
65
66/* The output format for the -p option .*/
67static const char *const posix_format = "real %e\nuser %U\nsys %S";
68
69
70/* Format string for printing all statistics verbosely.
71 Keep this output to 24 lines so users on terminals can see it all.*/
72static const char *const long_format =
73 "\tCommand being timed: \"%C\"\n"
74 "\tUser time (seconds): %U\n"
75 "\tSystem time (seconds): %S\n"
76 "\tPercent of CPU this job got: %P\n"
77 "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
78 "\tAverage shared text size (kbytes): %X\n"
79 "\tAverage unshared data size (kbytes): %D\n"
80 "\tAverage stack size (kbytes): %p\n"
81 "\tAverage total size (kbytes): %K\n"
82 "\tMaximum resident set size (kbytes): %M\n"
83 "\tAverage resident set size (kbytes): %t\n"
84 "\tMajor (requiring I/O) page faults: %F\n"
85 "\tMinor (reclaiming a frame) page faults: %R\n"
86 "\tVoluntary context switches: %w\n"
87 "\tInvoluntary context switches: %c\n"
88 "\tSwaps: %W\n"
89 "\tFile system inputs: %I\n"
90 "\tFile system outputs: %O\n"
91 "\tSocket messages sent: %s\n"
92 "\tSocket messages received: %r\n"
93 "\tSignals delivered: %k\n"
94 "\tPage size (bytes): %Z\n"
95 "\tExit status: %x";
96
97
98 /* Wait for and fill in data on child process PID.
99 Return 0 on error, 1 if ok. */
100
101/* pid_t is short on BSDI, so don't try to promote it. */
102static int resuse_end (pid_t pid, resource_t *resp)
103{
104 int status;
105
106 pid_t caught;
107
108 /* Ignore signals, but don't ignore the children. When wait3
109 returns the child process, set the time the command finished. */
110 while ((caught = wait3 (&status, 0, &resp->ru)) != pid)
111 {
112 if (caught == -1)
113 return 0;
114 }
115
116 gettimeofday (&resp->elapsed, (struct timezone *) 0);
117 resp->elapsed.tv_sec -= resp->start.tv_sec;
118 if (resp->elapsed.tv_usec < resp->start.tv_usec)
119 {
120 /* Manually carry a one from the seconds field. */
121 resp->elapsed.tv_usec += 1000000;
122 --resp->elapsed.tv_sec;
123 }
124 resp->elapsed.tv_usec -= resp->start.tv_usec;
125
126 resp->waitstatus = status;
127
128 return 1;
129}
130
131/* Print ARGV to FP, with each entry in ARGV separated by FILLER. */
132static void fprintargv (FILE *fp, char *const *argv, const char *filler)
133{
134 char *const *av;
135
136 av = argv;
137 fputs (*av, fp);
138 while (*++av)
139 {
140 fputs (filler, fp);
141 fputs (*av, fp);
142 }
143 if (ferror (fp))
144 bb_error_msg_and_die("write error");
145}
146
147/* Return the number of kilobytes corresponding to a number of pages PAGES.
148 (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
149
150 Try to do arithmetic so that the risk of overflow errors is minimized.
151 This is funky since the pagesize could be less than 1K.
152 Note: Some machines express getrusage statistics in terms of K,
153 others in terms of pages. */
154
155static unsigned long ptok (unsigned long pages)
156{
157 static unsigned long ps = 0;
158 unsigned long tmp;
159 static long size = LONG_MAX;
160
161 /* Initialization. */
162 if (ps == 0)
163 ps = (long) getpagesize ();
164
165 /* Conversion. */
166 if (pages > (LONG_MAX / ps))
167 { /* Could overflow. */
168 tmp = pages / 1024; /* Smaller first, */
169 size = tmp * ps; /* then larger. */
170 }
171 else
172 { /* Could underflow. */
173 tmp = pages * ps; /* Larger first, */
174 size = tmp / 1024; /* then smaller. */
175 }
176 return size;
177}
178
179/* summarize: Report on the system use of a command.
180
181 Copy the FMT argument to FP except that `%' sequences
182 have special meaning, and `\n' and `\t' are translated into
183 newline and tab, respectively, and `\\' is translated into `\'.
184
185 The character following a `%' can be:
186 (* means the tcsh time builtin also recognizes it)
187 % == a literal `%'
188 C == command name and arguments
189* D == average unshared data size in K (ru_idrss+ru_isrss)
190* E == elapsed real (wall clock) time in [hour:]min:sec
191* F == major page faults (required physical I/O) (ru_majflt)
192* I == file system inputs (ru_inblock)
193* K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss)
194* M == maximum resident set size in K (ru_maxrss)
195* O == file system outputs (ru_oublock)
196* P == percent of CPU this job got (total cpu time / elapsed time)
197* R == minor page faults (reclaims; no physical I/O involved) (ru_minflt)
198* S == system (kernel) time (seconds) (ru_stime)
199* T == system time in [hour:]min:sec
200* U == user time (seconds) (ru_utime)
201* u == user time in [hour:]min:sec
202* W == times swapped out (ru_nswap)
203* X == average amount of shared text in K (ru_ixrss)
204 Z == page size
205* c == involuntary context switches (ru_nivcsw)
206 e == elapsed real time in seconds
207* k == signals delivered (ru_nsignals)
208 p == average unshared stack size in K (ru_isrss)
209* r == socket messages received (ru_msgrcv)
210* s == socket messages sent (ru_msgsnd)
211 t == average resident set size in K (ru_idrss)
212* w == voluntary context switches (ru_nvcsw)
213 x == exit status of command
214
215 Various memory usages are found by converting from page-seconds
216 to kbytes by multiplying by the page size, dividing by 1024,
217 and dividing by elapsed real time.
218
219 FP is the stream to print to.
220 FMT is the format string, interpreted as described above.
221 COMMAND is the command and args that are being summarized.
222 RESP is resource information on the command. */
223
224static void summarize (FILE *fp, const char *fmt, char **command, resource_t *resp)
225{
226 unsigned long r; /* Elapsed real milliseconds. */
227 unsigned long v; /* Elapsed virtual (CPU) milliseconds. */
228
229 if (WIFSTOPPED (resp->waitstatus))
230 fprintf (fp, "Command stopped by signal %d\n", WSTOPSIG (resp->waitstatus));
231 else if (WIFSIGNALED (resp->waitstatus))
232 fprintf (fp, "Command terminated by signal %d\n", WTERMSIG (resp->waitstatus));
233 else if (WIFEXITED (resp->waitstatus) && WEXITSTATUS (resp->waitstatus))
234 fprintf (fp, "Command exited with non-zero status %d\n", WEXITSTATUS (resp->waitstatus));
235
236 /* Convert all times to milliseconds. Occasionally, one of these values
237 comes out as zero. Dividing by zero causes problems, so we first
238 check the time value. If it is zero, then we take `evasive action'
239 instead of calculating a value. */
240
241 r = resp->elapsed.tv_sec * 1000 + resp->elapsed.tv_usec / 1000;
242
243 v = resp->ru.ru_utime.tv_sec * 1000 + resp->ru.ru_utime.TV_MSEC +
244 resp->ru.ru_stime.tv_sec * 1000 + resp->ru.ru_stime.TV_MSEC;
245
246 while (*fmt)
247 {
248 switch (*fmt)
249 {
250 case '%':
251 switch (*++fmt)
252 {
253 case '%': /* Literal '%'. */
254 putc ('%', fp);
255 break;
256 case 'C': /* The command that got timed. */
257 fprintargv (fp, command, " ");
258 break;
259 case 'D': /* Average unshared data size. */
260 fprintf (fp, "%lu",
261 MSEC_TO_TICKS (v) == 0 ? 0 :
262 ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v) +
263 ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v));
264 break;
265 case 'E': /* Elapsed real (wall clock) time. */
266 if (resp->elapsed.tv_sec >= 3600) /* One hour -> h:m:s. */
267 fprintf (fp, "%ldh %ldm %02lds",
268 resp->elapsed.tv_sec / 3600,
269 (resp->elapsed.tv_sec % 3600) / 60,
270 resp->elapsed.tv_sec % 60);
271 else
272 fprintf (fp, "%ldm %ld.%02lds", /* -> m:s. */
273 resp->elapsed.tv_sec / 60,
274 resp->elapsed.tv_sec % 60,
275 resp->elapsed.tv_usec / 10000);
276 break;
277 case 'F': /* Major page faults. */
278 fprintf (fp, "%ld", resp->ru.ru_majflt);
279 break;
280 case 'I': /* Inputs. */
281 fprintf (fp, "%ld", resp->ru.ru_inblock);
282 break;
283 case 'K': /* Average mem usage == data+stack+text. */
284 fprintf (fp, "%lu",
285 MSEC_TO_TICKS (v) == 0 ? 0 :
286 ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v) +
287 ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v) +
288 ptok ((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS (v));
289 break;
290 case 'M': /* Maximum resident set size. */
291 fprintf (fp, "%lu", ptok ((UL) resp->ru.ru_maxrss));
292 break;
293 case 'O': /* Outputs. */
294 fprintf (fp, "%ld", resp->ru.ru_oublock);
295 break;
296 case 'P': /* Percent of CPU this job got. */
297 /* % cpu is (total cpu time)/(elapsed time). */
298 if (r > 0)
299 fprintf (fp, "%lu%%", (v * 100 / r));
300 else
301 fprintf (fp, "?%%");
302 break;
303 case 'R': /* Minor page faults (reclaims). */
304 fprintf (fp, "%ld", resp->ru.ru_minflt);
305 break;
306 case 'S': /* System time. */
307 fprintf (fp, "%ld.%02ld",
308 resp->ru.ru_stime.tv_sec,
309 resp->ru.ru_stime.TV_MSEC / 10);
310 break;
311 case 'T': /* System time. */
312 if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */
313 fprintf (fp, "%ldh %ldm %02lds",
314 resp->ru.ru_stime.tv_sec / 3600,
315 (resp->ru.ru_stime.tv_sec % 3600) / 60,
316 resp->ru.ru_stime.tv_sec % 60);
317 else
318 fprintf (fp, "%ldm %ld.%02lds", /* -> m:s. */
319 resp->ru.ru_stime.tv_sec / 60,
320 resp->ru.ru_stime.tv_sec % 60,
321 resp->ru.ru_stime.tv_usec / 10000);
322 break;
323 case 'U': /* User time. */
324 fprintf (fp, "%ld.%02ld",
325 resp->ru.ru_utime.tv_sec,
326 resp->ru.ru_utime.TV_MSEC / 10);
327 break;
328 case 'u': /* User time. */
329 if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */
330 fprintf (fp, "%ldh %ldm %02lds",
331 resp->ru.ru_utime.tv_sec / 3600,
332 (resp->ru.ru_utime.tv_sec % 3600) / 60,
333 resp->ru.ru_utime.tv_sec % 60);
334 else
335 fprintf (fp, "%ldm %ld.%02lds", /* -> m:s. */
336 resp->ru.ru_utime.tv_sec / 60,
337 resp->ru.ru_utime.tv_sec % 60,
338 resp->ru.ru_utime.tv_usec / 10000);
339 break;
340 case 'W': /* Times swapped out. */
341 fprintf (fp, "%ld", resp->ru.ru_nswap);
342 break;
343 case 'X': /* Average shared text size. */
344 fprintf (fp, "%lu",
345 MSEC_TO_TICKS (v) == 0 ? 0 :
346 ptok ((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS (v));
347 break;
348 case 'Z': /* Page size. */
349 fprintf (fp, "%d", getpagesize ());
350 break;
351 case 'c': /* Involuntary context switches. */
352 fprintf (fp, "%ld", resp->ru.ru_nivcsw);
353 break;
354 case 'e': /* Elapsed real time in seconds. */
355 fprintf (fp, "%ld.%02ld",
356 resp->elapsed.tv_sec,
357 resp->elapsed.tv_usec / 10000);
358 break;
359 case 'k': /* Signals delivered. */
360 fprintf (fp, "%ld", resp->ru.ru_nsignals);
361 break;
362 case 'p': /* Average stack segment. */
363 fprintf (fp, "%lu",
364 MSEC_TO_TICKS (v) == 0 ? 0 :
365 ptok ((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS (v));
366 break;
367 case 'r': /* Incoming socket messages received. */
368 fprintf (fp, "%ld", resp->ru.ru_msgrcv);
369 break;
370 case 's': /* Outgoing socket messages sent. */
371 fprintf (fp, "%ld", resp->ru.ru_msgsnd);
372 break;
373 case 't': /* Average resident set size. */
374 fprintf (fp, "%lu",
375 MSEC_TO_TICKS (v) == 0 ? 0 :
376 ptok ((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS (v));
377 break;
378 case 'w': /* Voluntary context switches. */
379 fprintf (fp, "%ld", resp->ru.ru_nvcsw);
380 break;
381 case 'x': /* Exit status. */
382 fprintf (fp, "%d", WEXITSTATUS (resp->waitstatus));
383 break;
384 case '\0':
385 putc ('?', fp);
386 return;
387 default:
388 putc ('?', fp);
389 putc (*fmt, fp);
390 }
391 ++fmt;
392 break;
393
394 case '\\': /* Format escape. */
395 switch (*++fmt)
396 {
397 case 't':
398 putc ('\t', fp);
399 break;
400 case 'n':
401 putc ('\n', fp);
402 break;
403 case '\\':
404 putc ('\\', fp);
405 break;
406 default:
407 putc ('?', fp);
408 putc ('\\', fp);
409 putc (*fmt, fp);
410 }
411 ++fmt;
412 break;
413
414 default:
415 putc (*fmt++, fp);
416 }
417
418 if (ferror (fp))
419 bb_error_msg_and_die("write error");
420 }
421 putc ('\n', fp);
422
423 if (ferror (fp))
424 bb_error_msg_and_die("write error");
425}
426
427/* Run command CMD and return statistics on it.
428 Put the statistics in *RESP. */
429static void run_command (char *const *cmd, resource_t *resp)
430{
431 pid_t pid; /* Pid of child. */
432 __sighandler_t interrupt_signal, quit_signal;
433
434 gettimeofday (&resp->start, (struct timezone *) 0);
435 pid = fork (); /* Run CMD as child process. */
436 if (pid < 0)
437 bb_error_msg_and_die("cannot fork");
438 else if (pid == 0)
439 { /* If child. */
440 /* Don't cast execvp arguments; that causes errors on some systems,
441 versus merely warnings if the cast is left off. */
442 execvp (cmd[0], cmd);
443 bb_error_msg("cannot run %s", cmd[0]);
444 _exit (errno == ENOENT ? 127 : 126);
445 }
446
447 /* Have signals kill the child but not self (if possible). */
448 interrupt_signal = signal (SIGINT, SIG_IGN);
449 quit_signal = signal (SIGQUIT, SIG_IGN);
450
451 if (resuse_end (pid, resp) == 0)
452 bb_error_msg("error waiting for child process");
453
454 /* Re-enable signals. */
455 signal (SIGINT, interrupt_signal);
456 signal (SIGQUIT, quit_signal);
457}
458
459extern int time_main (int argc, char **argv)
460{
461 int gotone;
462 resource_t res;
463 const char *output_format = default_format;
464
465 argc--;
466 argv++;
467 /* Parse any options -- don't use getopt() here so we don't
468 * consume the args of our client application... */
469 while (argc > 0 && **argv == '-') {
470 gotone = 0;
471 while (gotone==0 && *++(*argv)) {
472 switch (**argv) {
473 case 'v':
474 output_format = long_format;
475 break;
476 case 'p':
477 output_format = posix_format;
478 break;
479 default:
480 bb_show_usage();
481 }
482 argc--;
483 argv++;
484 gotone = 1;
485 }
486 }
487
488 if (argv == NULL || *argv == NULL)
489 bb_show_usage();
490
491 run_command (argv, &res);
492 summarize (stderr, output_format, argv, &res);
493 fflush (stderr);
494
495 if (WIFSTOPPED (res.waitstatus))
496 exit (WSTOPSIG (res.waitstatus));
497 else if (WIFSIGNALED (res.waitstatus))
498 exit (WTERMSIG (res.waitstatus));
499 else if (WIFEXITED (res.waitstatus))
500 exit (WEXITSTATUS (res.waitstatus));
501 return 0;
502}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini watchdog implementation for busybox
4 *
5 * Copyright (C) 2003 Paul Mundt <lethal@linux-sh.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <stdio.h>
24#include <fcntl.h>
25#include <unistd.h>
26#include <stdlib.h>
27#include <signal.h>
28#include "busybox.h"
29
30/* Userspace timer duration, in seconds */
31static unsigned int timer_duration = 30;
32
33/* Watchdog file descriptor */
34static int fd;
35
36static void watchdog_shutdown(int unused)
37{
38 write(fd, "V", 1); /* Magic */
39 close(fd);
40 exit(0);
41}
42
43extern int watchdog_main(int argc, char **argv)
44{
45 int opt;
46
47 while ((opt = getopt(argc, argv, "t:")) > 0) {
48 switch (opt) {
49 case 't':
50 timer_duration = bb_xgetlarg(optarg, 10, 0, INT_MAX);
51 break;
52 default:
53 bb_show_usage();
54 }
55 }
56
57 /* We're only interested in the watchdog device .. */
58 if (optind < argc - 1 || argc == 1)
59 bb_show_usage();
60
61 if (daemon(0, 1) < 0)
62 bb_perror_msg_and_die("Failed forking watchdog daemon");
63
64 signal(SIGHUP, watchdog_shutdown);
65 signal(SIGINT, watchdog_shutdown);
66
67 fd = bb_xopen(argv[argc - 1], O_WRONLY);
68
69 while (1) {
70 /*
71 * Make sure we clear the counter before sleeping, as the counter value
72 * is undefined at this point -- PFM
73 */
74 write(fd, "\0", 1);
75 sleep(timer_duration);
76 }
77
78 watchdog_shutdown(0);
79
80 return EXIT_SUCCESS;
81}
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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Linux Module Utilities"
7
8config CONFIG_INSMOD
9 bool "insmod"
10 default n
11 help
12 insmod is used to load specified modules in the running kernel.
13
14config CONFIG_FEATURE_2_4_MODULES
15 bool " Support version 2.2.x to 2.4.x Linux kernels"
16 default y
17 depends on CONFIG_INSMOD
18 help
19 Support module loading for 2.2.x and 2.4.x Linux kernels.
20
21config CONFIG_FEATURE_2_6_MODULES
22 bool " Support version 2.6.x Linux kernels"
23 default n
24 depends on CONFIG_INSMOD
25 help
26 Support module loading for newer 2.6.x Linux kernels.
27
28config CONFIG_FEATURE_INSMOD_VERSION_CHECKING
29 bool " Module version checking"
30 default n
31 depends on CONFIG_INSMOD && CONFIG_FEATURE_2_4_MODULES
32 help
33 Support checking of versions for modules. This is used to
34 ensure that the kernel and module are made for each other.
35
36config CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
37 bool " Add module symbols to kernel symbol table"
38 default n
39 depends on CONFIG_INSMOD && CONFIG_FEATURE_2_4_MODULES
40 help
41 By adding module symbols to the kernel symbol table, Oops messages
42 occuring within kernel modules can be properly debugged. By enabling
43 this feature, module symbols will always be added to the kernel symbol
44 table for properly debugging support. If you are not interested in
45 Oops messages from kernel modules, say N.
46
47config CONFIG_FEATURE_INSMOD_LOADINKMEM
48 bool " In kernel memory optimization (uClinux only)"
49 default n
50 depends on CONFIG_INSMOD && CONFIG_FEATURE_2_4_MODULES
51 help
52 This is a special uClinux only memory optimization that lets insmod
53 load the specified kernel module directly into kernel space, reducing
54 memory usage by preventing the need for two copies of the module
55 being loaded into memory.
56
57config CONFIG_FEATURE_INSMOD_LOAD_MAP
58 bool " Enable load map (-m) option"
59 default n
60 depends on CONFIG_INSMOD && CONFIG_FEATURE_2_4_MODULES
61 help
62 Enabling this, one would be able to get a load map
63 output on stdout. This makes kernel module debugging
64 easier.
65 If you don't plan to debug kernel modules, you
66 don't need this option.
67
68config CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL
69 bool " Symbols in load map"
70 default y
71 depends on CONFIG_FEATURE_INSMOD_LOAD_MAP
72 help
73 Without this option, -m will only output section
74 load map. With this option, -m will also output
75 symbols load map.
76
77config CONFIG_LSMOD
78 bool "lsmod"
79 default n
80 help
81 lsmod is used to display a list of loaded modules.
82
83config CONFIG_FEATURE_QUERY_MODULE_INTERFACE
84 bool
85 default y
86 depends on CONFIG_FEATURE_2_4_MODULES && !CONFIG_FEATURE_2_6_MODULES
87
88config CONFIG_MODPROBE
89 bool "modprobe"
90 default n
91 help
92 Handle the loading of modules, and their dependancies on a high
93 level.
94
95config CONFIG_RMMOD
96 bool "rmmod"
97 default n
98 help
99 rmmod is used to unload specified modules from the kernel.
100
101config CONFIG_FEATURE_CHECK_TAINTED_MODULE
102 bool "Support tainted module checking with new kernels"
103 default y
104 depends on CONFIG_INSMOD || CONFIG_LSMOD
105 help
106 Support checking for tainted modules. These are usually binary
107 only modules that will make the linux-kernel list ignore your
108 support request.
109 This option is required to support GPLONLY modules.
110
111
112endmenu
113
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/modutils
23MODUTILS_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20MODUTILS_AR:=modutils.a
21ifndef $(MODUTILS_DIR)
22MODUTILS_DIR:=$(top_builddir)/modutils/
23endif
24srcdir=$(top_srcdir)/modutils
25
26MODUTILS-y:=
27MODUTILS-$(CONFIG_INSMOD) += insmod.o
28MODUTILS-$(CONFIG_LSMOD) += lsmod.o
29MODUTILS-$(CONFIG_MODPROBE) += modprobe.o
30MODUTILS-$(CONFIG_RMMOD) += rmmod.o
31
32libraries-y+=$(MODUTILS_DIR)$(MODUTILS_AR)
33
34$(MODUTILS_DIR)$(MODUTILS_AR): $(patsubst %,$(MODUTILS_DIR)%, $(MODUTILS-y))
35 $(AR) -ro $@ $(patsubst %,$(MODUTILS_DIR)%, $(MODUTILS-y))
36
37$(MODUTILS_DIR)%.o: $(srcdir)/%.c
38 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
39
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini insmod implementation for busybox
4 *
5 * This version of insmod supports ARM, CRIS, H8/300, x86, ia64, x86_64,
6 * m68k, MIPS, PowerPC, S390, SH3/4/5, Sparc, v850e, and x86_64.
7 *
8 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
9 * and Ron Alder <alder@lineo.com>
10 *
11 * Rodney Radford <rradford@mindspring.com> 17-Aug-2004.
12 * Added x86_64 support.
13 *
14 * Miles Bader <miles@gnu.org> added NEC V850E support.
15 *
16 * Modified by Bryan Rittmeyer <bryan@ixiacom.com> to support SH4
17 * and (theoretically) SH3. I have only tested SH4 in little endian mode.
18 *
19 * Modified by Alcove, Julien Gaulmin <julien.gaulmin@alcove.fr> and
20 * Nicolas Ferre <nicolas.ferre@alcove.fr> to support ARM7TDMI. Only
21 * very minor changes required to also work with StrongArm and presumably
22 * all ARM based systems.
23 *
24 * Yoshinori Sato <ysato@users.sourceforge.jp> 19-May-2004.
25 * added Renesas H8/300 support.
26 *
27 * Paul Mundt <lethal@linux-sh.org> 08-Aug-2003.
28 * Integrated support for sh64 (SH-5), from preliminary modutils
29 * patches from Benedict Gaster <benedict.gaster@superh.com>.
30 * Currently limited to support for 32bit ABI.
31 *
32 * Magnus Damm <damm@opensource.se> 22-May-2002.
33 * The plt and got code are now using the same structs.
34 * Added generic linked list code to fully support PowerPC.
35 * Replaced the mess in arch_apply_relocation() with architecture blocks.
36 * The arch_create_got() function got cleaned up with architecture blocks.
37 * These blocks should be easy maintain and sync with obj_xxx.c in modutils.
38 *
39 * Magnus Damm <damm@opensource.se> added PowerPC support 20-Feb-2001.
40 * PowerPC specific code stolen from modutils-2.3.16,
41 * written by Paul Mackerras, Copyright 1996, 1997 Linux International.
42 * I've only tested the code on mpc8xx platforms in big-endian mode.
43 * Did some cleanup and added CONFIG_USE_xxx_ENTRIES...
44 *
45 * Quinn Jensen <jensenq@lineo.com> added MIPS support 23-Feb-2001.
46 * based on modutils-2.4.2
47 * MIPS specific support for Elf loading and relocation.
48 * Copyright 1996, 1997 Linux International.
49 * Contributed by Ralf Baechle <ralf@gnu.ai.mit.edu>
50 *
51 * Based almost entirely on the Linux modutils-2.3.11 implementation.
52 * Copyright 1996, 1997 Linux International.
53 * New implementation contributed by Richard Henderson <rth@tamu.edu>
54 * Based on original work by Bjorn Ekwall <bj0rn@blox.se>
55 * Restructured (and partly rewritten) by:
56 * Björn Ekwall <bj0rn@blox.se> February 1999
57 *
58 * This program is free software; you can redistribute it and/or modify
59 * it under the terms of the GNU General Public License as published by
60 * the Free Software Foundation; either version 2 of the License, or
61 * (at your option) any later version.
62 *
63 * This program is distributed in the hope that it will be useful,
64 * but WITHOUT ANY WARRANTY; without even the implied warranty of
65 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
66 * General Public License for more details.
67 *
68 * You should have received a copy of the GNU General Public License
69 * along with this program; if not, write to the Free Software
70 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
71 *
72 */
73
74#include <stdlib.h>
75#include <stdio.h>
76#include <stddef.h>
77#include <errno.h>
78#include <unistd.h>
79#include <dirent.h>
80#include <ctype.h>
81#include <assert.h>
82#include <string.h>
83#include <getopt.h>
84#include <fcntl.h>
85#include <sys/utsname.h>
86#include "busybox.h"
87
88#if !defined(CONFIG_FEATURE_2_4_MODULES) && \
89 !defined(CONFIG_FEATURE_2_6_MODULES)
90#define CONFIG_FEATURE_2_4_MODULES
91#endif
92
93#if !defined(CONFIG_FEATURE_2_4_MODULES)
94#define insmod_ng_main insmod_main
95#endif
96
97#if defined(CONFIG_FEATURE_2_6_MODULES)
98extern int insmod_ng_main( int argc, char **argv);
99#endif
100
101
102#if defined(CONFIG_FEATURE_2_4_MODULES)
103
104
105#ifdef CONFIG_FEATURE_INSMOD_LOADINKMEM
106#define LOADBITS 0
107#else
108#define LOADBITS 1
109#endif
110
111
112/* ARM support */
113#if defined(__arm__)
114#define MATCH_MACHINE(x) (x == EM_ARM)
115#define SHT_RELM SHT_REL
116#define Elf32_RelM Elf32_Rel
117#define ELFCLASSM ELFCLASS32
118#define CONFIG_USE_PLT_ENTRIES
119#define CONFIG_PLT_ENTRY_SIZE 8
120#define CONFIG_USE_GOT_ENTRIES
121#define CONFIG_GOT_ENTRY_SIZE 8
122#define CONFIG_USE_SINGLE
123#endif
124
125/* CRIS */
126#if defined(__cris__)
127#define MATCH_MACHINE(x) (x == EM_CRIS)
128#define SHT_RELM SHT_RELA
129#define Elf32_RelM Elf32_Rela
130#define ELFCLASSM ELFCLASS32
131#ifndef EM_CRIS
132#define EM_CRIS 76
133#define R_CRIS_NONE 0
134#define R_CRIS_32 3
135#endif
136#endif
137
138/* H8/300 */
139#if defined(__H8300H__) || defined(__H8300S__)
140#define MATCH_MACHINE(x) (x == EM_H8_300)
141#define SHT_RELM SHT_RELA
142#define Elf32_RelM Elf32_Rela
143#define ELFCLASSM ELFCLASS32
144#define CONFIG_USE_SINGLE
145#define SYMBOL_PREFIX "_"
146#endif
147
148/* x86 */
149#if defined(__i386__)
150#ifndef EM_486
151#define MATCH_MACHINE(x) (x == EM_386)
152#else
153#define MATCH_MACHINE(x) (x == EM_386 || x == EM_486)
154#endif
155#define SHT_RELM SHT_REL
156#define Elf32_RelM Elf32_Rel
157#define ELFCLASSM ELFCLASS32
158#define CONFIG_USE_GOT_ENTRIES
159#define CONFIG_GOT_ENTRY_SIZE 4
160#define CONFIG_USE_SINGLE
161#endif
162
163/* IA64, aka Itanium */
164#if defined(__ia64__)
165#define MATCH_MACHINE(x) (x == EM_IA_64)
166#define SHT_RELM SHT_RELA
167#define Elf64_RelM Elf64_Rela
168#define ELFCLASSM ELFCLASS64
169#endif
170
171/* m68k */
172#if defined(__mc68000__)
173#define MATCH_MACHINE(x) (x == EM_68K)
174#define SHT_RELM SHT_RELA
175#define Elf32_RelM Elf32_Rela
176#define ELFCLASSM ELFCLASS32
177#define CONFIG_USE_GOT_ENTRIES
178#define CONFIG_GOT_ENTRY_SIZE 4
179#define CONFIG_USE_SINGLE
180#endif
181
182/* MIPS */
183#if defined(__mips__)
184#define MATCH_MACHINE(x) (x == EM_MIPS || x == EM_MIPS_RS3_LE)
185#define SHT_RELM SHT_REL
186#define Elf32_RelM Elf32_Rel
187#define ELFCLASSM ELFCLASS32
188/* Account for ELF spec changes. */
189#ifndef EM_MIPS_RS3_LE
190#ifdef EM_MIPS_RS4_BE
191#define EM_MIPS_RS3_LE EM_MIPS_RS4_BE
192#else
193#define EM_MIPS_RS3_LE 10
194#endif
195#endif /* !EM_MIPS_RS3_LE */
196#define ARCHDATAM "__dbe_table"
197#endif
198
199/* PowerPC */
200#if defined(__powerpc__)
201#define MATCH_MACHINE(x) (x == EM_PPC)
202#define SHT_RELM SHT_RELA
203#define Elf32_RelM Elf32_Rela
204#define ELFCLASSM ELFCLASS32
205#define CONFIG_USE_PLT_ENTRIES
206#define CONFIG_PLT_ENTRY_SIZE 16
207#define CONFIG_USE_PLT_LIST
208#define CONFIG_LIST_ARCHTYPE ElfW(Addr)
209#define CONFIG_USE_LIST
210#define ARCHDATAM "__ftr_fixup"
211#endif
212
213/* S390 */
214#if defined(__s390__)
215#define MATCH_MACHINE(x) (x == EM_S390)
216#define SHT_RELM SHT_RELA
217#define Elf32_RelM Elf32_Rela
218#define ELFCLASSM ELFCLASS32
219#define CONFIG_USE_PLT_ENTRIES
220#define CONFIG_PLT_ENTRY_SIZE 8
221#define CONFIG_USE_GOT_ENTRIES
222#define CONFIG_GOT_ENTRY_SIZE 8
223#define CONFIG_USE_SINGLE
224#endif
225
226/* SuperH */
227#if defined(__sh__)
228#define MATCH_MACHINE(x) (x == EM_SH)
229#define SHT_RELM SHT_RELA
230#define Elf32_RelM Elf32_Rela
231#define ELFCLASSM ELFCLASS32
232#define CONFIG_USE_GOT_ENTRIES
233#define CONFIG_GOT_ENTRY_SIZE 4
234#define CONFIG_USE_SINGLE
235/* the SH changes have only been tested in =little endian= mode */
236/* I'm not sure about big endian, so let's warn: */
237#if defined(__sh__) && defined(__BIG_ENDIAN__)
238#error insmod.c may require changes for use on big endian SH
239#endif
240/* it may or may not work on the SH1/SH2... Error on those also */
241#if ((!(defined(__SH3__) || defined(__SH4__) || defined(__SH5__)))) && (defined(__sh__))
242#error insmod.c may require changes for SH1 or SH2 use
243#endif
244#endif
245
246/* Sparc */
247#if defined(__sparc__)
248#define MATCH_MACHINE(x) (x == EM_SPARC)
249#define SHT_RELM SHT_RELA
250#define Elf32_RelM Elf32_Rela
251#define ELFCLASSM ELFCLASS32
252#endif
253
254/* v850e */
255#if defined (__v850e__)
256#define MATCH_MACHINE(x) ((x) == EM_V850 || (x) == EM_CYGNUS_V850)
257#define SHT_RELM SHT_RELA
258#define Elf32_RelM Elf32_Rela
259#define ELFCLASSM ELFCLASS32
260#define CONFIG_USE_PLT_ENTRIES
261#define CONFIG_PLT_ENTRY_SIZE 8
262#define CONFIG_USE_SINGLE
263#ifndef EM_CYGNUS_V850 /* grumble */
264#define EM_CYGNUS_V850 0x9080
265#endif
266#define SYMBOL_PREFIX "_"
267#endif
268
269/* X86_64 */
270#if defined(__x86_64__)
271#define MATCH_MACHINE(x) (x == EM_X86_64)
272#define SHT_RELM SHT_REL
273#define Elf64_RelM Elf64_Rel
274#define ELFCLASSM ELFCLASS64
275#endif
276
277#ifndef SHT_RELM
278#error Sorry, but insmod.c does not yet support this architecture...
279#endif
280
281
282//----------------------------------------------------------------------------
283//--------modutils module.h, lines 45-242
284//----------------------------------------------------------------------------
285
286/* Definitions for the Linux module syscall interface.
287 Copyright 1996, 1997 Linux International.
288
289 Contributed by Richard Henderson <rth@tamu.edu>
290
291 This file is part of the Linux modutils.
292
293 This program is free software; you can redistribute it and/or modify it
294 under the terms of the GNU General Public License as published by the
295 Free Software Foundation; either version 2 of the License, or (at your
296 option) any later version.
297
298 This program is distributed in the hope that it will be useful, but
299 WITHOUT ANY WARRANTY; without even the implied warranty of
300 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
301 General Public License for more details.
302
303 You should have received a copy of the GNU General Public License
304 along with this program; if not, write to the Free Software Foundation,
305 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
306
307
308#ifndef MODUTILS_MODULE_H
309static const int MODUTILS_MODULE_H = 1;
310
311#ident "$Id: insmod.c,v 1.125 2004/09/02 23:03:25 andersen Exp $"
312
313/*======================================================================*/
314/* For sizeof() which are related to the module platform and not to the
315 environment isnmod is running in, use sizeof_xx instead of sizeof(xx). */
316
317#define tgt_sizeof_char sizeof(char)
318#define tgt_sizeof_short sizeof(short)
319#define tgt_sizeof_int sizeof(int)
320#define tgt_sizeof_long sizeof(long)
321#define tgt_sizeof_char_p sizeof(char *)
322#define tgt_sizeof_void_p sizeof(void *)
323#define tgt_long long
324
325#if defined(__sparc__) && !defined(__sparc_v9__) && defined(ARCH_sparc64)
326#undef tgt_sizeof_long
327#undef tgt_sizeof_char_p
328#undef tgt_sizeof_void_p
329#undef tgt_long
330static const int tgt_sizeof_long = 8;
331static const int tgt_sizeof_char_p = 8;
332static const int tgt_sizeof_void_p = 8;
333#define tgt_long long long
334#endif
335
336/*======================================================================*/
337/* The structures used in Linux 2.1. */
338
339/* Note: new_module_symbol does not use tgt_long intentionally */
340struct new_module_symbol
341{
342 unsigned long value;
343 unsigned long name;
344};
345
346struct new_module_persist;
347
348struct new_module_ref
349{
350 unsigned tgt_long dep; /* kernel addresses */
351 unsigned tgt_long ref;
352 unsigned tgt_long next_ref;
353};
354
355struct new_module
356{
357 unsigned tgt_long size_of_struct; /* == sizeof(module) */
358 unsigned tgt_long next;
359 unsigned tgt_long name;
360 unsigned tgt_long size;
361
362 tgt_long usecount;
363 unsigned tgt_long flags; /* AUTOCLEAN et al */
364
365 unsigned nsyms;
366 unsigned ndeps;
367
368 unsigned tgt_long syms;
369 unsigned tgt_long deps;
370 unsigned tgt_long refs;
371 unsigned tgt_long init;
372 unsigned tgt_long cleanup;
373 unsigned tgt_long ex_table_start;
374 unsigned tgt_long ex_table_end;
375#ifdef __alpha__
376 unsigned tgt_long gp;
377#endif
378 /* Everything after here is extension. */
379 unsigned tgt_long persist_start;
380 unsigned tgt_long persist_end;
381 unsigned tgt_long can_unload;
382 unsigned tgt_long runsize;
383 const char *kallsyms_start; /* All symbols for kernel debugging */
384 const char *kallsyms_end;
385 const char *archdata_start; /* arch specific data for module */
386 const char *archdata_end;
387 const char *kernel_data; /* Reserved for kernel internal use */
388};
389
390#ifdef ARCHDATAM
391#define ARCHDATA_SEC_NAME ARCHDATAM
392#else
393#define ARCHDATA_SEC_NAME "__archdata"
394#endif
395#define KALLSYMS_SEC_NAME "__kallsyms"
396
397
398struct new_module_info
399{
400 unsigned long addr;
401 unsigned long size;
402 unsigned long flags;
403 long usecount;
404};
405
406/* Bits of module.flags. */
407static const int NEW_MOD_RUNNING = 1;
408static const int NEW_MOD_DELETED = 2;
409static const int NEW_MOD_AUTOCLEAN = 4;
410static const int NEW_MOD_VISITED = 8;
411static const int NEW_MOD_USED_ONCE = 16;
412
413int init_module(const char *name, const struct new_module *);
414int query_module(const char *name, int which, void *buf,
415 size_t bufsize, size_t *ret);
416
417/* Values for query_module's which. */
418
419static const int QM_MODULES = 1;
420static const int QM_DEPS = 2;
421static const int QM_REFS = 3;
422static const int QM_SYMBOLS = 4;
423static const int QM_INFO = 5;
424
425/*======================================================================*/
426/* The system calls unchanged between 2.0 and 2.1. */
427
428unsigned long create_module(const char *, size_t);
429int delete_module(const char *);
430
431
432#endif /* module.h */
433
434//----------------------------------------------------------------------------
435//--------end of modutils module.h
436//----------------------------------------------------------------------------
437
438
439
440//----------------------------------------------------------------------------
441//--------modutils obj.h, lines 253-462
442//----------------------------------------------------------------------------
443
444/* Elf object file loading and relocation routines.
445 Copyright 1996, 1997 Linux International.
446
447 Contributed by Richard Henderson <rth@tamu.edu>
448
449 This file is part of the Linux modutils.
450
451 This program is free software; you can redistribute it and/or modify it
452 under the terms of the GNU General Public License as published by the
453 Free Software Foundation; either version 2 of the License, or (at your
454 option) any later version.
455
456 This program is distributed in the hope that it will be useful, but
457 WITHOUT ANY WARRANTY; without even the implied warranty of
458 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
459 General Public License for more details.
460
461 You should have received a copy of the GNU General Public License
462 along with this program; if not, write to the Free Software Foundation,
463 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
464
465
466#ifndef MODUTILS_OBJ_H
467static const int MODUTILS_OBJ_H = 1;
468
469#ident "$Id: insmod.c,v 1.125 2004/09/02 23:03:25 andersen Exp $"
470
471/* The relocatable object is manipulated using elfin types. */
472
473#include <stdio.h>
474#include <elf.h>
475#include <endian.h>
476
477#if __BYTE_ORDER == __LITTLE_ENDIAN
478#define ELFDATAM ELFDATA2LSB
479#elif __BYTE_ORDER == __BIG_ENDIAN
480#define ELFDATAM ELFDATA2MSB
481#endif
482
483#ifndef ElfW
484# if ELFCLASSM == ELFCLASS32
485# define ElfW(x) Elf32_ ## x
486# define ELFW(x) ELF32_ ## x
487# else
488# define ElfW(x) Elf64_ ## x
489# define ELFW(x) ELF64_ ## x
490# endif
491#endif
492
493/* For some reason this is missing from some ancient C libraries.... */
494#ifndef ELF32_ST_INFO
495# define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
496#endif
497
498#ifndef ELF64_ST_INFO
499# define ELF64_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
500#endif
501
502struct obj_string_patch;
503struct obj_symbol_patch;
504
505struct obj_section
506{
507 ElfW(Shdr) header;
508 const char *name;
509 char *contents;
510 struct obj_section *load_next;
511 int idx;
512};
513
514struct obj_symbol
515{
516 struct obj_symbol *next; /* hash table link */
517 const char *name;
518 unsigned long value;
519 unsigned long size;
520 int secidx; /* the defining section index/module */
521 int info;
522 int ksymidx; /* for export to the kernel symtab */
523 int referenced; /* actually used in the link */
524};
525
526/* Hardcode the hash table size. We shouldn't be needing so many
527 symbols that we begin to degrade performance, and we get a big win
528 by giving the compiler a constant divisor. */
529
530#define HASH_BUCKETS 521
531
532struct obj_file
533{
534 ElfW(Ehdr) header;
535 ElfW(Addr) baseaddr;
536 struct obj_section **sections;
537 struct obj_section *load_order;
538 struct obj_section **load_order_search_start;
539 struct obj_string_patch *string_patches;
540 struct obj_symbol_patch *symbol_patches;
541 int (*symbol_cmp)(const char *, const char *);
542 unsigned long (*symbol_hash)(const char *);
543 unsigned long local_symtab_size;
544 struct obj_symbol **local_symtab;
545 struct obj_symbol *symtab[HASH_BUCKETS];
546};
547
548enum obj_reloc
549{
550 obj_reloc_ok,
551 obj_reloc_overflow,
552 obj_reloc_dangerous,
553 obj_reloc_unhandled
554};
555
556struct obj_string_patch
557{
558 struct obj_string_patch *next;
559 int reloc_secidx;
560 ElfW(Addr) reloc_offset;
561 ElfW(Addr) string_offset;
562};
563
564struct obj_symbol_patch
565{
566 struct obj_symbol_patch *next;
567 int reloc_secidx;
568 ElfW(Addr) reloc_offset;
569 struct obj_symbol *sym;
570};
571
572
573/* Generic object manipulation routines. */
574
575static unsigned long obj_elf_hash(const char *);
576
577static unsigned long obj_elf_hash_n(const char *, unsigned long len);
578
579static struct obj_symbol *obj_find_symbol (struct obj_file *f,
580 const char *name);
581
582static ElfW(Addr) obj_symbol_final_value(struct obj_file *f,
583 struct obj_symbol *sym);
584
585#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
586static void obj_set_symbol_compare(struct obj_file *f,
587 int (*cmp)(const char *, const char *),
588 unsigned long (*hash)(const char *));
589#endif
590
591static struct obj_section *obj_find_section (struct obj_file *f,
592 const char *name);
593
594static void obj_insert_section_load_order (struct obj_file *f,
595 struct obj_section *sec);
596
597static struct obj_section *obj_create_alloced_section (struct obj_file *f,
598 const char *name,
599 unsigned long align,
600 unsigned long size);
601
602static struct obj_section *obj_create_alloced_section_first (struct obj_file *f,
603 const char *name,
604 unsigned long align,
605 unsigned long size);
606
607static void *obj_extend_section (struct obj_section *sec, unsigned long more);
608
609static int obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
610 const char *string);
611
612static int obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
613 struct obj_symbol *sym);
614
615static int obj_check_undefineds(struct obj_file *f);
616
617static void obj_allocate_commons(struct obj_file *f);
618
619static unsigned long obj_load_size (struct obj_file *f);
620
621static int obj_relocate (struct obj_file *f, ElfW(Addr) base);
622
623static struct obj_file *obj_load(FILE *f, int loadprogbits);
624
625static int obj_create_image (struct obj_file *f, char *image);
626
627/* Architecture specific manipulation routines. */
628
629static struct obj_file *arch_new_file (void);
630
631static struct obj_section *arch_new_section (void);
632
633static struct obj_symbol *arch_new_symbol (void);
634
635static enum obj_reloc arch_apply_relocation (struct obj_file *f,
636 struct obj_section *targsec,
637 struct obj_section *symsec,
638 struct obj_symbol *sym,
639 ElfW(RelM) *rel, ElfW(Addr) value);
640
641static void arch_create_got (struct obj_file *f);
642
643static int obj_gpl_license(struct obj_file *f, const char **license);
644
645#endif /* obj.h */
646//----------------------------------------------------------------------------
647//--------end of modutils obj.h
648//----------------------------------------------------------------------------
649
650
651/* SPFX is always a string, so it can be concatenated to string constants. */
652#ifdef SYMBOL_PREFIX
653#define SPFX SYMBOL_PREFIX
654#else
655#define SPFX ""
656#endif
657
658
659#define _PATH_MODULES "/lib/modules"
660static const int STRVERSIONLEN = 32;
661
662/*======================================================================*/
663
664static int flag_force_load = 0;
665static int flag_autoclean = 0;
666static int flag_verbose = 0;
667static int flag_quiet = 0;
668static int flag_export = 1;
669
670
671/*======================================================================*/
672
673#if defined(CONFIG_USE_LIST)
674
675struct arch_list_entry
676{
677 struct arch_list_entry *next;
678 CONFIG_LIST_ARCHTYPE addend;
679 int offset;
680 int inited : 1;
681};
682
683#endif
684
685#if defined(CONFIG_USE_SINGLE)
686
687struct arch_single_entry
688{
689 int offset;
690 int inited : 1;
691 int allocated : 1;
692};
693
694#endif
695
696#if defined(__mips__)
697struct mips_hi16
698{
699 struct mips_hi16 *next;
700 Elf32_Addr *addr;
701 Elf32_Addr value;
702};
703#endif
704
705struct arch_file {
706 struct obj_file root;
707#if defined(CONFIG_USE_PLT_ENTRIES)
708 struct obj_section *plt;
709#endif
710#if defined(CONFIG_USE_GOT_ENTRIES)
711 struct obj_section *got;
712#endif
713#if defined(__mips__)
714 struct mips_hi16 *mips_hi16_list;
715#endif
716};
717
718struct arch_symbol {
719 struct obj_symbol root;
720#if defined(CONFIG_USE_PLT_ENTRIES)
721#if defined(CONFIG_USE_PLT_LIST)
722 struct arch_list_entry *pltent;
723#else
724 struct arch_single_entry pltent;
725#endif
726#endif
727#if defined(CONFIG_USE_GOT_ENTRIES)
728 struct arch_single_entry gotent;
729#endif
730};
731
732
733struct external_module {
734 const char *name;
735 ElfW(Addr) addr;
736 int used;
737 size_t nsyms;
738 struct new_module_symbol *syms;
739};
740
741static struct new_module_symbol *ksyms;
742static size_t nksyms;
743
744static struct external_module *ext_modules;
745static int n_ext_modules;
746static int n_ext_modules_used;
747extern int delete_module(const char *);
748
749static char *m_filename;
750static char *m_fullName;
751
752
753
754/*======================================================================*/
755
756
757static int check_module_name_match(const char *filename, struct stat *statbuf,
758 void *userdata)
759{
760 char *fullname = (char *) userdata;
761
762 if (fullname[0] == '\0')
763 return (FALSE);
764 else {
765 char *tmp, *tmp1 = bb_xstrdup(filename);
766 tmp = bb_get_last_path_component(tmp1);
767 if (strcmp(tmp, fullname) == 0) {
768 free(tmp1);
769 /* Stop searching if we find a match */
770 m_filename = bb_xstrdup(filename);
771 return (FALSE);
772 }
773 free(tmp1);
774 }
775 return (TRUE);
776}
777
778
779/*======================================================================*/
780
781static struct obj_file *arch_new_file(void)
782{
783 struct arch_file *f;
784 f = xmalloc(sizeof(*f));
785
786 memset(f, 0, sizeof(*f));
787
788 return &f->root;
789}
790
791static struct obj_section *arch_new_section(void)
792{
793 return xmalloc(sizeof(struct obj_section));
794}
795
796static struct obj_symbol *arch_new_symbol(void)
797{
798 struct arch_symbol *sym;
799 sym = xmalloc(sizeof(*sym));
800
801 memset(sym, 0, sizeof(*sym));
802
803 return &sym->root;
804}
805
806static enum obj_reloc
807arch_apply_relocation(struct obj_file *f,
808 struct obj_section *targsec,
809 struct obj_section *symsec,
810 struct obj_symbol *sym,
811 ElfW(RelM) *rel, ElfW(Addr) v)
812{
813 struct arch_file *ifile = (struct arch_file *) f;
814 enum obj_reloc ret = obj_reloc_ok;
815 ElfW(Addr) *loc = (ElfW(Addr) *) (targsec->contents + rel->r_offset);
816 ElfW(Addr) dot = targsec->header.sh_addr + rel->r_offset;
817#if defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES)
818 struct arch_symbol *isym = (struct arch_symbol *) sym;
819#endif
820#if defined(CONFIG_USE_GOT_ENTRIES)
821 ElfW(Addr) got = ifile->got ? ifile->got->header.sh_addr : 0;
822#endif
823#if defined(CONFIG_USE_PLT_ENTRIES)
824 ElfW(Addr) plt = ifile->plt ? ifile->plt->header.sh_addr : 0;
825 unsigned long *ip;
826#if defined(CONFIG_USE_PLT_LIST)
827 struct arch_list_entry *pe;
828#else
829 struct arch_single_entry *pe;
830#endif
831#endif
832
833 switch (ELF32_R_TYPE(rel->r_info)) {
834
835
836#if defined(__arm__)
837 case R_ARM_NONE:
838 break;
839
840 case R_ARM_ABS32:
841 *loc += v;
842 break;
843
844 case R_ARM_GOT32:
845 goto bb_use_got;
846
847 case R_ARM_GOTPC:
848 /* relative reloc, always to _GLOBAL_OFFSET_TABLE_
849 * (which is .got) similar to branch,
850 * but is full 32 bits relative */
851
852 assert(got);
853 *loc += got - dot;
854 break;
855
856 case R_ARM_PC24:
857 case R_ARM_PLT32:
858 goto bb_use_plt;
859
860 case R_ARM_GOTOFF: /* address relative to the got */
861 assert(got);
862 *loc += v - got;
863 break;
864
865#elif defined(__s390__)
866 case R_390_32:
867 *(unsigned int *) loc += v;
868 break;
869 case R_390_16:
870 *(unsigned short *) loc += v;
871 break;
872 case R_390_8:
873 *(unsigned char *) loc += v;
874 break;
875
876 case R_390_PC32:
877 *(unsigned int *) loc += v - dot;
878 break;
879 case R_390_PC16DBL:
880 *(unsigned short *) loc += (v - dot) >> 1;
881 break;
882 case R_390_PC16:
883 *(unsigned short *) loc += v - dot;
884 break;
885
886 case R_390_PLT32:
887 case R_390_PLT16DBL:
888 /* find the plt entry and initialize it. */
889 assert(isym != NULL);
890 pe = (struct arch_single_entry *) &isym->pltent;
891 assert(pe->allocated);
892 if (pe->inited == 0) {
893 ip = (unsigned long *)(ifile->plt->contents + pe->offset);
894 ip[0] = 0x0d105810; /* basr 1,0; lg 1,10(1); br 1 */
895 ip[1] = 0x100607f1;
896 if (ELF32_R_TYPE(rel->r_info) == R_390_PLT16DBL)
897 ip[2] = v - 2;
898 else
899 ip[2] = v;
900 pe->inited = 1;
901 }
902
903 /* Insert relative distance to target. */
904 v = plt + pe->offset - dot;
905 if (ELF32_R_TYPE(rel->r_info) == R_390_PLT32)
906 *(unsigned int *) loc = (unsigned int) v;
907 else if (ELF32_R_TYPE(rel->r_info) == R_390_PLT16DBL)
908 *(unsigned short *) loc = (unsigned short) ((v + 2) >> 1);
909 break;
910
911 case R_390_GLOB_DAT:
912 case R_390_JMP_SLOT:
913 *loc = v;
914 break;
915
916 case R_390_RELATIVE:
917 *loc += f->baseaddr;
918 break;
919
920 case R_390_GOTPC:
921 assert(got != 0);
922 *(unsigned long *) loc += got - dot;
923 break;
924
925 case R_390_GOT12:
926 case R_390_GOT16:
927 case R_390_GOT32:
928 assert(isym != NULL);
929 assert(got != 0);
930 if (!isym->gotent.inited)
931 {
932 isym->gotent.inited = 1;
933 *(Elf32_Addr *)(ifile->got->contents + isym->gotent.offset) = v;
934 }
935 if (ELF32_R_TYPE(rel->r_info) == R_390_GOT12)
936 *(unsigned short *) loc |= (*(unsigned short *) loc + isym->gotent.offset) & 0xfff;
937 else if (ELF32_R_TYPE(rel->r_info) == R_390_GOT16)
938 *(unsigned short *) loc += isym->gotent.offset;
939 else if (ELF32_R_TYPE(rel->r_info) == R_390_GOT32)
940 *(unsigned int *) loc += isym->gotent.offset;
941 break;
942
943#ifndef R_390_GOTOFF32
944#define R_390_GOTOFF32 R_390_GOTOFF
945#endif
946 case R_390_GOTOFF32:
947 assert(got != 0);
948 *loc += v - got;
949 break;
950
951#elif defined(__i386__)
952
953 case R_386_NONE:
954 break;
955
956 case R_386_32:
957 *loc += v;
958 break;
959
960 case R_386_PLT32:
961 case R_386_PC32:
962 *loc += v - dot;
963 break;
964
965 case R_386_GLOB_DAT:
966 case R_386_JMP_SLOT:
967 *loc = v;
968 break;
969
970 case R_386_RELATIVE:
971 *loc += f->baseaddr;
972 break;
973
974 case R_386_GOTPC:
975 assert(got != 0);
976 *loc += got - dot;
977 break;
978
979 case R_386_GOT32:
980 goto bb_use_got;
981
982 case R_386_GOTOFF:
983 assert(got != 0);
984 *loc += v - got;
985 break;
986
987#elif defined(__mc68000__)
988
989 case R_68K_NONE:
990 break;
991
992 case R_68K_32:
993 *loc += v;
994 break;
995
996 case R_68K_8:
997 if (v > 0xff) {
998 ret = obj_reloc_overflow;
999 }
1000 *(char *)loc = v;
1001 break;
1002
1003 case R_68K_16:
1004 if (v > 0xffff) {
1005 ret = obj_reloc_overflow;
1006 }
1007 *(short *)loc = v;
1008 break;
1009
1010 case R_68K_PC8:
1011 v -= dot;
1012 if ((Elf32_Sword)v > 0x7f ||
1013 (Elf32_Sword)v < -(Elf32_Sword)0x80) {
1014 ret = obj_reloc_overflow;
1015 }
1016 *(char *)loc = v;
1017 break;
1018
1019 case R_68K_PC16:
1020 v -= dot;
1021 if ((Elf32_Sword)v > 0x7fff ||
1022 (Elf32_Sword)v < -(Elf32_Sword)0x8000) {
1023 ret = obj_reloc_overflow;
1024 }
1025 *(short *)loc = v;
1026 break;
1027
1028 case R_68K_PC32:
1029 *(int *)loc = v - dot;
1030 break;
1031
1032 case R_68K_GLOB_DAT:
1033 case R_68K_JMP_SLOT:
1034 *loc = v;
1035 break;
1036
1037 case R_68K_RELATIVE:
1038 *(int *)loc += f->baseaddr;
1039 break;
1040
1041 case R_68K_GOT32:
1042 goto bb_use_got;
1043
1044#ifdef R_68K_GOTOFF
1045 case R_68K_GOTOFF:
1046 assert(got != 0);
1047 *loc += v - got;
1048 break;
1049#endif
1050
1051#elif defined(__mips__)
1052
1053 case R_MIPS_NONE:
1054 break;
1055
1056 case R_MIPS_32:
1057 *loc += v;
1058 break;
1059
1060 case R_MIPS_26:
1061 if (v % 4)
1062 ret = obj_reloc_dangerous;
1063 if ((v & 0xf0000000) != ((dot + 4) & 0xf0000000))
1064 ret = obj_reloc_overflow;
1065 *loc =
1066 (*loc & ~0x03ffffff) | ((*loc + (v >> 2)) &
1067 0x03ffffff);
1068 break;
1069
1070 case R_MIPS_HI16:
1071 {
1072 struct mips_hi16 *n;
1073
1074 /* We cannot relocate this one now because we don't know the value
1075 of the carry we need to add. Save the information, and let LO16
1076 do the actual relocation. */
1077 n = (struct mips_hi16 *) xmalloc(sizeof *n);
1078 n->addr = loc;
1079 n->value = v;
1080 n->next = ifile->mips_hi16_list;
1081 ifile->mips_hi16_list = n;
1082 break;
1083 }
1084
1085 case R_MIPS_LO16:
1086 {
1087 unsigned long insnlo = *loc;
1088 Elf32_Addr val, vallo;
1089
1090 /* Sign extend the addend we extract from the lo insn. */
1091 vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
1092
1093 if (ifile->mips_hi16_list != NULL) {
1094 struct mips_hi16 *l;
1095
1096 l = ifile->mips_hi16_list;
1097 while (l != NULL) {
1098 struct mips_hi16 *next;
1099 unsigned long insn;
1100
1101 /* The value for the HI16 had best be the same. */
1102 assert(v == l->value);
1103
1104 /* Do the HI16 relocation. Note that we actually don't
1105 need to know anything about the LO16 itself, except where
1106 to find the low 16 bits of the addend needed by the LO16. */
1107 insn = *l->addr;
1108 val =
1109 ((insn & 0xffff) << 16) +
1110 vallo;
1111 val += v;
1112
1113 /* Account for the sign extension that will happen in the
1114 low bits. */
1115 val =
1116 ((val >> 16) +
1117 ((val & 0x8000) !=
1118 0)) & 0xffff;
1119
1120 insn = (insn & ~0xffff) | val;
1121 *l->addr = insn;
1122
1123 next = l->next;
1124 free(l);
1125 l = next;
1126 }
1127
1128 ifile->mips_hi16_list = NULL;
1129 }
1130
1131 /* Ok, we're done with the HI16 relocs. Now deal with the LO16. */
1132 val = v + vallo;
1133 insnlo = (insnlo & ~0xffff) | (val & 0xffff);
1134 *loc = insnlo;
1135 break;
1136 }
1137
1138#elif defined(__powerpc__)
1139
1140 case R_PPC_ADDR16_HA:
1141 *(unsigned short *)loc = (v + 0x8000) >> 16;
1142 break;
1143
1144 case R_PPC_ADDR16_HI:
1145 *(unsigned short *)loc = v >> 16;
1146 break;
1147
1148 case R_PPC_ADDR16_LO:
1149 *(unsigned short *)loc = v;
1150 break;
1151
1152 case R_PPC_REL24:
1153 goto bb_use_plt;
1154
1155 case R_PPC_REL32:
1156 *loc = v - dot;
1157 break;
1158
1159 case R_PPC_ADDR32:
1160 *loc = v;
1161 break;
1162
1163#elif defined(__sh__)
1164
1165 case R_SH_NONE:
1166 break;
1167
1168 case R_SH_DIR32:
1169 *loc += v;
1170 break;
1171
1172 case R_SH_REL32:
1173 *loc += v - dot;
1174 break;
1175
1176 case R_SH_PLT32:
1177 *loc = v - dot;
1178 break;
1179
1180 case R_SH_GLOB_DAT:
1181 case R_SH_JMP_SLOT:
1182 *loc = v;
1183 break;
1184
1185 case R_SH_RELATIVE:
1186 *loc = f->baseaddr + rel->r_addend;
1187 break;
1188
1189 case R_SH_GOTPC:
1190 assert(got != 0);
1191 *loc = got - dot + rel->r_addend;
1192 break;
1193
1194 case R_SH_GOT32:
1195 goto bb_use_got;
1196
1197 case R_SH_GOTOFF:
1198 assert(got != 0);
1199 *loc = v - got;
1200 break;
1201
1202#if defined(__SH5__)
1203 case R_SH_IMM_MEDLOW16:
1204 case R_SH_IMM_LOW16:
1205 {
1206 Elf32_Addr word;
1207
1208 if (ELF32_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16)
1209 v >>= 16;
1210
1211 /*
1212 * movi and shori have the format:
1213 *
1214 * | op | imm | reg | reserved |
1215 * 31..26 25..10 9.. 4 3 .. 0
1216 *
1217 * so we simply mask and or in imm.
1218 */
1219 word = *loc & ~0x3fffc00;
1220 word |= (v & 0xffff) << 10;
1221
1222 *loc = word;
1223
1224 break;
1225 }
1226
1227 case R_SH_IMM_MEDLOW16_PCREL:
1228 case R_SH_IMM_LOW16_PCREL:
1229 {
1230 Elf32_Addr word;
1231
1232 word = *loc & ~0x3fffc00;
1233
1234 v -= dot;
1235
1236 if (ELF32_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16_PCREL)
1237 v >>= 16;
1238
1239 word |= (v & 0xffff) << 10;
1240
1241 *loc = word;
1242
1243 break;
1244 }
1245#endif /* __SH5__ */
1246#endif /* __sh__ */
1247
1248 default:
1249 printf("Warning: unhandled reloc %d\n",(int)ELF32_R_TYPE(rel->r_info));
1250 ret = obj_reloc_unhandled;
1251 break;
1252
1253#if defined (__v850e__)
1254 case R_V850_NONE:
1255 break;
1256
1257 case R_V850_32:
1258 /* We write two shorts instead of a long because even
1259 32-bit insns only need half-word alignment, but
1260 32-bit data needs to be long-word aligned. */
1261 v += ((unsigned short *)loc)[0];
1262 v += ((unsigned short *)loc)[1] << 16;
1263 ((unsigned short *)loc)[0] = v & 0xffff;
1264 ((unsigned short *)loc)[1] = (v >> 16) & 0xffff;
1265 break;
1266
1267 case R_V850_22_PCREL:
1268 goto bb_use_plt;
1269#endif
1270
1271#if defined (__cris__)
1272 case R_CRIS_NONE:
1273 break;
1274
1275 case R_CRIS_32:
1276 /* CRIS keeps the relocation value in the r_addend field and
1277 * should not use whats in *loc at all
1278 */
1279 *loc = v;
1280 break;
1281#endif
1282
1283#if defined(__H8300H__) || defined(__H8300S__)
1284 case R_H8_DIR24R8:
1285 loc = (ElfW(Addr) *)((ElfW(Addr))loc - 1);
1286 *loc = (*loc & 0xff000000) | ((*loc & 0xffffff) + v);
1287 break;
1288 case R_H8_DIR24A8:
1289 *loc += v;
1290 break;
1291 case R_H8_DIR32:
1292 case R_H8_DIR32A16:
1293 *loc += v;
1294 break;
1295 case R_H8_PCREL16:
1296 v -= dot + 2;
1297 if ((Elf32_Sword)v > 0x7fff ||
1298 (Elf32_Sword)v < -(Elf32_Sword)0x8000)
1299 ret = obj_reloc_overflow;
1300 else
1301 *(unsigned short *)loc = v;
1302 break;
1303 case R_H8_PCREL8:
1304 v -= dot + 1;
1305 if ((Elf32_Sword)v > 0x7f ||
1306 (Elf32_Sword)v < -(Elf32_Sword)0x80)
1307 ret = obj_reloc_overflow;
1308 else
1309 *(unsigned char *)loc = v;
1310 break;
1311#endif
1312
1313#if defined(CONFIG_USE_PLT_ENTRIES)
1314
1315bb_use_plt:
1316
1317 /* find the plt entry and initialize it if necessary */
1318 assert(isym != NULL);
1319
1320#if defined(CONFIG_USE_PLT_LIST)
1321 for (pe = isym->pltent; pe != NULL && pe->addend != rel->r_addend;)
1322 pe = pe->next;
1323 assert(pe != NULL);
1324#else
1325 pe = &isym->pltent;
1326#endif
1327
1328 if (! pe->inited) {
1329 ip = (unsigned long *) (ifile->plt->contents + pe->offset);
1330
1331 /* generate some machine code */
1332
1333#if defined(__arm__)
1334 ip[0] = 0xe51ff004; /* ldr pc,[pc,#-4] */
1335 ip[1] = v; /* sym@ */
1336#endif
1337#if defined(__powerpc__)
1338 ip[0] = 0x3d600000 + ((v + 0x8000) >> 16); /* lis r11,sym@ha */
1339 ip[1] = 0x396b0000 + (v & 0xffff); /* addi r11,r11,sym@l */
1340 ip[2] = 0x7d6903a6; /* mtctr r11 */
1341 ip[3] = 0x4e800420; /* bctr */
1342#endif
1343#if defined (__v850e__)
1344 /* We have to trash a register, so we assume that any control
1345 transfer more than 21-bits away must be a function call
1346 (so we can use a call-clobbered register). */
1347 ip[0] = 0x0621 + ((v & 0xffff) << 16); /* mov sym, r1 ... */
1348 ip[1] = ((v >> 16) & 0xffff) + 0x610000; /* ...; jmp r1 */
1349#endif
1350 pe->inited = 1;
1351 }
1352
1353 /* relative distance to target */
1354 v -= dot;
1355 /* if the target is too far away.... */
1356#if defined (__arm__) || defined (__powerpc__)
1357 if ((int)v < -0x02000000 || (int)v >= 0x02000000)
1358#elif defined (__v850e__)
1359 if ((Elf32_Sword)v > 0x1fffff || (Elf32_Sword)v < (Elf32_Sword)-0x200000)
1360#endif
1361 /* go via the plt */
1362 v = plt + pe->offset - dot;
1363
1364#if defined (__v850e__)
1365 if (v & 1)
1366#else
1367 if (v & 3)
1368#endif
1369 ret = obj_reloc_dangerous;
1370
1371 /* merge the offset into the instruction. */
1372#if defined(__arm__)
1373 /* Convert to words. */
1374 v >>= 2;
1375
1376 *loc = (*loc & ~0x00ffffff) | ((v + *loc) & 0x00ffffff);
1377#endif
1378#if defined(__powerpc__)
1379 *loc = (*loc & ~0x03fffffc) | (v & 0x03fffffc);
1380#endif
1381#if defined (__v850e__)
1382 /* We write two shorts instead of a long because even 32-bit insns
1383 only need half-word alignment, but the 32-bit data write needs
1384 to be long-word aligned. */
1385 ((unsigned short *)loc)[0] =
1386 (*(unsigned short *)loc & 0xffc0) /* opcode + reg */
1387 | ((v >> 16) & 0x3f); /* offs high part */
1388 ((unsigned short *)loc)[1] =
1389 (v & 0xffff); /* offs low part */
1390#endif
1391 break;
1392#endif /* CONFIG_USE_PLT_ENTRIES */
1393
1394#if defined(CONFIG_USE_GOT_ENTRIES)
1395bb_use_got:
1396
1397 assert(isym != NULL);
1398 /* needs an entry in the .got: set it, once */
1399 if (!isym->gotent.inited) {
1400 isym->gotent.inited = 1;
1401 *(ElfW(Addr) *) (ifile->got->contents + isym->gotent.offset) = v;
1402 }
1403 /* make the reloc with_respect_to_.got */
1404#if defined(__sh__)
1405 *loc += isym->gotent.offset + rel->r_addend;
1406#elif defined(__i386__) || defined(__arm__) || defined(__mc68000__)
1407 *loc += isym->gotent.offset;
1408#endif
1409 break;
1410
1411#endif /* CONFIG_USE_GOT_ENTRIES */
1412 }
1413
1414 return ret;
1415}
1416
1417
1418#if defined(CONFIG_USE_LIST)
1419
1420static int arch_list_add(ElfW(RelM) *rel, struct arch_list_entry **list,
1421 int offset, int size)
1422{
1423 struct arch_list_entry *pe;
1424
1425 for (pe = *list; pe != NULL; pe = pe->next) {
1426 if (pe->addend == rel->r_addend) {
1427 break;
1428 }
1429 }
1430
1431 if (pe == NULL) {
1432 pe = xmalloc(sizeof(struct arch_list_entry));
1433 pe->next = *list;
1434 pe->addend = rel->r_addend;
1435 pe->offset = offset;
1436 pe->inited = 0;
1437 *list = pe;
1438 return size;
1439 }
1440 return 0;
1441}
1442
1443#endif
1444
1445#if defined(CONFIG_USE_SINGLE)
1446
1447static int arch_single_init(ElfW(RelM) *rel, struct arch_single_entry *single,
1448 int offset, int size)
1449{
1450 if (single->allocated == 0) {
1451 single->allocated = 1;
1452 single->offset = offset;
1453 single->inited = 0;
1454 return size;
1455 }
1456 return 0;
1457}
1458
1459#endif
1460
1461#if defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES)
1462
1463static struct obj_section *arch_xsect_init(struct obj_file *f, char *name,
1464 int offset, int size)
1465{
1466 struct obj_section *myrelsec = obj_find_section(f, name);
1467
1468 if (offset == 0) {
1469 offset += size;
1470 }
1471
1472 if (myrelsec) {
1473 obj_extend_section(myrelsec, offset);
1474 } else {
1475 myrelsec = obj_create_alloced_section(f, name,
1476 size, offset);
1477 assert(myrelsec);
1478 }
1479
1480 return myrelsec;
1481}
1482
1483#endif
1484
1485static void arch_create_got(struct obj_file *f)
1486{
1487#if defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES)
1488 struct arch_file *ifile = (struct arch_file *) f;
1489 int i;
1490#if defined(CONFIG_USE_GOT_ENTRIES)
1491 int got_offset = 0, got_needed = 0, got_allocate;
1492#endif
1493#if defined(CONFIG_USE_PLT_ENTRIES)
1494 int plt_offset = 0, plt_needed = 0, plt_allocate;
1495#endif
1496 struct obj_section *relsec, *symsec, *strsec;
1497 ElfW(RelM) *rel, *relend;
1498 ElfW(Sym) *symtab, *extsym;
1499 const char *strtab, *name;
1500 struct arch_symbol *intsym;
1501
1502 for (i = 0; i < f->header.e_shnum; ++i) {
1503 relsec = f->sections[i];
1504 if (relsec->header.sh_type != SHT_RELM)
1505 continue;
1506
1507 symsec = f->sections[relsec->header.sh_link];
1508 strsec = f->sections[symsec->header.sh_link];
1509
1510 rel = (ElfW(RelM) *) relsec->contents;
1511 relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM)));
1512 symtab = (ElfW(Sym) *) symsec->contents;
1513 strtab = (const char *) strsec->contents;
1514
1515 for (; rel < relend; ++rel) {
1516 extsym = &symtab[ELF32_R_SYM(rel->r_info)];
1517
1518#if defined(CONFIG_USE_GOT_ENTRIES)
1519 got_allocate = 0;
1520#endif
1521#if defined(CONFIG_USE_PLT_ENTRIES)
1522 plt_allocate = 0;
1523#endif
1524
1525 switch (ELF32_R_TYPE(rel->r_info)) {
1526#if defined(__arm__)
1527 case R_ARM_PC24:
1528 case R_ARM_PLT32:
1529 plt_allocate = 1;
1530 break;
1531
1532 case R_ARM_GOTOFF:
1533 case R_ARM_GOTPC:
1534 got_needed = 1;
1535 continue;
1536
1537 case R_ARM_GOT32:
1538 got_allocate = 1;
1539 break;
1540
1541#elif defined(__i386__)
1542 case R_386_GOTPC:
1543 case R_386_GOTOFF:
1544 got_needed = 1;
1545 continue;
1546
1547 case R_386_GOT32:
1548 got_allocate = 1;
1549 break;
1550
1551#elif defined(__powerpc__)
1552 case R_PPC_REL24:
1553 plt_allocate = 1;
1554 break;
1555
1556#elif defined(__mc68000__)
1557 case R_68K_GOT32:
1558 got_allocate = 1;
1559 break;
1560
1561#ifdef R_68K_GOTOFF
1562 case R_68K_GOTOFF:
1563 got_needed = 1;
1564 continue;
1565#endif
1566
1567#elif defined(__sh__)
1568 case R_SH_GOT32:
1569 got_allocate = 1;
1570 break;
1571
1572 case R_SH_GOTPC:
1573 case R_SH_GOTOFF:
1574 got_needed = 1;
1575 continue;
1576
1577#elif defined (__v850e__)
1578 case R_V850_22_PCREL:
1579 plt_needed = 1;
1580 break;
1581
1582#endif
1583 default:
1584 continue;
1585 }
1586
1587 if (extsym->st_name != 0) {
1588 name = strtab + extsym->st_name;
1589 } else {
1590 name = f->sections[extsym->st_shndx]->name;
1591 }
1592 intsym = (struct arch_symbol *) obj_find_symbol(f, name);
1593#if defined(CONFIG_USE_GOT_ENTRIES)
1594 if (got_allocate) {
1595 got_offset += arch_single_init(
1596 rel, &intsym->gotent,
1597 got_offset, CONFIG_GOT_ENTRY_SIZE);
1598
1599 got_needed = 1;
1600 }
1601#endif
1602#if defined(CONFIG_USE_PLT_ENTRIES)
1603 if (plt_allocate) {
1604#if defined(CONFIG_USE_PLT_LIST)
1605 plt_offset += arch_list_add(
1606 rel, &intsym->pltent,
1607 plt_offset, CONFIG_PLT_ENTRY_SIZE);
1608#else
1609 plt_offset += arch_single_init(
1610 rel, &intsym->pltent,
1611 plt_offset, CONFIG_PLT_ENTRY_SIZE);
1612#endif
1613 plt_needed = 1;
1614 }
1615#endif
1616 }
1617 }
1618
1619#if defined(CONFIG_USE_GOT_ENTRIES)
1620 if (got_needed) {
1621 ifile->got = arch_xsect_init(f, ".got", got_offset,
1622 CONFIG_GOT_ENTRY_SIZE);
1623 }
1624#endif
1625
1626#if defined(CONFIG_USE_PLT_ENTRIES)
1627 if (plt_needed) {
1628 ifile->plt = arch_xsect_init(f, ".plt", plt_offset,
1629 CONFIG_PLT_ENTRY_SIZE);
1630 }
1631#endif
1632
1633#endif /* defined(CONFIG_USE_GOT_ENTRIES) || defined(CONFIG_USE_PLT_ENTRIES) */
1634}
1635
1636/*======================================================================*/
1637
1638/* Standard ELF hash function. */
1639static inline unsigned long obj_elf_hash_n(const char *name, unsigned long n)
1640{
1641 unsigned long h = 0;
1642 unsigned long g;
1643 unsigned char ch;
1644
1645 while (n > 0) {
1646 ch = *name++;
1647 h = (h << 4) + ch;
1648 if ((g = (h & 0xf0000000)) != 0) {
1649 h ^= g >> 24;
1650 h &= ~g;
1651 }
1652 n--;
1653 }
1654 return h;
1655}
1656
1657static unsigned long obj_elf_hash(const char *name)
1658{
1659 return obj_elf_hash_n(name, strlen(name));
1660}
1661
1662#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
1663/* String comparison for non-co-versioned kernel and module. */
1664
1665static int ncv_strcmp(const char *a, const char *b)
1666{
1667 size_t alen = strlen(a), blen = strlen(b);
1668
1669 if (blen == alen + 10 && b[alen] == '_' && b[alen + 1] == 'R')
1670 return strncmp(a, b, alen);
1671 else if (alen == blen + 10 && a[blen] == '_' && a[blen + 1] == 'R')
1672 return strncmp(a, b, blen);
1673 else
1674 return strcmp(a, b);
1675}
1676
1677/* String hashing for non-co-versioned kernel and module. Here
1678 we are simply forced to drop the crc from the hash. */
1679
1680static unsigned long ncv_symbol_hash(const char *str)
1681{
1682 size_t len = strlen(str);
1683 if (len > 10 && str[len - 10] == '_' && str[len - 9] == 'R')
1684 len -= 10;
1685 return obj_elf_hash_n(str, len);
1686}
1687
1688static void
1689obj_set_symbol_compare(struct obj_file *f,
1690 int (*cmp) (const char *, const char *),
1691 unsigned long (*hash) (const char *))
1692{
1693 if (cmp)
1694 f->symbol_cmp = cmp;
1695 if (hash) {
1696 struct obj_symbol *tmptab[HASH_BUCKETS], *sym, *next;
1697 int i;
1698
1699 f->symbol_hash = hash;
1700
1701 memcpy(tmptab, f->symtab, sizeof(tmptab));
1702 memset(f->symtab, 0, sizeof(f->symtab));
1703
1704 for (i = 0; i < HASH_BUCKETS; ++i)
1705 for (sym = tmptab[i]; sym; sym = next) {
1706 unsigned long h = hash(sym->name) % HASH_BUCKETS;
1707 next = sym->next;
1708 sym->next = f->symtab[h];
1709 f->symtab[h] = sym;
1710 }
1711 }
1712}
1713
1714#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */
1715
1716static struct obj_symbol *
1717obj_add_symbol(struct obj_file *f, const char *name,
1718 unsigned long symidx, int info,
1719 int secidx, ElfW(Addr) value,
1720 unsigned long size)
1721{
1722 struct obj_symbol *sym;
1723 unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS;
1724 int n_type = ELFW(ST_TYPE) (info);
1725 int n_binding = ELFW(ST_BIND) (info);
1726
1727 for (sym = f->symtab[hash]; sym; sym = sym->next)
1728 if (f->symbol_cmp(sym->name, name) == 0) {
1729 int o_secidx = sym->secidx;
1730 int o_info = sym->info;
1731 int o_type = ELFW(ST_TYPE) (o_info);
1732 int o_binding = ELFW(ST_BIND) (o_info);
1733
1734 /* A redefinition! Is it legal? */
1735
1736 if (secidx == SHN_UNDEF)
1737 return sym;
1738 else if (o_secidx == SHN_UNDEF)
1739 goto found;
1740 else if (n_binding == STB_GLOBAL && o_binding == STB_LOCAL) {
1741 /* Cope with local and global symbols of the same name
1742 in the same object file, as might have been created
1743 by ld -r. The only reason locals are now seen at this
1744 level at all is so that we can do semi-sensible things
1745 with parameters. */
1746
1747 struct obj_symbol *nsym, **p;
1748
1749 nsym = arch_new_symbol();
1750 nsym->next = sym->next;
1751 nsym->ksymidx = -1;
1752
1753 /* Excise the old (local) symbol from the hash chain. */
1754 for (p = &f->symtab[hash]; *p != sym; p = &(*p)->next)
1755 continue;
1756 *p = sym = nsym;
1757 goto found;
1758 } else if (n_binding == STB_LOCAL) {
1759 /* Another symbol of the same name has already been defined.
1760 Just add this to the local table. */
1761 sym = arch_new_symbol();
1762 sym->next = NULL;
1763 sym->ksymidx = -1;
1764 f->local_symtab[symidx] = sym;
1765 goto found;
1766 } else if (n_binding == STB_WEAK)
1767 return sym;
1768 else if (o_binding == STB_WEAK)
1769 goto found;
1770 /* Don't unify COMMON symbols with object types the programmer
1771 doesn't expect. */
1772 else if (secidx == SHN_COMMON
1773 && (o_type == STT_NOTYPE || o_type == STT_OBJECT))
1774 return sym;
1775 else if (o_secidx == SHN_COMMON
1776 && (n_type == STT_NOTYPE || n_type == STT_OBJECT))
1777 goto found;
1778 else {
1779 /* Don't report an error if the symbol is coming from
1780 the kernel or some external module. */
1781 if (secidx <= SHN_HIRESERVE)
1782 bb_error_msg("%s multiply defined", name);
1783 return sym;
1784 }
1785 }
1786
1787 /* Completely new symbol. */
1788 sym = arch_new_symbol();
1789 sym->next = f->symtab[hash];
1790 f->symtab[hash] = sym;
1791 sym->ksymidx = -1;
1792
1793 if (ELFW(ST_BIND)(info) == STB_LOCAL && symidx != -1) {
1794 if (symidx >= f->local_symtab_size)
1795 bb_error_msg("local symbol %s with index %ld exceeds local_symtab_size %ld",
1796 name, (long) symidx, (long) f->local_symtab_size);
1797 else
1798 f->local_symtab[symidx] = sym;
1799 }
1800
1801found:
1802 sym->name = name;
1803 sym->value = value;
1804 sym->size = size;
1805 sym->secidx = secidx;
1806 sym->info = info;
1807
1808 return sym;
1809}
1810
1811static struct obj_symbol *
1812obj_find_symbol(struct obj_file *f, const char *name)
1813{
1814 struct obj_symbol *sym;
1815 unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS;
1816
1817 for (sym = f->symtab[hash]; sym; sym = sym->next)
1818 if (f->symbol_cmp(sym->name, name) == 0)
1819 return sym;
1820
1821 return NULL;
1822}
1823
1824static ElfW(Addr)
1825 obj_symbol_final_value(struct obj_file * f, struct obj_symbol * sym)
1826{
1827 if (sym) {
1828 if (sym->secidx >= SHN_LORESERVE)
1829 return sym->value;
1830
1831 return sym->value + f->sections[sym->secidx]->header.sh_addr;
1832 } else {
1833 /* As a special case, a NULL sym has value zero. */
1834 return 0;
1835 }
1836}
1837
1838static struct obj_section *obj_find_section(struct obj_file *f, const char *name)
1839{
1840 int i, n = f->header.e_shnum;
1841
1842 for (i = 0; i < n; ++i)
1843 if (strcmp(f->sections[i]->name, name) == 0)
1844 return f->sections[i];
1845
1846 return NULL;
1847}
1848
1849static int obj_load_order_prio(struct obj_section *a)
1850{
1851 unsigned long af, ac;
1852
1853 af = a->header.sh_flags;
1854
1855 ac = 0;
1856 if (a->name[0] != '.' || strlen(a->name) != 10 ||
1857 strcmp(a->name + 5, ".init"))
1858 ac |= 32;
1859 if (af & SHF_ALLOC)
1860 ac |= 16;
1861 if (!(af & SHF_WRITE))
1862 ac |= 8;
1863 if (af & SHF_EXECINSTR)
1864 ac |= 4;
1865 if (a->header.sh_type != SHT_NOBITS)
1866 ac |= 2;
1867
1868 return ac;
1869}
1870
1871static void
1872obj_insert_section_load_order(struct obj_file *f, struct obj_section *sec)
1873{
1874 struct obj_section **p;
1875 int prio = obj_load_order_prio(sec);
1876 for (p = f->load_order_search_start; *p; p = &(*p)->load_next)
1877 if (obj_load_order_prio(*p) < prio)
1878 break;
1879 sec->load_next = *p;
1880 *p = sec;
1881}
1882
1883static struct obj_section *obj_create_alloced_section(struct obj_file *f,
1884 const char *name,
1885 unsigned long align,
1886 unsigned long size)
1887{
1888 int newidx = f->header.e_shnum++;
1889 struct obj_section *sec;
1890
1891 f->sections = xrealloc(f->sections, (newidx + 1) * sizeof(sec));
1892 f->sections[newidx] = sec = arch_new_section();
1893
1894 memset(sec, 0, sizeof(*sec));
1895 sec->header.sh_type = SHT_PROGBITS;
1896 sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
1897 sec->header.sh_size = size;
1898 sec->header.sh_addralign = align;
1899 sec->name = name;
1900 sec->idx = newidx;
1901 if (size)
1902 sec->contents = xmalloc(size);
1903
1904 obj_insert_section_load_order(f, sec);
1905
1906 return sec;
1907}
1908
1909static struct obj_section *obj_create_alloced_section_first(struct obj_file *f,
1910 const char *name,
1911 unsigned long align,
1912 unsigned long size)
1913{
1914 int newidx = f->header.e_shnum++;
1915 struct obj_section *sec;
1916
1917 f->sections = xrealloc(f->sections, (newidx + 1) * sizeof(sec));
1918 f->sections[newidx] = sec = arch_new_section();
1919
1920 memset(sec, 0, sizeof(*sec));
1921 sec->header.sh_type = SHT_PROGBITS;
1922 sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
1923 sec->header.sh_size = size;
1924 sec->header.sh_addralign = align;
1925 sec->name = name;
1926 sec->idx = newidx;
1927 if (size)
1928 sec->contents = xmalloc(size);
1929
1930 sec->load_next = f->load_order;
1931 f->load_order = sec;
1932 if (f->load_order_search_start == &f->load_order)
1933 f->load_order_search_start = &sec->load_next;
1934
1935 return sec;
1936}
1937
1938static void *obj_extend_section(struct obj_section *sec, unsigned long more)
1939{
1940 unsigned long oldsize = sec->header.sh_size;
1941 if (more) {
1942 sec->contents = xrealloc(sec->contents, sec->header.sh_size += more);
1943 }
1944 return sec->contents + oldsize;
1945}
1946
1947
1948/* Conditionally add the symbols from the given symbol set to the
1949 new module. */
1950
1951static int
1952add_symbols_from( struct obj_file *f,
1953 int idx, struct new_module_symbol *syms, size_t nsyms)
1954{
1955 struct new_module_symbol *s;
1956 size_t i;
1957 int used = 0;
1958#ifdef SYMBOL_PREFIX
1959 char *name_buf = 0;
1960 size_t name_alloced_size = 0;
1961#endif
1962#ifdef CONFIG_FEATURE_CHECK_TAINTED_MODULE
1963 int gpl;
1964
1965 gpl = obj_gpl_license(f, NULL) == 0;
1966#endif
1967 for (i = 0, s = syms; i < nsyms; ++i, ++s) {
1968 /* Only add symbols that are already marked external.
1969 If we override locals we may cause problems for
1970 argument initialization. We will also create a false
1971 dependency on the module. */
1972 struct obj_symbol *sym;
1973 char *name;
1974
1975 /* GPL licensed modules can use symbols exported with
1976 * EXPORT_SYMBOL_GPL, so ignore any GPLONLY_ prefix on the
1977 * exported names. Non-GPL modules never see any GPLONLY_
1978 * symbols so they cannot fudge it by adding the prefix on
1979 * their references.
1980 */
1981 if (strncmp((char *)s->name, "GPLONLY_", 8) == 0) {
1982#ifdef CONFIG_FEATURE_CHECK_TAINTED_MODULE
1983 if (gpl)
1984 s->name += 8;
1985 else
1986#endif
1987 continue;
1988 }
1989 name = (char *)s->name;
1990
1991#ifdef SYMBOL_PREFIX
1992 /* Prepend SYMBOL_PREFIX to the symbol's name (the
1993 kernel exports `C names', but module object files
1994 reference `linker names'). */
1995 size_t extra = sizeof SYMBOL_PREFIX;
1996 size_t name_size = strlen (name) + extra;
1997 if (name_size > name_alloced_size) {
1998 name_alloced_size = name_size * 2;
1999 name_buf = alloca (name_alloced_size);
2000 }
2001 strcpy (name_buf, SYMBOL_PREFIX);
2002 strcpy (name_buf + extra - 1, name);
2003 name = name_buf;
2004#endif /* SYMBOL_PREFIX */
2005
2006 sym = obj_find_symbol(f, name);
2007 if (sym && !(ELFW(ST_BIND) (sym->info) == STB_LOCAL)) {
2008#ifdef SYMBOL_PREFIX
2009 /* Put NAME_BUF into more permanent storage. */
2010 name = xmalloc (name_size);
2011 strcpy (name, name_buf);
2012#endif
2013 sym = obj_add_symbol(f, name, -1,
2014 ELFW(ST_INFO) (STB_GLOBAL,
2015 STT_NOTYPE),
2016 idx, s->value, 0);
2017 /* Did our symbol just get installed? If so, mark the
2018 module as "used". */
2019 if (sym->secidx == idx)
2020 used = 1;
2021 }
2022 }
2023
2024 return used;
2025}
2026
2027static void add_kernel_symbols(struct obj_file *f)
2028{
2029 struct external_module *m;
2030 int i, nused = 0;
2031
2032 /* Add module symbols first. */
2033
2034 for (i = 0, m = ext_modules; i < n_ext_modules; ++i, ++m)
2035 if (m->nsyms
2036 && add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms,
2037 m->nsyms)) m->used = 1, ++nused;
2038
2039 n_ext_modules_used = nused;
2040
2041 /* And finally the symbols from the kernel proper. */
2042
2043 if (nksyms)
2044 add_symbols_from(f, SHN_HIRESERVE + 1, ksyms, nksyms);
2045}
2046
2047static char *get_modinfo_value(struct obj_file *f, const char *key)
2048{
2049 struct obj_section *sec;
2050 char *p, *v, *n, *ep;
2051 size_t klen = strlen(key);
2052
2053 sec = obj_find_section(f, ".modinfo");
2054 if (sec == NULL)
2055 return NULL;
2056 p = sec->contents;
2057 ep = p + sec->header.sh_size;
2058 while (p < ep) {
2059 v = strchr(p, '=');
2060 n = strchr(p, '\0');
2061 if (v) {
2062 if (p + klen == v && strncmp(p, key, klen) == 0)
2063 return v + 1;
2064 } else {
2065 if (p + klen == n && strcmp(p, key) == 0)
2066 return n;
2067 }
2068 p = n + 1;
2069 }
2070
2071 return NULL;
2072}
2073
2074
2075/*======================================================================*/
2076/* Functions relating to module loading after 2.1.18. */
2077
2078static int
2079new_process_module_arguments(struct obj_file *f, int argc, char **argv)
2080{
2081 while (argc > 0) {
2082 char *p, *q, *key, *sym_name;
2083 struct obj_symbol *sym;
2084 char *contents, *loc;
2085 int min, max, n;
2086
2087 p = *argv;
2088 if ((q = strchr(p, '=')) == NULL) {
2089 argc--;
2090 continue;
2091 }
2092
2093 key = alloca(q - p + 6);
2094 memcpy(key, "parm_", 5);
2095 memcpy(key + 5, p, q - p);
2096 key[q - p + 5] = 0;
2097
2098 p = get_modinfo_value(f, key);
2099 key += 5;
2100 if (p == NULL) {
2101 bb_error_msg("invalid parameter %s", key);
2102 return 0;
2103 }
2104
2105#ifdef SYMBOL_PREFIX
2106 sym_name = alloca (strlen (key) + sizeof SYMBOL_PREFIX);
2107 strcpy (sym_name, SYMBOL_PREFIX);
2108 strcat (sym_name, key);
2109#else
2110 sym_name = key;
2111#endif
2112 sym = obj_find_symbol(f, sym_name);
2113
2114 /* Also check that the parameter was not resolved from the kernel. */
2115 if (sym == NULL || sym->secidx > SHN_HIRESERVE) {
2116 bb_error_msg("symbol for parameter %s not found", key);
2117 return 0;
2118 }
2119
2120 if (isdigit(*p)) {
2121 min = strtoul(p, &p, 10);
2122 if (*p == '-')
2123 max = strtoul(p + 1, &p, 10);
2124 else
2125 max = min;
2126 } else
2127 min = max = 1;
2128
2129 contents = f->sections[sym->secidx]->contents;
2130 loc = contents + sym->value;
2131 n = (*++q != '\0');
2132
2133 while (1) {
2134 if ((*p == 's') || (*p == 'c')) {
2135 char *str;
2136
2137 /* Do C quoting if we begin with a ", else slurp the lot. */
2138 if (*q == '"') {
2139 char *r;
2140
2141 str = alloca(strlen(q));
2142 for (r = str, q++; *q != '"'; ++q, ++r) {
2143 if (*q == '\0') {
2144 bb_error_msg("improperly terminated string argument for %s",
2145 key);
2146 return 0;
2147 } else if (*q == '\\')
2148 switch (*++q) {
2149 case 'a':
2150 *r = '\a';
2151 break;
2152 case 'b':
2153 *r = '\b';
2154 break;
2155 case 'e':
2156 *r = '\033';
2157 break;
2158 case 'f':
2159 *r = '\f';
2160 break;
2161 case 'n':
2162 *r = '\n';
2163 break;
2164 case 'r':
2165 *r = '\r';
2166 break;
2167 case 't':
2168 *r = '\t';
2169 break;
2170
2171 case '0':
2172 case '1':
2173 case '2':
2174 case '3':
2175 case '4':
2176 case '5':
2177 case '6':
2178 case '7':
2179 {
2180 int c = *q - '0';
2181 if (q[1] >= '0' && q[1] <= '7') {
2182 c = (c * 8) + *++q - '0';
2183 if (q[1] >= '0' && q[1] <= '7')
2184 c = (c * 8) + *++q - '0';
2185 }
2186 *r = c;
2187 }
2188 break;
2189
2190 default:
2191 *r = *q;
2192 break;
2193 } else
2194 *r = *q;
2195 }
2196 *r = '\0';
2197 ++q;
2198 } else {
2199 char *r;
2200
2201 /* In this case, the string is not quoted. We will break
2202 it using the coma (like for ints). If the user wants to
2203 include comas in a string, he just has to quote it */
2204
2205 /* Search the next coma */
2206 r = strchr(q, ',');
2207
2208 /* Found ? */
2209 if (r != (char *) NULL) {
2210 /* Recopy the current field */
2211 str = alloca(r - q + 1);
2212 memcpy(str, q, r - q);
2213
2214 /* I don't know if it is useful, as the previous case
2215 doesn't nul terminate the string ??? */
2216 str[r - q] = '\0';
2217
2218 /* Keep next fields */
2219 q = r;
2220 } else {
2221 /* last string */
2222 str = q;
2223 q = "";
2224 }
2225 }
2226
2227 if (*p == 's') {
2228 /* Normal string */
2229 obj_string_patch(f, sym->secidx, loc - contents, str);
2230 loc += tgt_sizeof_char_p;
2231 } else {
2232 /* Array of chars (in fact, matrix !) */
2233 unsigned long charssize; /* size of each member */
2234
2235 /* Get the size of each member */
2236 /* Probably we should do that outside the loop ? */
2237 if (!isdigit(*(p + 1))) {
2238 bb_error_msg("parameter type 'c' for %s must be followed by"
2239 " the maximum size", key);
2240 return 0;
2241 }
2242 charssize = strtoul(p + 1, (char **) NULL, 10);
2243
2244 /* Check length */
2245 if (strlen(str) >= charssize) {
2246 bb_error_msg("string too long for %s (max %ld)", key,
2247 charssize - 1);
2248 return 0;
2249 }
2250
2251 /* Copy to location */
2252 strcpy((char *) loc, str);
2253 loc += charssize;
2254 }
2255 } else {
2256 long v = strtoul(q, &q, 0);
2257 switch (*p) {
2258 case 'b':
2259 *loc++ = v;
2260 break;
2261 case 'h':
2262 *(short *) loc = v;
2263 loc += tgt_sizeof_short;
2264 break;
2265 case 'i':
2266 *(int *) loc = v;
2267 loc += tgt_sizeof_int;
2268 break;
2269 case 'l':
2270 *(long *) loc = v;
2271 loc += tgt_sizeof_long;
2272 break;
2273
2274 default:
2275 bb_error_msg("unknown parameter type '%c' for %s", *p, key);
2276 return 0;
2277 }
2278 }
2279
2280retry_end_of_value:
2281 switch (*q) {
2282 case '\0':
2283 goto end_of_arg;
2284
2285 case ' ':
2286 case '\t':
2287 case '\n':
2288 case '\r':
2289 ++q;
2290 goto retry_end_of_value;
2291
2292 case ',':
2293 if (++n > max) {
2294 bb_error_msg("too many values for %s (max %d)", key, max);
2295 return 0;
2296 }
2297 ++q;
2298 break;
2299
2300 default:
2301 bb_error_msg("invalid argument syntax for %s", key);
2302 return 0;
2303 }
2304 }
2305
2306end_of_arg:
2307 if (n < min) {
2308 bb_error_msg("too few values for %s (min %d)", key, min);
2309 return 0;
2310 }
2311
2312 argc--, argv++;
2313 }
2314
2315 return 1;
2316}
2317
2318#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
2319static int new_is_module_checksummed(struct obj_file *f)
2320{
2321 const char *p = get_modinfo_value(f, "using_checksums");
2322 if (p)
2323 return atoi(p);
2324 else
2325 return 0;
2326}
2327
2328/* Get the module's kernel version in the canonical integer form. */
2329
2330static int
2331new_get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
2332{
2333 char *p, *q;
2334 int a, b, c;
2335
2336 p = get_modinfo_value(f, "kernel_version");
2337 if (p == NULL)
2338 return -1;
2339 safe_strncpy(str, p, STRVERSIONLEN);
2340
2341 a = strtoul(p, &p, 10);
2342 if (*p != '.')
2343 return -1;
2344 b = strtoul(p + 1, &p, 10);
2345 if (*p != '.')
2346 return -1;
2347 c = strtoul(p + 1, &q, 10);
2348 if (p + 1 == q)
2349 return -1;
2350
2351 return a << 16 | b << 8 | c;
2352}
2353
2354#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */
2355
2356
2357/* Fetch the loaded modules, and all currently exported symbols. */
2358
2359static int new_get_kernel_symbols(void)
2360{
2361 char *module_names, *mn;
2362 struct external_module *modules, *m;
2363 struct new_module_symbol *syms, *s;
2364 size_t ret, bufsize, nmod, nsyms, i, j;
2365
2366 /* Collect the loaded modules. */
2367
2368 module_names = xmalloc(bufsize = 256);
2369retry_modules_load:
2370 if (query_module(NULL, QM_MODULES, module_names, bufsize, &ret)) {
2371 if (errno == ENOSPC && bufsize < ret) {
2372 module_names = xrealloc(module_names, bufsize = ret);
2373 goto retry_modules_load;
2374 }
2375 bb_perror_msg("QM_MODULES");
2376 return 0;
2377 }
2378
2379 n_ext_modules = nmod = ret;
2380
2381 /* Collect the modules' symbols. */
2382
2383 if (nmod){
2384 ext_modules = modules = xmalloc(nmod * sizeof(*modules));
2385 memset(modules, 0, nmod * sizeof(*modules));
2386 for (i = 0, mn = module_names, m = modules;
2387 i < nmod; ++i, ++m, mn += strlen(mn) + 1) {
2388 struct new_module_info info;
2389
2390 if (query_module(mn, QM_INFO, &info, sizeof(info), &ret)) {
2391 if (errno == ENOENT) {
2392 /* The module was removed out from underneath us. */
2393 continue;
2394 }
2395 bb_perror_msg("query_module: QM_INFO: %s", mn);
2396 return 0;
2397 }
2398
2399 syms = xmalloc(bufsize = 1024);
2400retry_mod_sym_load:
2401 if (query_module(mn, QM_SYMBOLS, syms, bufsize, &ret)) {
2402 switch (errno) {
2403 case ENOSPC:
2404 syms = xrealloc(syms, bufsize = ret);
2405 goto retry_mod_sym_load;
2406 case ENOENT:
2407 /* The module was removed out from underneath us. */
2408 continue;
2409 default:
2410 bb_perror_msg("query_module: QM_SYMBOLS: %s", mn);
2411 return 0;
2412 }
2413 }
2414 nsyms = ret;
2415
2416 m->name = mn;
2417 m->addr = info.addr;
2418 m->nsyms = nsyms;
2419 m->syms = syms;
2420
2421 for (j = 0, s = syms; j < nsyms; ++j, ++s) {
2422 s->name += (unsigned long) syms;
2423 }
2424 }
2425 }
2426
2427 /* Collect the kernel's symbols. */
2428
2429 syms = xmalloc(bufsize = 16 * 1024);
2430retry_kern_sym_load:
2431 if (query_module(NULL, QM_SYMBOLS, syms, bufsize, &ret)) {
2432 if (errno == ENOSPC && bufsize < ret) {
2433 syms = xrealloc(syms, bufsize = ret);
2434 goto retry_kern_sym_load;
2435 }
2436 bb_perror_msg("kernel: QM_SYMBOLS");
2437 return 0;
2438 }
2439 nksyms = nsyms = ret;
2440 ksyms = syms;
2441
2442 for (j = 0, s = syms; j < nsyms; ++j, ++s) {
2443 s->name += (unsigned long) syms;
2444 }
2445 return 1;
2446}
2447
2448
2449/* Return the kernel symbol checksum version, or zero if not used. */
2450
2451static int new_is_kernel_checksummed(void)
2452{
2453 struct new_module_symbol *s;
2454 size_t i;
2455
2456 /* Using_Versions is not the first symbol, but it should be in there. */
2457
2458 for (i = 0, s = ksyms; i < nksyms; ++i, ++s)
2459 if (strcmp((char *) s->name, "Using_Versions") == 0)
2460 return s->value;
2461
2462 return 0;
2463}
2464
2465
2466static int new_create_this_module(struct obj_file *f, const char *m_name)
2467{
2468 struct obj_section *sec;
2469
2470 sec = obj_create_alloced_section_first(f, ".this", tgt_sizeof_long,
2471 sizeof(struct new_module));
2472 memset(sec->contents, 0, sizeof(struct new_module));
2473
2474 obj_add_symbol(f, SPFX "__this_module", -1,
2475 ELFW(ST_INFO) (STB_LOCAL, STT_OBJECT), sec->idx, 0,
2476 sizeof(struct new_module));
2477
2478 obj_string_patch(f, sec->idx, offsetof(struct new_module, name),
2479 m_name);
2480
2481 return 1;
2482}
2483
2484#ifdef CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
2485/* add an entry to the __ksymtab section, creating it if necessary */
2486static void new_add_ksymtab(struct obj_file *f, struct obj_symbol *sym)
2487{
2488 struct obj_section *sec;
2489 ElfW(Addr) ofs;
2490
2491 /* ensure __ksymtab is allocated, EXPORT_NOSYMBOLS creates a non-alloc section.
2492 * If __ksymtab is defined but not marked alloc, x out the first character
2493 * (no obj_delete routine) and create a new __ksymtab with the correct
2494 * characteristics.
2495 */
2496 sec = obj_find_section(f, "__ksymtab");
2497 if (sec && !(sec->header.sh_flags & SHF_ALLOC)) {
2498 *((char *)(sec->name)) = 'x'; /* override const */
2499 sec = NULL;
2500 }
2501 if (!sec)
2502 sec = obj_create_alloced_section(f, "__ksymtab",
2503 tgt_sizeof_void_p, 0);
2504 if (!sec)
2505 return;
2506 sec->header.sh_flags |= SHF_ALLOC;
2507 sec->header.sh_addralign = tgt_sizeof_void_p; /* Empty section might
2508 be byte-aligned */
2509 ofs = sec->header.sh_size;
2510 obj_symbol_patch(f, sec->idx, ofs, sym);
2511 obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p, sym->name);
2512 obj_extend_section(sec, 2 * tgt_sizeof_char_p);
2513}
2514#endif /* CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
2515
2516static int new_create_module_ksymtab(struct obj_file *f)
2517{
2518 struct obj_section *sec;
2519 int i;
2520
2521 /* We must always add the module references. */
2522
2523 if (n_ext_modules_used) {
2524 struct new_module_ref *dep;
2525 struct obj_symbol *tm;
2526
2527 sec = obj_create_alloced_section(f, ".kmodtab", tgt_sizeof_void_p,
2528 (sizeof(struct new_module_ref)
2529 * n_ext_modules_used));
2530 if (!sec)
2531 return 0;
2532
2533 tm = obj_find_symbol(f, SPFX "__this_module");
2534 dep = (struct new_module_ref *) sec->contents;
2535 for (i = 0; i < n_ext_modules; ++i)
2536 if (ext_modules[i].used) {
2537 dep->dep = ext_modules[i].addr;
2538 obj_symbol_patch(f, sec->idx,
2539 (char *) &dep->ref - sec->contents, tm);
2540 dep->next_ref = 0;
2541 ++dep;
2542 }
2543 }
2544
2545 if (flag_export && !obj_find_section(f, "__ksymtab")) {
2546 size_t nsyms;
2547 int *loaded;
2548
2549 sec =
2550 obj_create_alloced_section(f, "__ksymtab", tgt_sizeof_void_p,
2551 0);
2552
2553 /* We don't want to export symbols residing in sections that
2554 aren't loaded. There are a number of these created so that
2555 we make sure certain module options don't appear twice. */
2556
2557 loaded = alloca(sizeof(int) * (i = f->header.e_shnum));
2558 while (--i >= 0)
2559 loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0;
2560
2561 for (nsyms = i = 0; i < HASH_BUCKETS; ++i) {
2562 struct obj_symbol *sym;
2563 for (sym = f->symtab[i]; sym; sym = sym->next)
2564 if (ELFW(ST_BIND) (sym->info) != STB_LOCAL
2565 && sym->secidx <= SHN_HIRESERVE
2566 && (sym->secidx >= SHN_LORESERVE
2567 || loaded[sym->secidx])) {
2568 ElfW(Addr) ofs = nsyms * 2 * tgt_sizeof_void_p;
2569
2570 obj_symbol_patch(f, sec->idx, ofs, sym);
2571 obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p,
2572 sym->name);
2573
2574 nsyms++;
2575 }
2576 }
2577
2578 obj_extend_section(sec, nsyms * 2 * tgt_sizeof_char_p);
2579 }
2580
2581 return 1;
2582}
2583
2584
2585static int
2586new_init_module(const char *m_name, struct obj_file *f, unsigned long m_size)
2587{
2588 struct new_module *module;
2589 struct obj_section *sec;
2590 void *image;
2591 int ret;
2592 tgt_long m_addr;
2593
2594 sec = obj_find_section(f, ".this");
2595 if (!sec || !sec->contents) {
2596 bb_perror_msg_and_die("corrupt module %s?",m_name);
2597 }
2598 module = (struct new_module *) sec->contents;
2599 m_addr = sec->header.sh_addr;
2600
2601 module->size_of_struct = sizeof(*module);
2602 module->size = m_size;
2603 module->flags = flag_autoclean ? NEW_MOD_AUTOCLEAN : 0;
2604
2605 sec = obj_find_section(f, "__ksymtab");
2606 if (sec && sec->header.sh_size) {
2607 module->syms = sec->header.sh_addr;
2608 module->nsyms = sec->header.sh_size / (2 * tgt_sizeof_char_p);
2609 }
2610
2611 if (n_ext_modules_used) {
2612 sec = obj_find_section(f, ".kmodtab");
2613 module->deps = sec->header.sh_addr;
2614 module->ndeps = n_ext_modules_used;
2615 }
2616
2617 module->init =
2618 obj_symbol_final_value(f, obj_find_symbol(f, SPFX "init_module"));
2619 module->cleanup =
2620 obj_symbol_final_value(f, obj_find_symbol(f, SPFX "cleanup_module"));
2621
2622 sec = obj_find_section(f, "__ex_table");
2623 if (sec) {
2624 module->ex_table_start = sec->header.sh_addr;
2625 module->ex_table_end = sec->header.sh_addr + sec->header.sh_size;
2626 }
2627
2628 sec = obj_find_section(f, ".text.init");
2629 if (sec) {
2630 module->runsize = sec->header.sh_addr - m_addr;
2631 }
2632 sec = obj_find_section(f, ".data.init");
2633 if (sec) {
2634 if (!module->runsize ||
2635 module->runsize > sec->header.sh_addr - m_addr)
2636 module->runsize = sec->header.sh_addr - m_addr;
2637 }
2638 sec = obj_find_section(f, ARCHDATA_SEC_NAME);
2639 if (sec && sec->header.sh_size) {
2640 module->archdata_start = (void*)sec->header.sh_addr;
2641 module->archdata_end = module->archdata_start + sec->header.sh_size;
2642 }
2643 sec = obj_find_section(f, KALLSYMS_SEC_NAME);
2644 if (sec && sec->header.sh_size) {
2645 module->kallsyms_start = (void*)sec->header.sh_addr;
2646 module->kallsyms_end = module->kallsyms_start + sec->header.sh_size;
2647 }
2648
2649 /* Whew! All of the initialization is complete. Collect the final
2650 module image and give it to the kernel. */
2651
2652 image = xmalloc(m_size);
2653 obj_create_image(f, image);
2654
2655 ret = init_module(m_name, (struct new_module *) image);
2656 if (ret)
2657 bb_perror_msg("init_module: %s", m_name);
2658
2659 free(image);
2660
2661 return ret == 0;
2662}
2663
2664
2665/*======================================================================*/
2666
2667static int
2668obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
2669 const char *string)
2670{
2671 struct obj_string_patch *p;
2672 struct obj_section *strsec;
2673 size_t len = strlen(string) + 1;
2674 char *loc;
2675
2676 p = xmalloc(sizeof(*p));
2677 p->next = f->string_patches;
2678 p->reloc_secidx = secidx;
2679 p->reloc_offset = offset;
2680 f->string_patches = p;
2681
2682 strsec = obj_find_section(f, ".kstrtab");
2683 if (strsec == NULL) {
2684 strsec = obj_create_alloced_section(f, ".kstrtab", 1, len);
2685 p->string_offset = 0;
2686 loc = strsec->contents;
2687 } else {
2688 p->string_offset = strsec->header.sh_size;
2689 loc = obj_extend_section(strsec, len);
2690 }
2691 memcpy(loc, string, len);
2692
2693 return 1;
2694}
2695
2696static int
2697obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
2698 struct obj_symbol *sym)
2699{
2700 struct obj_symbol_patch *p;
2701
2702 p = xmalloc(sizeof(*p));
2703 p->next = f->symbol_patches;
2704 p->reloc_secidx = secidx;
2705 p->reloc_offset = offset;
2706 p->sym = sym;
2707 f->symbol_patches = p;
2708
2709 return 1;
2710}
2711
2712static int obj_check_undefineds(struct obj_file *f)
2713{
2714 unsigned long i;
2715 int ret = 1;
2716
2717 for (i = 0; i < HASH_BUCKETS; ++i) {
2718 struct obj_symbol *sym;
2719 for (sym = f->symtab[i]; sym; sym = sym->next)
2720 if (sym->secidx == SHN_UNDEF) {
2721 if (ELFW(ST_BIND) (sym->info) == STB_WEAK) {
2722 sym->secidx = SHN_ABS;
2723 sym->value = 0;
2724 } else {
2725 if (!flag_quiet) {
2726 bb_error_msg("unresolved symbol %s", sym->name);
2727 }
2728 ret = 0;
2729 }
2730 }
2731 }
2732
2733 return ret;
2734}
2735
2736static void obj_allocate_commons(struct obj_file *f)
2737{
2738 struct common_entry {
2739 struct common_entry *next;
2740 struct obj_symbol *sym;
2741 } *common_head = NULL;
2742
2743 unsigned long i;
2744
2745 for (i = 0; i < HASH_BUCKETS; ++i) {
2746 struct obj_symbol *sym;
2747 for (sym = f->symtab[i]; sym; sym = sym->next)
2748 if (sym->secidx == SHN_COMMON) {
2749 /* Collect all COMMON symbols and sort them by size so as to
2750 minimize space wasted by alignment requirements. */
2751 {
2752 struct common_entry **p, *n;
2753 for (p = &common_head; *p; p = &(*p)->next)
2754 if (sym->size <= (*p)->sym->size)
2755 break;
2756
2757 n = alloca(sizeof(*n));
2758 n->next = *p;
2759 n->sym = sym;
2760 *p = n;
2761 }
2762 }
2763 }
2764
2765 for (i = 1; i < f->local_symtab_size; ++i) {
2766 struct obj_symbol *sym = f->local_symtab[i];
2767 if (sym && sym->secidx == SHN_COMMON) {
2768 struct common_entry **p, *n;
2769 for (p = &common_head; *p; p = &(*p)->next)
2770 if (sym == (*p)->sym)
2771 break;
2772 else if (sym->size < (*p)->sym->size) {
2773 n = alloca(sizeof(*n));
2774 n->next = *p;
2775 n->sym = sym;
2776 *p = n;
2777 break;
2778 }
2779 }
2780 }
2781
2782 if (common_head) {
2783 /* Find the bss section. */
2784 for (i = 0; i < f->header.e_shnum; ++i)
2785 if (f->sections[i]->header.sh_type == SHT_NOBITS)
2786 break;
2787
2788 /* If for some reason there hadn't been one, create one. */
2789 if (i == f->header.e_shnum) {
2790 struct obj_section *sec;
2791
2792 f->sections = xrealloc(f->sections, (i + 1) * sizeof(sec));
2793 f->sections[i] = sec = arch_new_section();
2794 f->header.e_shnum = i + 1;
2795
2796 memset(sec, 0, sizeof(*sec));
2797 sec->header.sh_type = SHT_PROGBITS;
2798 sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
2799 sec->name = ".bss";
2800 sec->idx = i;
2801 }
2802
2803 /* Allocate the COMMONS. */
2804 {
2805 ElfW(Addr) bss_size = f->sections[i]->header.sh_size;
2806 ElfW(Addr) max_align = f->sections[i]->header.sh_addralign;
2807 struct common_entry *c;
2808
2809 for (c = common_head; c; c = c->next) {
2810 ElfW(Addr) align = c->sym->value;
2811
2812 if (align > max_align)
2813 max_align = align;
2814 if (bss_size & (align - 1))
2815 bss_size = (bss_size | (align - 1)) + 1;
2816
2817 c->sym->secidx = i;
2818 c->sym->value = bss_size;
2819
2820 bss_size += c->sym->size;
2821 }
2822
2823 f->sections[i]->header.sh_size = bss_size;
2824 f->sections[i]->header.sh_addralign = max_align;
2825 }
2826 }
2827
2828 /* For the sake of patch relocation and parameter initialization,
2829 allocate zeroed data for NOBITS sections now. Note that after
2830 this we cannot assume NOBITS are really empty. */
2831 for (i = 0; i < f->header.e_shnum; ++i) {
2832 struct obj_section *s = f->sections[i];
2833 if (s->header.sh_type == SHT_NOBITS) {
2834 if (s->header.sh_size != 0)
2835 s->contents = memset(xmalloc(s->header.sh_size),
2836 0, s->header.sh_size);
2837 else
2838 s->contents = NULL;
2839
2840 s->header.sh_type = SHT_PROGBITS;
2841 }
2842 }
2843}
2844
2845static unsigned long obj_load_size(struct obj_file *f)
2846{
2847 unsigned long dot = 0;
2848 struct obj_section *sec;
2849
2850 /* Finalize the positions of the sections relative to one another. */
2851
2852 for (sec = f->load_order; sec; sec = sec->load_next) {
2853 ElfW(Addr) align;
2854
2855 align = sec->header.sh_addralign;
2856 if (align && (dot & (align - 1)))
2857 dot = (dot | (align - 1)) + 1;
2858
2859 sec->header.sh_addr = dot;
2860 dot += sec->header.sh_size;
2861 }
2862
2863 return dot;
2864}
2865
2866static int obj_relocate(struct obj_file *f, ElfW(Addr) base)
2867{
2868 int i, n = f->header.e_shnum;
2869 int ret = 1;
2870
2871 /* Finalize the addresses of the sections. */
2872
2873 f->baseaddr = base;
2874 for (i = 0; i < n; ++i)
2875 f->sections[i]->header.sh_addr += base;
2876
2877 /* And iterate over all of the relocations. */
2878
2879 for (i = 0; i < n; ++i) {
2880 struct obj_section *relsec, *symsec, *targsec, *strsec;
2881 ElfW(RelM) * rel, *relend;
2882 ElfW(Sym) * symtab;
2883 const char *strtab;
2884
2885 relsec = f->sections[i];
2886 if (relsec->header.sh_type != SHT_RELM)
2887 continue;
2888
2889 symsec = f->sections[relsec->header.sh_link];
2890 targsec = f->sections[relsec->header.sh_info];
2891 strsec = f->sections[symsec->header.sh_link];
2892
2893 rel = (ElfW(RelM) *) relsec->contents;
2894 relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM)));
2895 symtab = (ElfW(Sym) *) symsec->contents;
2896 strtab = (const char *) strsec->contents;
2897
2898 for (; rel < relend; ++rel) {
2899 ElfW(Addr) value = 0;
2900 struct obj_symbol *intsym = NULL;
2901 unsigned long symndx;
2902 ElfW(Sym) * extsym = 0;
2903 const char *errmsg;
2904
2905 /* Attempt to find a value to use for this relocation. */
2906
2907 symndx = ELFW(R_SYM) (rel->r_info);
2908 if (symndx) {
2909 /* Note we've already checked for undefined symbols. */
2910
2911 extsym = &symtab[symndx];
2912 if (ELFW(ST_BIND) (extsym->st_info) == STB_LOCAL) {
2913 /* Local symbols we look up in the local table to be sure
2914 we get the one that is really intended. */
2915 intsym = f->local_symtab[symndx];
2916 } else {
2917 /* Others we look up in the hash table. */
2918 const char *name;
2919 if (extsym->st_name)
2920 name = strtab + extsym->st_name;
2921 else
2922 name = f->sections[extsym->st_shndx]->name;
2923 intsym = obj_find_symbol(f, name);
2924 }
2925
2926 value = obj_symbol_final_value(f, intsym);
2927 intsym->referenced = 1;
2928 }
2929#if SHT_RELM == SHT_RELA
2930#if defined(__alpha__) && defined(AXP_BROKEN_GAS)
2931 /* Work around a nasty GAS bug, that is fixed as of 2.7.0.9. */
2932 if (!extsym || !extsym->st_name ||
2933 ELFW(ST_BIND) (extsym->st_info) != STB_LOCAL)
2934#endif
2935 value += rel->r_addend;
2936#endif
2937
2938 /* Do it! */
2939 switch (arch_apply_relocation
2940 (f, targsec, symsec, intsym, rel, value)) {
2941 case obj_reloc_ok:
2942 break;
2943
2944 case obj_reloc_overflow:
2945 errmsg = "Relocation overflow";
2946 goto bad_reloc;
2947 case obj_reloc_dangerous:
2948 errmsg = "Dangerous relocation";
2949 goto bad_reloc;
2950 case obj_reloc_unhandled:
2951 errmsg = "Unhandled relocation";
2952bad_reloc:
2953 if (extsym) {
2954 bb_error_msg("%s of type %ld for %s", errmsg,
2955 (long) ELFW(R_TYPE) (rel->r_info),
2956 strtab + extsym->st_name);
2957 } else {
2958 bb_error_msg("%s of type %ld", errmsg,
2959 (long) ELFW(R_TYPE) (rel->r_info));
2960 }
2961 ret = 0;
2962 break;
2963 }
2964 }
2965 }
2966
2967 /* Finally, take care of the patches. */
2968
2969 if (f->string_patches) {
2970 struct obj_string_patch *p;
2971 struct obj_section *strsec;
2972 ElfW(Addr) strsec_base;
2973 strsec = obj_find_section(f, ".kstrtab");
2974 strsec_base = strsec->header.sh_addr;
2975
2976 for (p = f->string_patches; p; p = p->next) {
2977 struct obj_section *targsec = f->sections[p->reloc_secidx];
2978 *(ElfW(Addr) *) (targsec->contents + p->reloc_offset)
2979 = strsec_base + p->string_offset;
2980 }
2981 }
2982
2983 if (f->symbol_patches) {
2984 struct obj_symbol_patch *p;
2985
2986 for (p = f->symbol_patches; p; p = p->next) {
2987 struct obj_section *targsec = f->sections[p->reloc_secidx];
2988 *(ElfW(Addr) *) (targsec->contents + p->reloc_offset)
2989 = obj_symbol_final_value(f, p->sym);
2990 }
2991 }
2992
2993 return ret;
2994}
2995
2996static int obj_create_image(struct obj_file *f, char *image)
2997{
2998 struct obj_section *sec;
2999 ElfW(Addr) base = f->baseaddr;
3000
3001 for (sec = f->load_order; sec; sec = sec->load_next) {
3002 char *secimg;
3003
3004 if (sec->contents == 0 || sec->header.sh_size == 0)
3005 continue;
3006
3007 secimg = image + (sec->header.sh_addr - base);
3008
3009 /* Note that we allocated data for NOBITS sections earlier. */
3010 memcpy(secimg, sec->contents, sec->header.sh_size);
3011 }
3012
3013 return 1;
3014}
3015
3016/*======================================================================*/
3017
3018static struct obj_file *obj_load(FILE * fp, int loadprogbits)
3019{
3020 struct obj_file *f;
3021 ElfW(Shdr) * section_headers;
3022 int shnum, i;
3023 char *shstrtab;
3024
3025 /* Read the file header. */
3026
3027 f = arch_new_file();
3028 memset(f, 0, sizeof(*f));
3029 f->symbol_cmp = strcmp;
3030 f->symbol_hash = obj_elf_hash;
3031 f->load_order_search_start = &f->load_order;
3032
3033 fseek(fp, 0, SEEK_SET);
3034 if (fread(&f->header, sizeof(f->header), 1, fp) != 1) {
3035 bb_perror_msg("error reading ELF header");
3036 return NULL;
3037 }
3038
3039 if (f->header.e_ident[EI_MAG0] != ELFMAG0
3040 || f->header.e_ident[EI_MAG1] != ELFMAG1
3041 || f->header.e_ident[EI_MAG2] != ELFMAG2
3042 || f->header.e_ident[EI_MAG3] != ELFMAG3) {
3043 bb_error_msg("not an ELF file");
3044 return NULL;
3045 }
3046 if (f->header.e_ident[EI_CLASS] != ELFCLASSM
3047 || f->header.e_ident[EI_DATA] != ELFDATAM
3048 || f->header.e_ident[EI_VERSION] != EV_CURRENT
3049 || !MATCH_MACHINE(f->header.e_machine)) {
3050 bb_error_msg("ELF file not for this architecture");
3051 return NULL;
3052 }
3053 if (f->header.e_type != ET_REL) {
3054 bb_error_msg("ELF file not a relocatable object");
3055 return NULL;
3056 }
3057
3058 /* Read the section headers. */
3059
3060 if (f->header.e_shentsize != sizeof(ElfW(Shdr))) {
3061 bb_error_msg("section header size mismatch: %lu != %lu",
3062 (unsigned long) f->header.e_shentsize,
3063 (unsigned long) sizeof(ElfW(Shdr)));
3064 return NULL;
3065 }
3066
3067 shnum = f->header.e_shnum;
3068 f->sections = xmalloc(sizeof(struct obj_section *) * shnum);
3069 memset(f->sections, 0, sizeof(struct obj_section *) * shnum);
3070
3071 section_headers = alloca(sizeof(ElfW(Shdr)) * shnum);
3072 fseek(fp, f->header.e_shoff, SEEK_SET);
3073 if (fread(section_headers, sizeof(ElfW(Shdr)), shnum, fp) != shnum) {
3074 bb_perror_msg("error reading ELF section headers");
3075 return NULL;
3076 }
3077
3078 /* Read the section data. */
3079
3080 for (i = 0; i < shnum; ++i) {
3081 struct obj_section *sec;
3082
3083 f->sections[i] = sec = arch_new_section();
3084 memset(sec, 0, sizeof(*sec));
3085
3086 sec->header = section_headers[i];
3087 sec->idx = i;
3088
3089 if(sec->header.sh_size) switch (sec->header.sh_type) {
3090 case SHT_NULL:
3091 case SHT_NOTE:
3092 case SHT_NOBITS:
3093 /* ignore */
3094 break;
3095
3096 case SHT_PROGBITS:
3097#if LOADBITS
3098 if (!loadprogbits) {
3099 sec->contents = NULL;
3100 break;
3101 }
3102#endif
3103 case SHT_SYMTAB:
3104 case SHT_STRTAB:
3105 case SHT_RELM:
3106 if (sec->header.sh_size > 0) {
3107 sec->contents = xmalloc(sec->header.sh_size);
3108 fseek(fp, sec->header.sh_offset, SEEK_SET);
3109 if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) {
3110 bb_perror_msg("error reading ELF section data");
3111 return NULL;
3112 }
3113 } else {
3114 sec->contents = NULL;
3115 }
3116 break;
3117
3118#if SHT_RELM == SHT_REL
3119 case SHT_RELA:
3120 bb_error_msg("RELA relocations not supported on this architecture");
3121 return NULL;
3122#else
3123 case SHT_REL:
3124 bb_error_msg("REL relocations not supported on this architecture");
3125 return NULL;
3126#endif
3127
3128 default:
3129 if (sec->header.sh_type >= SHT_LOPROC) {
3130 /* Assume processor specific section types are debug
3131 info and can safely be ignored. If this is ever not
3132 the case (Hello MIPS?), don't put ifdefs here but
3133 create an arch_load_proc_section(). */
3134 break;
3135 }
3136
3137 bb_error_msg("can't handle sections of type %ld",
3138 (long) sec->header.sh_type);
3139 return NULL;
3140 }
3141 }
3142
3143 /* Do what sort of interpretation as needed by each section. */
3144
3145 shstrtab = f->sections[f->header.e_shstrndx]->contents;
3146
3147 for (i = 0; i < shnum; ++i) {
3148 struct obj_section *sec = f->sections[i];
3149 sec->name = shstrtab + sec->header.sh_name;
3150 }
3151
3152 for (i = 0; i < shnum; ++i) {
3153 struct obj_section *sec = f->sections[i];
3154
3155 /* .modinfo should be contents only but gcc has no attribute for that.
3156 * The kernel may have marked .modinfo as ALLOC, ignore this bit.
3157 */
3158 if (strcmp(sec->name, ".modinfo") == 0)
3159 sec->header.sh_flags &= ~SHF_ALLOC;
3160
3161 if (sec->header.sh_flags & SHF_ALLOC)
3162 obj_insert_section_load_order(f, sec);
3163
3164 switch (sec->header.sh_type) {
3165 case SHT_SYMTAB:
3166 {
3167 unsigned long nsym, j;
3168 char *strtab;
3169 ElfW(Sym) * sym;
3170
3171 if (sec->header.sh_entsize != sizeof(ElfW(Sym))) {
3172 bb_error_msg("symbol size mismatch: %lu != %lu",
3173 (unsigned long) sec->header.sh_entsize,
3174 (unsigned long) sizeof(ElfW(Sym)));
3175 return NULL;
3176 }
3177
3178 nsym = sec->header.sh_size / sizeof(ElfW(Sym));
3179 strtab = f->sections[sec->header.sh_link]->contents;
3180 sym = (ElfW(Sym) *) sec->contents;
3181
3182 /* Allocate space for a table of local symbols. */
3183 j = f->local_symtab_size = sec->header.sh_info;
3184 f->local_symtab = xcalloc(j, sizeof(struct obj_symbol *));
3185
3186 /* Insert all symbols into the hash table. */
3187 for (j = 1, ++sym; j < nsym; ++j, ++sym) {
3188 ElfW(Addr) val = sym->st_value;
3189 const char *name;
3190 if (sym->st_name)
3191 name = strtab + sym->st_name;
3192 else if (sym->st_shndx < shnum)
3193 name = f->sections[sym->st_shndx]->name;
3194 else
3195 continue;
3196
3197#if defined(__SH5__)
3198 /*
3199 * For sh64 it is possible that the target of a branch
3200 * requires a mode switch (32 to 16 and back again).
3201 *
3202 * This is implied by the lsb being set in the target
3203 * address for SHmedia mode and clear for SHcompact.
3204 */
3205 val |= sym->st_other & 4;
3206#endif
3207
3208 obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx,
3209 val, sym->st_size);
3210 }
3211 }
3212 break;
3213
3214 case SHT_RELM:
3215 if (sec->header.sh_entsize != sizeof(ElfW(RelM))) {
3216 bb_error_msg("relocation entry size mismatch: %lu != %lu",
3217 (unsigned long) sec->header.sh_entsize,
3218 (unsigned long) sizeof(ElfW(RelM)));
3219 return NULL;
3220 }
3221 break;
3222 /* XXX Relocation code from modutils-2.3.19 is not here.
3223 * Why? That's about 20 lines of code from obj/obj_load.c,
3224 * which gets done in a second pass through the sections.
3225 * This BusyBox insmod does similar work in obj_relocate(). */
3226 }
3227 }
3228
3229 return f;
3230}
3231
3232#ifdef CONFIG_FEATURE_INSMOD_LOADINKMEM
3233/*
3234 * load the unloaded sections directly into the memory allocated by
3235 * kernel for the module
3236 */
3237
3238static int obj_load_progbits(FILE * fp, struct obj_file* f, char* imagebase)
3239{
3240 ElfW(Addr) base = f->baseaddr;
3241 struct obj_section* sec;
3242
3243 for (sec = f->load_order; sec; sec = sec->load_next) {
3244
3245 /* section already loaded? */
3246 if (sec->contents != NULL)
3247 continue;
3248
3249 if (sec->header.sh_size == 0)
3250 continue;
3251
3252 sec->contents = imagebase + (sec->header.sh_addr - base);
3253 fseek(fp, sec->header.sh_offset, SEEK_SET);
3254 if (fread(sec->contents, sec->header.sh_size, 1, fp) != 1) {
3255 bb_error_msg("error reading ELF section data: %s\n", strerror(errno));
3256 return 0;
3257 }
3258
3259 }
3260 return 1;
3261}
3262#endif
3263
3264static void hide_special_symbols(struct obj_file *f)
3265{
3266 static const char *const specials[] = {
3267 SPFX "cleanup_module",
3268 SPFX "init_module",
3269 SPFX "kernel_version",
3270 NULL
3271 };
3272
3273 struct obj_symbol *sym;
3274 const char *const *p;
3275
3276 for (p = specials; *p; ++p)
3277 if ((sym = obj_find_symbol(f, *p)) != NULL)
3278 sym->info =
3279 ELFW(ST_INFO) (STB_LOCAL, ELFW(ST_TYPE) (sym->info));
3280}
3281
3282
3283#ifdef CONFIG_FEATURE_CHECK_TAINTED_MODULE
3284static int obj_gpl_license(struct obj_file *f, const char **license)
3285{
3286 struct obj_section *sec;
3287 /* This list must match *exactly* the list of allowable licenses in
3288 * linux/include/linux/module.h. Checking for leading "GPL" will not
3289 * work, somebody will use "GPL sucks, this is proprietary".
3290 */
3291 static const char *gpl_licenses[] = {
3292 "GPL",
3293 "GPL v2",
3294 "GPL and additional rights",
3295 "Dual BSD/GPL",
3296 "Dual MPL/GPL",
3297 };
3298
3299 if ((sec = obj_find_section(f, ".modinfo"))) {
3300 const char *value, *ptr, *endptr;
3301 ptr = sec->contents;
3302 endptr = ptr + sec->header.sh_size;
3303 while (ptr < endptr) {
3304 if ((value = strchr(ptr, '=')) && strncmp(ptr, "license", value-ptr) == 0) {
3305 int i;
3306 if (license)
3307 *license = value+1;
3308 for (i = 0; i < sizeof(gpl_licenses)/sizeof(gpl_licenses[0]); ++i) {
3309 if (strcmp(value+1, gpl_licenses[i]) == 0)
3310 return(0);
3311 }
3312 return(2);
3313 }
3314 if (strchr(ptr, '\0'))
3315 ptr = strchr(ptr, '\0') + 1;
3316 else
3317 ptr = endptr;
3318 }
3319 }
3320 return(1);
3321}
3322
3323#define TAINT_FILENAME "/proc/sys/kernel/tainted"
3324#define TAINT_PROPRIETORY_MODULE (1<<0)
3325#define TAINT_FORCED_MODULE (1<<1)
3326#define TAINT_UNSAFE_SMP (1<<2)
3327#define TAINT_URL "http://www.tux.org/lkml/#export-tainted"
3328
3329static void set_tainted(struct obj_file *f, int fd, char *m_name,
3330 int kernel_has_tainted, int taint, const char *text1, const char *text2)
3331{
3332 char buf[80];
3333 int oldval;
3334 static int first = 1;
3335 if (fd < 0 && !kernel_has_tainted)
3336 return; /* New modutils on old kernel */
3337 printf("Warning: loading %s will taint the kernel: %s%s\n",
3338 m_name, text1, text2);
3339 if (first) {
3340 printf(" See %s for information about tainted modules\n", TAINT_URL);
3341 first = 0;
3342 }
3343 if (fd >= 0) {
3344 read(fd, buf, sizeof(buf)-1);
3345 buf[sizeof(buf)-1] = '\0';
3346 oldval = strtoul(buf, NULL, 10);
3347 sprintf(buf, "%d\n", oldval | taint);
3348 write(fd, buf, strlen(buf));
3349 }
3350}
3351
3352/* Check if loading this module will taint the kernel. */
3353static void check_tainted_module(struct obj_file *f, char *m_name)
3354{
3355 static const char tainted_file[] = TAINT_FILENAME;
3356 int fd, kernel_has_tainted;
3357 const char *ptr;
3358
3359 kernel_has_tainted = 1;
3360 if ((fd = open(tainted_file, O_RDWR)) < 0) {
3361 if (errno == ENOENT)
3362 kernel_has_tainted = 0;
3363 else if (errno == EACCES)
3364 kernel_has_tainted = 1;
3365 else {
3366 perror(tainted_file);
3367 kernel_has_tainted = 0;
3368 }
3369 }
3370
3371 switch (obj_gpl_license(f, &ptr)) {
3372 case 0:
3373 break;
3374 case 1:
3375 set_tainted(f, fd, m_name, kernel_has_tainted, TAINT_PROPRIETORY_MODULE, "no license", "");
3376 break;
3377 case 2:
3378 /* The module has a non-GPL license so we pretend that the
3379 * kernel always has a taint flag to get a warning even on
3380 * kernels without the proc flag.
3381 */
3382 set_tainted(f, fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "non-GPL license - ", ptr);
3383 break;
3384 default:
3385 set_tainted(f, fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "Unexpected return from obj_gpl_license", "");
3386 break;
3387 }
3388
3389 if (flag_force_load)
3390 set_tainted(f, fd, m_name, 1, TAINT_FORCED_MODULE, "forced load", "");
3391
3392 if (fd >= 0)
3393 close(fd);
3394}
3395#else /* CONFIG_FEATURE_CHECK_TAINTED_MODULE */
3396#define check_tainted_module(x, y) do { } while(0);
3397#endif /* CONFIG_FEATURE_CHECK_TAINTED_MODULE */
3398
3399#ifdef CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
3400/* add module source, timestamp, kernel version and a symbol for the
3401 * start of some sections. this info is used by ksymoops to do better
3402 * debugging.
3403 */
3404static int
3405get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
3406{
3407#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
3408 return new_get_module_version(f, str);
3409#else /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */
3410 strncpy(str, "???", sizeof(str));
3411 return -1;
3412#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */
3413}
3414
3415/* add module source, timestamp, kernel version and a symbol for the
3416 * start of some sections. this info is used by ksymoops to do better
3417 * debugging.
3418 */
3419static void
3420add_ksymoops_symbols(struct obj_file *f, const char *filename,
3421 const char *m_name)
3422{
3423 static const char symprefix[] = "__insmod_";
3424 struct obj_section *sec;
3425 struct obj_symbol *sym;
3426 char *name, *absolute_filename;
3427 char str[STRVERSIONLEN], real[PATH_MAX];
3428 int i, l, lm_name, lfilename, use_ksymtab, version;
3429 struct stat statbuf;
3430
3431 static const char *section_names[] = {
3432 ".text",
3433 ".rodata",
3434 ".data",
3435 ".bss"
3436 ".sbss"
3437 };
3438
3439 if (realpath(filename, real)) {
3440 absolute_filename = bb_xstrdup(real);
3441 }
3442 else {
3443 int save_errno = errno;
3444 bb_error_msg("cannot get realpath for %s", filename);
3445 errno = save_errno;
3446 perror("");
3447 absolute_filename = bb_xstrdup(filename);
3448 }
3449
3450 lm_name = strlen(m_name);
3451 lfilename = strlen(absolute_filename);
3452
3453 /* add to ksymtab if it already exists or there is no ksymtab and other symbols
3454 * are not to be exported. otherwise leave ksymtab alone for now, the
3455 * "export all symbols" compatibility code will export these symbols later.
3456 */
3457 use_ksymtab = obj_find_section(f, "__ksymtab") || !flag_export;
3458
3459 if ((sec = obj_find_section(f, ".this"))) {
3460 /* tag the module header with the object name, last modified
3461 * timestamp and module version. worst case for module version
3462 * is 0xffffff, decimal 16777215. putting all three fields in
3463 * one symbol is less readable but saves kernel space.
3464 */
3465 l = sizeof(symprefix)+ /* "__insmod_" */
3466 lm_name+ /* module name */
3467 2+ /* "_O" */
3468 lfilename+ /* object filename */
3469 2+ /* "_M" */
3470 2*sizeof(statbuf.st_mtime)+ /* mtime in hex */
3471 2+ /* "_V" */
3472 8+ /* version in dec */
3473 1; /* nul */
3474 name = xmalloc(l);
3475 if (stat(absolute_filename, &statbuf) != 0)
3476 statbuf.st_mtime = 0;
3477 version = get_module_version(f, str); /* -1 if not found */
3478 snprintf(name, l, "%s%s_O%s_M%0*lX_V%d",
3479 symprefix, m_name, absolute_filename,
3480 (int)(2*sizeof(statbuf.st_mtime)), statbuf.st_mtime,
3481 version);
3482 sym = obj_add_symbol(f, name, -1,
3483 ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),
3484 sec->idx, sec->header.sh_addr, 0);
3485 if (use_ksymtab)
3486 new_add_ksymtab(f, sym);
3487 }
3488 free(absolute_filename);
3489#ifdef _NOT_SUPPORTED_
3490 /* record where the persistent data is going, same address as previous symbol */
3491
3492 if (f->persist) {
3493 l = sizeof(symprefix)+ /* "__insmod_" */
3494 lm_name+ /* module name */
3495 2+ /* "_P" */
3496 strlen(f->persist)+ /* data store */
3497 1; /* nul */
3498 name = xmalloc(l);
3499 snprintf(name, l, "%s%s_P%s",
3500 symprefix, m_name, f->persist);
3501 sym = obj_add_symbol(f, name, -1, ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),
3502 sec->idx, sec->header.sh_addr, 0);
3503 if (use_ksymtab)
3504 new_add_ksymtab(f, sym);
3505 }
3506#endif /* _NOT_SUPPORTED_ */
3507 /* tag the desired sections if size is non-zero */
3508
3509 for (i = 0; i < sizeof(section_names)/sizeof(section_names[0]); ++i) {
3510 if ((sec = obj_find_section(f, section_names[i])) &&
3511 sec->header.sh_size) {
3512 l = sizeof(symprefix)+ /* "__insmod_" */
3513 lm_name+ /* module name */
3514 2+ /* "_S" */
3515 strlen(sec->name)+ /* section name */
3516 2+ /* "_L" */
3517 8+ /* length in dec */
3518 1; /* nul */
3519 name = xmalloc(l);
3520 snprintf(name, l, "%s%s_S%s_L%ld",
3521 symprefix, m_name, sec->name,
3522 (long)sec->header.sh_size);
3523 sym = obj_add_symbol(f, name, -1, ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE),
3524 sec->idx, sec->header.sh_addr, 0);
3525 if (use_ksymtab)
3526 new_add_ksymtab(f, sym);
3527 }
3528 }
3529}
3530#endif /* CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
3531
3532#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
3533static void print_load_map(struct obj_file *f)
3534{
3535 struct obj_symbol *sym;
3536 struct obj_symbol **all, **p;
3537 struct obj_section *sec;
3538 int i, nsyms, *loaded;
3539
3540 /* Report on the section layout. */
3541
3542 printf("Sections: Size %-*s Align\n",
3543 (int) (2 * sizeof(void *)), "Address");
3544
3545 for (sec = f->load_order; sec; sec = sec->load_next) {
3546 int a;
3547 unsigned long tmp;
3548
3549 for (a = -1, tmp = sec->header.sh_addralign; tmp; ++a)
3550 tmp >>= 1;
3551 if (a == -1)
3552 a = 0;
3553
3554 printf("%-15s %08lx %0*lx 2**%d\n",
3555 sec->name,
3556 (long)sec->header.sh_size,
3557 (int) (2 * sizeof(void *)),
3558 (long)sec->header.sh_addr,
3559 a);
3560 }
3561#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL
3562 /* Quick reference which section indicies are loaded. */
3563
3564 loaded = alloca(sizeof(int) * (i = f->header.e_shnum));
3565 while (--i >= 0)
3566 loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0;
3567
3568 /* Collect the symbols we'll be listing. */
3569
3570 for (nsyms = i = 0; i < HASH_BUCKETS; ++i)
3571 for (sym = f->symtab[i]; sym; sym = sym->next)
3572 if (sym->secidx <= SHN_HIRESERVE
3573 && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]))
3574 ++nsyms;
3575
3576 all = alloca(nsyms * sizeof(struct obj_symbol *));
3577
3578 for (i = 0, p = all; i < HASH_BUCKETS; ++i)
3579 for (sym = f->symtab[i]; sym; sym = sym->next)
3580 if (sym->secidx <= SHN_HIRESERVE
3581 && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx]))
3582 *p++ = sym;
3583
3584 /* And list them. */
3585 printf("\nSymbols:\n");
3586 for (p = all; p < all + nsyms; ++p) {
3587 char type = '?';
3588 unsigned long value;
3589
3590 sym = *p;
3591 if (sym->secidx == SHN_ABS) {
3592 type = 'A';
3593 value = sym->value;
3594 } else if (sym->secidx == SHN_UNDEF) {
3595 type = 'U';
3596 value = 0;
3597 } else {
3598 sec = f->sections[sym->secidx];
3599
3600 if (sec->header.sh_type == SHT_NOBITS)
3601 type = 'B';
3602 else if (sec->header.sh_flags & SHF_ALLOC) {
3603 if (sec->header.sh_flags & SHF_EXECINSTR)
3604 type = 'T';
3605 else if (sec->header.sh_flags & SHF_WRITE)
3606 type = 'D';
3607 else
3608 type = 'R';
3609 }
3610 value = sym->value + sec->header.sh_addr;
3611 }
3612
3613 if (ELFW(ST_BIND) (sym->info) == STB_LOCAL)
3614 type = tolower(type);
3615
3616 printf("%0*lx %c %s\n", (int) (2 * sizeof(void *)), value,
3617 type, sym->name);
3618 }
3619#endif
3620}
3621
3622#endif
3623
3624extern int insmod_main( int argc, char **argv)
3625{
3626 int opt;
3627 int len;
3628 int k_crcs;
3629 char *tmp, *tmp1;
3630 unsigned long m_size;
3631 ElfW(Addr) m_addr;
3632 struct obj_file *f;
3633 struct stat st;
3634 char *m_name = 0;
3635 int exit_status = EXIT_FAILURE;
3636 int m_has_modinfo;
3637#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
3638 struct utsname uts_info;
3639 char m_strversion[STRVERSIONLEN];
3640 int m_version, m_crcs;
3641#endif
3642#ifdef CONFIG_FEATURE_CLEAN_UP
3643 FILE *fp = 0;
3644#else
3645 FILE *fp;
3646#endif
3647#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
3648 int flag_print_load_map = 0;
3649#endif
3650 int k_version = 0;
3651 struct utsname myuname;
3652
3653 /* Parse any options */
3654#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
3655 while ((opt = getopt(argc, argv, "fkqsvxmLo:")) > 0)
3656#else
3657 while ((opt = getopt(argc, argv, "fkqsvxLo:")) > 0)
3658#endif
3659 {
3660 switch (opt) {
3661 case 'f': /* force loading */
3662 flag_force_load = 1;
3663 break;
3664 case 'k': /* module loaded by kerneld, auto-cleanable */
3665 flag_autoclean = 1;
3666 break;
3667 case 's': /* log to syslog */
3668 /* log to syslog -- not supported */
3669 /* but kernel needs this for request_module(), */
3670 /* as this calls: modprobe -k -s -- <module> */
3671 /* so silently ignore this flag */
3672 break;
3673 case 'v': /* verbose output */
3674 flag_verbose = 1;
3675 break;
3676 case 'q': /* silent */
3677 flag_quiet = 1;
3678 break;
3679 case 'x': /* do not export externs */
3680 flag_export = 0;
3681 break;
3682 case 'o': /* name the output module */
3683 free(m_name);
3684 m_name = bb_xstrdup(optarg);
3685 break;
3686 case 'L': /* Stub warning */
3687 /* This is needed for compatibility with modprobe.
3688 * In theory, this does locking, but we don't do
3689 * that. So be careful and plan your life around not
3690 * loading the same module 50 times concurrently. */
3691 break;
3692#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
3693 case 'm': /* print module load map */
3694 flag_print_load_map = 1;
3695 break;
3696#endif
3697 default:
3698 bb_show_usage();
3699 }
3700 }
3701
3702 if (argv[optind] == NULL) {
3703 bb_show_usage();
3704 }
3705
3706 /* Grab the module name */
3707 tmp1 = bb_xstrdup(argv[optind]);
3708 tmp = basename(tmp1);
3709 len = strlen(tmp);
3710
3711 if (uname(&myuname) == 0) {
3712 if (myuname.release[0] == '2') {
3713 k_version = myuname.release[2] - '0';
3714 }
3715 }
3716
3717#if defined(CONFIG_FEATURE_2_6_MODULES)
3718 if (k_version > 4 && len > 3 && tmp[len - 3] == '.' &&
3719 tmp[len - 2] == 'k' && tmp[len - 1] == 'o') {
3720 len-=3;
3721 tmp[len] = '\0';
3722 }
3723 else
3724#endif
3725 if (len > 2 && tmp[len - 2] == '.' && tmp[len - 1] == 'o') {
3726 len-=2;
3727 tmp[len] = '\0';
3728 }
3729
3730
3731#if defined(CONFIG_FEATURE_2_6_MODULES)
3732 if (k_version > 4)
3733 bb_xasprintf(&m_fullName, "%s.ko", tmp);
3734 else
3735#endif
3736 bb_xasprintf(&m_fullName, "%s.o", tmp);
3737
3738 if (!m_name) {
3739 m_name = tmp;
3740 } else {
3741 free(tmp1);
3742 tmp1 = 0; /* flag for free(m_name) before exit() */
3743 }
3744
3745 /* Get a filedesc for the module. Check we we have a complete path */
3746 if (stat(argv[optind], &st) < 0 || !S_ISREG(st.st_mode) ||
3747 (fp = fopen(argv[optind], "r")) == NULL) {
3748 /* Hmm. Could not open it. First search under /lib/modules/`uname -r`,
3749 * but do not error out yet if we fail to find it... */
3750 if (k_version) { /* uname succeedd */
3751 char *module_dir;
3752 char *tmdn;
3753 char real_module_dir[FILENAME_MAX];
3754
3755 tmdn = concat_path_file(_PATH_MODULES, myuname.release);
3756 /* Jump through hoops in case /lib/modules/`uname -r`
3757 * is a symlink. We do not want recursive_action to
3758 * follow symlinks, but we do want to follow the
3759 * /lib/modules/`uname -r` dir, So resolve it ourselves
3760 * if it is a link... */
3761 if (realpath (tmdn, real_module_dir) == NULL)
3762 module_dir = tmdn;
3763 else
3764 module_dir = real_module_dir;
3765 recursive_action(module_dir, TRUE, FALSE, FALSE,
3766 check_module_name_match, 0, m_fullName);
3767 free(tmdn);
3768 }
3769
3770 /* Check if we have found anything yet */
3771 if (m_filename == 0 || ((fp = fopen(m_filename, "r")) == NULL))
3772 {
3773 char module_dir[FILENAME_MAX];
3774
3775 free(m_filename);
3776 m_filename = 0;
3777 if (realpath (_PATH_MODULES, module_dir) == NULL)
3778 strcpy(module_dir, _PATH_MODULES);
3779 /* No module found under /lib/modules/`uname -r`, this
3780 * time cast the net a bit wider. Search /lib/modules/ */
3781 if (! recursive_action(module_dir, TRUE, FALSE, FALSE,
3782 check_module_name_match, 0, m_fullName))
3783 {
3784 if (m_filename == 0
3785 || ((fp = fopen(m_filename, "r")) == NULL))
3786 {
3787 bb_error_msg("%s: no module by that name found", m_fullName);
3788 goto out;
3789 }
3790 } else
3791 bb_error_msg_and_die("%s: no module by that name found", m_fullName);
3792 }
3793 } else
3794 m_filename = bb_xstrdup(argv[optind]);
3795
3796 if (!flag_quiet)
3797 printf("Using %s\n", m_filename);
3798
3799#ifdef CONFIG_FEATURE_2_6_MODULES
3800 if (k_version > 4)
3801 {
3802 optind--;
3803 argv[optind + 1] = m_filename;
3804 return insmod_ng_main(argc - optind, argv + optind);
3805 }
3806#endif
3807
3808 if ((f = obj_load(fp, LOADBITS)) == NULL)
3809 bb_perror_msg_and_die("Could not load the module");
3810
3811 if (get_modinfo_value(f, "kernel_version") == NULL)
3812 m_has_modinfo = 0;
3813 else
3814 m_has_modinfo = 1;
3815
3816#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
3817 /* Version correspondence? */
3818 if (!flag_quiet) {
3819 if (uname(&uts_info) < 0)
3820 uts_info.release[0] = '\0';
3821 if (m_has_modinfo) {
3822 m_version = new_get_module_version(f, m_strversion);
3823 if (m_version == -1) {
3824 bb_error_msg("couldn't find the kernel version the module was "
3825 "compiled for");
3826 goto out;
3827 }
3828 }
3829
3830 if (strncmp(uts_info.release, m_strversion, STRVERSIONLEN) != 0) {
3831 if (flag_force_load) {
3832 bb_error_msg("Warning: kernel-module version mismatch\n"
3833 "\t%s was compiled for kernel version %s\n"
3834 "\twhile this kernel is version %s",
3835 m_filename, m_strversion, uts_info.release);
3836 } else {
3837 bb_error_msg("kernel-module version mismatch\n"
3838 "\t%s was compiled for kernel version %s\n"
3839 "\twhile this kernel is version %s.",
3840 m_filename, m_strversion, uts_info.release);
3841 goto out;
3842 }
3843 }
3844 }
3845 k_crcs = 0;
3846#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */
3847
3848 if (!query_module(NULL, 0, NULL, 0, NULL)) {
3849 if (!new_get_kernel_symbols())
3850 goto out;
3851 k_crcs = new_is_kernel_checksummed();
3852 } else {
3853 bb_error_msg("Not configured to support old kernels");
3854 goto out;
3855 }
3856
3857#ifdef CONFIG_FEATURE_INSMOD_VERSION_CHECKING
3858 m_crcs = 0;
3859 if (m_has_modinfo)
3860 m_crcs = new_is_module_checksummed(f);
3861
3862 if (m_crcs != k_crcs)
3863 obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash);
3864#endif /* CONFIG_FEATURE_INSMOD_VERSION_CHECKING */
3865
3866 /* Let the module know about the kernel symbols. */
3867 add_kernel_symbols(f);
3868
3869 /* Allocate common symbols, symbol tables, and string tables. */
3870
3871 if (!new_create_this_module(f, m_name))
3872 {
3873 goto out;
3874 }
3875
3876 if (!obj_check_undefineds(f)) {
3877 goto out;
3878 }
3879 obj_allocate_commons(f);
3880 check_tainted_module(f, m_name);
3881
3882 /* done with the module name, on to the optional var=value arguments */
3883 ++optind;
3884
3885 if (optind < argc) {
3886 if (!new_process_module_arguments(f, argc - optind, argv + optind))
3887 {
3888 goto out;
3889 }
3890 }
3891
3892 arch_create_got(f);
3893 hide_special_symbols(f);
3894
3895#ifdef CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
3896 add_ksymoops_symbols(f, m_filename, m_name);
3897#endif /* CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
3898
3899 new_create_module_ksymtab(f);
3900
3901 /* Find current size of the module */
3902 m_size = obj_load_size(f);
3903
3904
3905 m_addr = create_module(m_name, m_size);
3906 if (m_addr == -1) switch (errno) {
3907 case EEXIST:
3908 bb_error_msg("A module named %s already exists", m_name);
3909 goto out;
3910 case ENOMEM:
3911 bb_error_msg("Can't allocate kernel memory for module; needed %lu bytes",
3912 m_size);
3913 goto out;
3914 default:
3915 bb_perror_msg("create_module: %s", m_name);
3916 goto out;
3917 }
3918
3919#if !LOADBITS
3920 /*
3921 * the PROGBITS section was not loaded by the obj_load
3922 * now we can load them directly into the kernel memory
3923 */
3924 if (!obj_load_progbits(fp, f, (char*)m_addr)) {
3925 delete_module(m_name);
3926 goto out;
3927 }
3928#endif
3929
3930 if (!obj_relocate(f, m_addr)) {
3931 delete_module(m_name);
3932 goto out;
3933 }
3934
3935 if (!new_init_module(m_name, f, m_size))
3936 {
3937 delete_module(m_name);
3938 goto out;
3939 }
3940
3941#ifdef CONFIG_FEATURE_INSMOD_LOAD_MAP
3942 if(flag_print_load_map)
3943 print_load_map(f);
3944#endif
3945
3946 exit_status = EXIT_SUCCESS;
3947
3948out:
3949#ifdef CONFIG_FEATURE_CLEAN_UP
3950 if(fp)
3951 fclose(fp);
3952 if(tmp1) {
3953 free(tmp1);
3954 } else {
3955 free(m_name);
3956 }
3957 free(m_filename);
3958#endif
3959 return(exit_status);
3960}
3961
3962
3963#endif
3964
3965
3966#ifdef CONFIG_FEATURE_2_6_MODULES
3967
3968#include <sys/mman.h>
3969#include <asm/unistd.h>
3970#include <sys/syscall.h>
3971
3972/* We use error numbers in a loose translation... */
3973static const char *moderror(int err)
3974{
3975 switch (err) {
3976 case ENOEXEC:
3977 return "Invalid module format";
3978 case ENOENT:
3979 return "Unknown symbol in module";
3980 case ESRCH:
3981 return "Module has wrong symbol version";
3982 case EINVAL:
3983 return "Invalid parameters";
3984 default:
3985 return strerror(err);
3986 }
3987}
3988
3989extern int insmod_ng_main( int argc, char **argv)
3990{
3991 int i;
3992 int fd;
3993 long int ret;
3994 struct stat st;
3995 unsigned long len;
3996 void *map;
3997 char *filename, *options = bb_xstrdup("");
3998
3999 filename = argv[1];
4000 if (!filename) {
4001 bb_show_usage();
4002 return -1;
4003 }
4004
4005 /* Rest is options */
4006 for (i = 2; i < argc; i++) {
4007 options = xrealloc(options, strlen(options) + 2 + strlen(argv[i]) + 2);
4008 /* Spaces handled by "" pairs, but no way of escaping quotes */
4009 if (strchr(argv[i], ' ')) {
4010 strcat(options, "\"");
4011 strcat(options, argv[i]);
4012 strcat(options, "\"");
4013 } else {
4014 strcat(options, argv[i]);
4015 }
4016 strcat(options, " ");
4017 }
4018
4019 if ((fd = open(filename, O_RDONLY, 0)) < 0) {
4020 bb_perror_msg_and_die("cannot open module `%s'", filename);
4021 }
4022
4023 fstat(fd, &st);
4024 len = st.st_size;
4025 map = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
4026 if (map == MAP_FAILED) {
4027 bb_perror_msg_and_die("cannot mmap `%s'", filename);
4028 }
4029
4030 ret = syscall(__NR_init_module, map, len, options);
4031 if (ret != 0) {
4032 bb_perror_msg_and_die("cannot insert `%s': %s (%li)",
4033 filename, moderror(errno), ret);
4034 }
4035
4036 return 0;
4037}
4038
4039#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini lsmod implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Modified by Alcove, Julien Gaulmin <julien.gaulmin@alcove.fr> and
8 * Nicolas Ferre <nicolas.ferre@alcove.fr> to support pre 2.1 kernels
9 * (which lack the query_module() interface).
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27#include <stdlib.h>
28#include <stdio.h>
29#include <string.h>
30#include <stddef.h>
31#include <errno.h>
32#include <unistd.h>
33#include <dirent.h>
34#include <ctype.h>
35#include <assert.h>
36#include <getopt.h>
37#include <sys/utsname.h>
38#include <sys/file.h>
39#include "busybox.h"
40
41
42#ifndef CONFIG_FEATURE_CHECK_TAINTED_MODULE
43static inline void check_tainted(void) { printf("\n"); }
44#else
45#define TAINT_FILENAME "/proc/sys/kernel/tainted"
46#define TAINT_PROPRIETORY_MODULE (1<<0)
47#define TAINT_FORCED_MODULE (1<<1)
48#define TAINT_UNSAFE_SMP (1<<2)
49
50static void check_tainted(void)
51{
52 int tainted;
53 FILE *f;
54
55 tainted = 0;
56 if ((f = fopen(TAINT_FILENAME, "r"))) {
57 fscanf(f, "%d", &tainted);
58 fclose(f);
59 }
60 if (f && tainted) {
61 printf(" Tainted: %c%c%c\n",
62 tainted & TAINT_PROPRIETORY_MODULE ? 'P' : 'G',
63 tainted & TAINT_FORCED_MODULE ? 'F' : ' ',
64 tainted & TAINT_UNSAFE_SMP ? 'S' : ' ');
65 }
66 else {
67 printf(" Not tainted\n");
68 }
69}
70#endif
71
72#ifdef CONFIG_FEATURE_QUERY_MODULE_INTERFACE
73
74struct module_info
75{
76 unsigned long addr;
77 unsigned long size;
78 unsigned long flags;
79 long usecount;
80};
81
82
83int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret);
84
85/* Values for query_module's which. */
86static const int QM_MODULES = 1;
87static const int QM_DEPS = 2;
88static const int QM_REFS = 3;
89static const int QM_SYMBOLS = 4;
90static const int QM_INFO = 5;
91
92/* Bits of module.flags. */
93static const int NEW_MOD_RUNNING = 1;
94static const int NEW_MOD_DELETED = 2;
95static const int NEW_MOD_AUTOCLEAN = 4;
96static const int NEW_MOD_VISITED = 8;
97static const int NEW_MOD_USED_ONCE = 16;
98static const int NEW_MOD_INITIALIZING = 64;
99
100extern int lsmod_main(int argc, char **argv)
101{
102 struct module_info info;
103 char *module_names, *mn, *deps, *dn;
104 size_t bufsize, depsize, nmod, count, i, j;
105
106 module_names = xmalloc(bufsize = 256);
107 if (my_query_module(NULL, QM_MODULES, (void **)&module_names, &bufsize,
108 &nmod)) {
109 bb_perror_msg_and_die("QM_MODULES");
110 }
111
112 deps = xmalloc(depsize = 256);
113 printf("Module Size Used by");
114 check_tainted();
115
116 for (i = 0, mn = module_names; i < nmod; mn += strlen(mn) + 1, i++) {
117 if (query_module(mn, QM_INFO, &info, sizeof(info), &count)) {
118 if (errno == ENOENT) {
119 /* The module was removed out from underneath us. */
120 continue;
121 }
122 /* else choke */
123 bb_perror_msg_and_die("module %s: QM_INFO", mn);
124 }
125 if (my_query_module(mn, QM_REFS, (void **)&deps, &depsize, &count)) {
126 if (errno == ENOENT) {
127 /* The module was removed out from underneath us. */
128 continue;
129 }
130 bb_perror_msg_and_die("module %s: QM_REFS", mn);
131 }
132 printf("%-20s%8lu%4ld", mn, info.size, info.usecount);
133 if (info.flags & NEW_MOD_DELETED)
134 printf(" (deleted)");
135 else if (info.flags & NEW_MOD_INITIALIZING)
136 printf(" (initializing)");
137 else if (!(info.flags & NEW_MOD_RUNNING))
138 printf(" (uninitialized)");
139 else {
140 if (info.flags & NEW_MOD_AUTOCLEAN)
141 printf(" (autoclean) ");
142 if (!(info.flags & NEW_MOD_USED_ONCE))
143 printf(" (unused)");
144 }
145 if (count) printf(" [");
146 for (j = 0, dn = deps; j < count; dn += strlen(dn) + 1, j++) {
147 printf("%s%s", dn, (j==count-1)? "":" ");
148 }
149 if (count) printf("]");
150
151 printf("\n");
152 }
153
154#ifdef CONFIG_FEATURE_CLEAN_UP
155 free(module_names);
156#endif
157
158 return( 0);
159}
160
161#else /* CONFIG_FEATURE_QUERY_MODULE_INTERFACE */
162
163extern int lsmod_main(int argc, char **argv)
164{
165 printf("Module Size Used by");
166 check_tainted();
167
168 if (bb_xprint_file_by_name("/proc/modules") < 0) {
169 return 0;
170 }
171 return 1;
172}
173
174#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Modprobe written from scratch for BusyBox
4 *
5 * Copyright (c) 2002 by Robert Griebl, griebl@gmx.de
6 * Copyright (c) 2003 by Andrew Dennison, andrew.dennison@motec.com.au
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22*/
23
24#include <sys/utsname.h>
25#include <getopt.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <syslog.h>
29#include <string.h>
30#include <ctype.h>
31#include <fcntl.h>
32#include "busybox.h"
33
34
35
36struct dep_t {
37 char * m_name;
38 char * m_path;
39 char * m_options;
40
41 int m_isalias : 1;
42 int m_reserved : 15;
43
44 int m_depcnt : 16;
45 char ** m_deparr;
46
47 struct dep_t * m_next;
48};
49
50struct mod_list_t {
51 char * m_name;
52 char * m_path;
53 char * m_options;
54
55 struct mod_list_t * m_prev;
56 struct mod_list_t * m_next;
57};
58
59
60static struct dep_t *depend;
61static int autoclean, show_only, quiet, do_syslog, verbose;
62static int k_version;
63
64int parse_tag_value ( char *buffer, char **ptag, char **pvalue )
65{
66 char *tag, *value;
67
68 while ( isspace ( *buffer ))
69 buffer++;
70 tag = value = buffer;
71 while ( !isspace ( *value ))
72 if (!*value) return 0;
73 else value++;
74 *value++ = 0;
75 while ( isspace ( *value ))
76 value++;
77 if (!*value) return 0;
78
79 *ptag = tag;
80 *pvalue = value;
81
82 return 1;
83}
84
85/* Jump through hoops to simulate how fgets() grabs just one line at a
86 * time... Don't use any stdio since modprobe gets called from a kernel
87 * thread and stdio junk can overflow the limited stack...
88 */
89static char *reads ( int fd, char *buffer, size_t len )
90{
91 int n = read ( fd, buffer, len );
92
93 if ( n > 0 ) {
94 char *p;
95
96 buffer [len-1] = 0;
97 p = strchr ( buffer, '\n' );
98
99 if ( p ) {
100 off_t offset;
101
102 offset = lseek ( fd, 0L, SEEK_CUR ); // Get the current file descriptor offset
103 lseek ( fd, offset-n + (p-buffer) + 1, SEEK_SET ); // Set the file descriptor offset to right after the \n
104
105 p[1] = 0;
106 }
107 return buffer;
108 }
109
110 else
111 return 0;
112}
113
114static struct dep_t *build_dep ( void )
115{
116 int fd;
117 struct utsname un;
118 struct dep_t *first = 0;
119 struct dep_t *current = 0;
120 char buffer[2048];
121 char *filename = buffer;
122 int continuation_line = 0;
123
124 k_version = 0;
125 if ( uname ( &un ))
126 return 0;
127
128 // check for buffer overflow in following code
129 if ( bb_strlen ( un.release ) > ( sizeof( buffer ) - 64 )) {
130 return 0;
131 }
132 if (un.release[0] == '2') {
133 k_version = un.release[2] - '0';
134 }
135
136 strcpy ( filename, "/lib/modules/" );
137 strcat ( filename, un.release );
138 strcat ( filename, "/modules.dep" );
139
140 if (( fd = open ( filename, O_RDONLY )) < 0 ) {
141
142 /* Ok, that didn't work. Fall back to looking in /lib/modules */
143 if (( fd = open ( "/lib/modules/modules.dep", O_RDONLY )) < 0 ) {
144 return 0;
145 }
146 }
147
148 while ( reads ( fd, buffer, sizeof( buffer ))) {
149 int l = bb_strlen ( buffer );
150 char *p = 0;
151
152 while ( isspace ( buffer [l-1] )) {
153 buffer [l-1] = 0;
154 l--;
155 }
156
157 if ( l == 0 ) {
158 continuation_line = 0;
159 continue;
160 }
161
162 if ( !continuation_line ) {
163 char *col = strchr ( buffer, ':' );
164 char *dot = col;
165
166 if ( col ) {
167 char *mods;
168 char *modpath;
169 char *mod;
170
171 *col = 0;
172 mods = strrchr ( buffer, '/' );
173
174 if ( !mods )
175 mods = buffer;
176 else
177 mods++;
178
179 modpath = strchr ( buffer, '/' );
180 if ( !modpath )
181 modpath = buffer;
182#if defined(CONFIG_FEATURE_2_6_MODULES)
183 if ((k_version > 4) && ( *(col-3) == '.' ) &&
184 ( *(col-2) == 'k' ) && ( *(col-1) == 'o' ))
185 dot = col - 3;
186 else
187#endif
188 if (( *(col-2) == '.' ) && ( *(col-1) == 'o' ))
189 dot = col - 2;
190
191 mod = bb_xstrndup ( mods, dot - mods );
192
193 if ( !current ) {
194 first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
195 }
196 else {
197 current-> m_next = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
198 current = current-> m_next;
199 }
200 current-> m_name = mod;
201 current-> m_path = bb_xstrdup(modpath);
202 current-> m_options = 0;
203 current-> m_isalias = 0;
204 current-> m_depcnt = 0;
205 current-> m_deparr = 0;
206 current-> m_next = 0;
207
208 //printf ( "%s:\n", mod );
209 p = col + 1;
210 }
211 else
212 p = 0;
213 }
214 else
215 p = buffer;
216
217 while ( p && *p && isblank(*p))
218 p++;
219
220 if ( p && *p ) {
221 char *end = &buffer [l-1];
222 char *deps;
223 char *dep;
224 char *next;
225 int ext = 0;
226
227 while ( isblank ( *end ) || ( *end == '\\' ))
228 end--;
229
230 do
231 {
232 next = strchr (p, ' ' );
233 if (next)
234 {
235 *next = 0;
236 next--;
237 }
238 else
239 next = end;
240
241 deps = strrchr ( p, '/' );
242
243 if ( !deps || ( deps < p )) {
244 deps = p;
245
246 while ( isblank ( *deps ))
247 deps++;
248 }
249 else
250 deps++;
251
252#if defined(CONFIG_FEATURE_2_6_MODULES)
253 if ((k_version > 4) && ( *(next-2) == '.' ) && *(next-1) == 'k' &&
254 ( *next == 'o' ))
255 ext = 3;
256 else
257#endif
258 if (( *(next-1) == '.' ) && ( *next == 'o' ))
259 ext = 2;
260
261 /* Cope with blank lines */
262 if ((next-deps-ext+1) <= 0)
263 continue;
264 dep = bb_xstrndup ( deps, next - deps - ext + 1 );
265
266 current-> m_depcnt++;
267 current-> m_deparr = (char **) xrealloc ( current-> m_deparr,
268 sizeof ( char *) * current-> m_depcnt );
269 current-> m_deparr [current-> m_depcnt - 1] = dep;
270
271 //printf ( " %d) %s\n", current-> m_depcnt, current-> m_deparr [current-> m_depcnt -1] );
272 p = next + 2;
273 } while (next < end);
274 }
275
276 if ( buffer [l-1] == '\\' )
277 continuation_line = 1;
278 else
279 continuation_line = 0;
280 }
281 close ( fd );
282
283 // alias parsing is not 100% correct (no correct handling of continuation lines within an alias) !
284
285#if defined(CONFIG_FEATURE_2_6_MODULES)
286 if (( fd = open ( "/etc/modprobe.conf", O_RDONLY )) < 0 )
287#endif
288 if (( fd = open ( "/etc/modules.conf", O_RDONLY )) < 0 )
289 if (( fd = open ( "/etc/conf.modules", O_RDONLY )) < 0 )
290 return first;
291
292 continuation_line = 0;
293 while ( reads ( fd, buffer, sizeof( buffer ))) {
294 int l;
295 char *p;
296
297 p = strchr ( buffer, '#' );
298 if ( p )
299 *p = 0;
300
301 l = bb_strlen ( buffer );
302
303 while ( l && isspace ( buffer [l-1] )) {
304 buffer [l-1] = 0;
305 l--;
306 }
307
308 if ( l == 0 ) {
309 continuation_line = 0;
310 continue;
311 }
312
313 if ( !continuation_line ) {
314 if (( strncmp ( buffer, "alias", 5 ) == 0 ) && isspace ( buffer [5] )) {
315 char *alias, *mod;
316
317 if ( parse_tag_value ( buffer + 6, &alias, &mod )) {
318 // fprintf ( stderr, "ALIAS: '%s' -> '%s'\n", alias, mod );
319
320 if ( !current ) {
321 first = current = (struct dep_t *) xcalloc ( 1, sizeof ( struct dep_t ));
322 }
323 else {
324 current-> m_next = (struct dep_t *) xcalloc ( 1, sizeof ( struct dep_t ));
325 current = current-> m_next;
326 }
327 current-> m_name = bb_xstrdup ( alias );
328 current-> m_isalias = 1;
329
330 if (( strcmp ( mod, "off" ) == 0 ) || ( strcmp ( mod, "null" ) == 0 )) {
331 current-> m_depcnt = 0;
332 current-> m_deparr = 0;
333 }
334 else {
335 current-> m_depcnt = 1;
336 current-> m_deparr = xmalloc ( 1 * sizeof( char * ));
337 current-> m_deparr[0] = bb_xstrdup ( mod );
338 }
339 current-> m_next = 0;
340 }
341 }
342 else if (( strncmp ( buffer, "options", 7 ) == 0 ) && isspace ( buffer [7] )) {
343 char *mod, *opt;
344
345 if ( parse_tag_value ( buffer + 8, &mod, &opt )) {
346 struct dep_t *dt;
347
348 for ( dt = first; dt; dt = dt-> m_next ) {
349 if ( strcmp ( dt-> m_name, mod ) == 0 )
350 break;
351 }
352 if ( dt ) {
353 dt-> m_options = xrealloc ( dt-> m_options, bb_strlen( opt ) + 1 );
354 strcpy ( dt-> m_options, opt );
355
356 // fprintf ( stderr, "OPTION: '%s' -> '%s'\n", dt-> m_name, dt-> m_options );
357 }
358 }
359 }
360 }
361 }
362 close ( fd );
363
364 return first;
365}
366
367/* return 1 = loaded, 0 = not loaded, -1 = can't tell */
368static int already_loaded (const char *name)
369{
370 int fd;
371 char buffer[4096];
372
373 fd = open ("/proc/modules", O_RDONLY);
374 if (fd < 0)
375 return -1;
376
377 while ( reads ( fd, buffer, sizeof( buffer ))) {
378 char *p;
379
380 p = strchr (buffer, ' ');
381 if (p) {
382 *p = 0;
383 if (strcmp (name, buffer) == 0) {
384 close (fd);
385 return 1;
386 }
387 }
388 }
389
390 close (fd);
391 return 0;
392}
393
394static int mod_process ( struct mod_list_t *list, int do_insert )
395{
396 char lcmd [4096];
397 int rc = 0;
398
399 while ( list ) {
400 *lcmd = '\0';
401 if ( do_insert ) {
402 if (already_loaded (list->m_name) != 1)
403 snprintf ( lcmd, sizeof( lcmd ) - 1, "insmod %s %s %s %s %s",
404 do_syslog ? "-s" : "", autoclean ? "-k" : "",
405 quiet ? "-q" : "", list-> m_path, list-> m_options ?
406 list-> m_options : "" );
407 } else {
408 /* modutils uses short name for removal */
409 if (already_loaded (list->m_name) != 0)
410 snprintf ( lcmd, sizeof( lcmd ) - 1, "rmmod %s %s",
411 do_syslog ? "-s" : "", list-> m_name );
412 }
413
414 if (*lcmd) {
415 if (verbose) {
416 printf("%s\n", lcmd);
417 }
418 if (!show_only) {
419 int rc2 = system(lcmd);
420 if (do_insert) {
421 rc = rc2; /* only last module matters */
422 }
423 else if (!rc2) {
424 rc = 0; /* success if remove any mod */
425 }
426 }
427 }
428 list = do_insert ? list-> m_prev : list-> m_next;
429 }
430 return (show_only) ? 0 : rc;
431}
432
433static void check_dep ( char *mod, struct mod_list_t **head, struct mod_list_t **tail )
434{
435 struct mod_list_t *find;
436 struct dep_t *dt;
437 char *opt = 0;
438 char *path = 0;
439
440 // check dependencies
441 for ( dt = depend; dt; dt = dt-> m_next ) {
442 if ( strcmp ( dt-> m_name, mod ) == 0) {
443 mod = dt-> m_name;
444 path = dt-> m_path;
445 opt = dt-> m_options;
446 break;
447 }
448 }
449
450 // resolve alias names
451 while ( dt && dt-> m_isalias ) {
452 if ( dt-> m_depcnt == 1 ) {
453 struct dep_t *adt;
454
455 for ( adt = depend; adt; adt = adt-> m_next ) {
456 if ( strcmp ( adt-> m_name, dt-> m_deparr [0] ) == 0 )
457 break;
458 }
459 if ( adt ) {
460 dt = adt;
461 mod = dt-> m_name;
462 path = dt-> m_path;
463 if ( !opt )
464 opt = dt-> m_options;
465 }
466 else
467 return;
468 }
469 else
470 return;
471 }
472
473 if ( !path ) {
474 bb_error_msg ("module %s not found.", mod);
475 return;
476 }
477
478 // search for duplicates
479 for ( find = *head; find; find = find-> m_next ) {
480 if ( !strcmp ( mod, find-> m_name )) {
481 // found -> dequeue it
482
483 if ( find-> m_prev )
484 find-> m_prev-> m_next = find-> m_next;
485 else
486 *head = find-> m_next;
487
488 if ( find-> m_next )
489 find-> m_next-> m_prev = find-> m_prev;
490 else
491 *tail = find-> m_prev;
492
493 break; // there can be only one duplicate
494 }
495 }
496
497 if ( !find ) { // did not find a duplicate
498 find = (struct mod_list_t *) xmalloc ( sizeof(struct mod_list_t));
499 find-> m_name = mod;
500 find-> m_path = path;
501 find-> m_options = opt;
502 }
503
504 // enqueue at tail
505 if ( *tail )
506 (*tail)-> m_next = find;
507 find-> m_prev = *tail;
508 find-> m_next = 0;
509
510 if ( !*head )
511 *head = find;
512 *tail = find;
513
514 if ( dt ) {
515 int i;
516
517 for ( i = 0; i < dt-> m_depcnt; i++ )
518 check_dep ( dt-> m_deparr [i], head, tail );
519 }
520}
521
522
523
524static int mod_insert ( char *mod, int argc, char **argv )
525{
526 struct mod_list_t *tail = 0;
527 struct mod_list_t *head = 0;
528 int rc;
529
530 // get dep list for module mod
531 check_dep ( mod, &head, &tail );
532
533 if ( head && tail ) {
534#if defined(CONFIG_FEATURE_2_6_MODULES)
535 if ( argc ) {
536 int i;
537 int l = 0;
538
539 // append module args
540 for ( i = 0; i < argc; i++ )
541 l += ( bb_strlen ( argv [i] ) + 1 );
542
543 head-> m_options = xrealloc ( head-> m_options, l + 1 );
544 head-> m_options [0] = 0;
545
546 for ( i = 0; i < argc; i++ ) {
547 strcat ( head-> m_options, argv [i] );
548 strcat ( head-> m_options, " " );
549 }
550 }
551#endif
552
553 // process tail ---> head
554 rc = mod_process ( tail, 1 );
555 }
556 else
557 rc = 1;
558
559 return rc;
560}
561
562static int mod_remove ( char *mod )
563{
564 int rc;
565 static struct mod_list_t rm_a_dummy = { "-a", 0, 0 };
566
567 struct mod_list_t *head = 0;
568 struct mod_list_t *tail = 0;
569
570 if ( mod )
571 check_dep ( mod, &head, &tail );
572 else // autoclean
573 head = tail = &rm_a_dummy;
574
575 if ( head && tail )
576 rc = mod_process ( head, 0 ); // process head ---> tail
577 else
578 rc = 1;
579 return rc;
580
581}
582
583extern int modprobe_main(int argc, char** argv)
584{
585 int opt;
586 int remove_opt = 0;
587
588 autoclean = show_only = quiet = do_syslog = verbose = 0;
589
590 while ((opt = getopt(argc, argv, "acdklnqrst:vVC:")) != -1) {
591 switch(opt) {
592 case 'c': // no config used
593 case 'l': // no pattern matching
594 return EXIT_SUCCESS;
595 break;
596 case 'C': // no config used
597 case 't': // no pattern matching
598 bb_error_msg_and_die("-t and -C not supported");
599
600 case 'a': // ignore
601 case 'd': // ignore
602 break;
603 case 'k':
604 autoclean++;
605 break;
606 case 'n':
607 show_only++;
608 break;
609 case 'q':
610 quiet++;
611 break;
612 case 'r':
613 remove_opt++;
614 break;
615 case 's':
616 do_syslog++;
617 break;
618 case 'v':
619 verbose++;
620 break;
621 case 'V':
622 default:
623 bb_show_usage();
624 break;
625 }
626 }
627
628 depend = build_dep ( );
629
630 if ( !depend )
631 bb_error_msg_and_die ( "could not parse modules.dep\n" );
632
633 if (remove_opt) {
634 int rc = EXIT_SUCCESS;
635 do {
636 if (mod_remove ( optind < argc ?
637 bb_xstrdup (argv [optind]) : NULL )) {
638 bb_error_msg ("failed to remove module %s",
639 argv [optind] );
640 rc = EXIT_FAILURE;
641 }
642 } while ( ++optind < argc );
643
644 return rc;
645 }
646
647 if (optind >= argc)
648 bb_error_msg_and_die ( "No module or pattern provided\n" );
649
650 if ( mod_insert ( bb_xstrdup ( argv [optind] ), argc - optind - 1, argv + optind + 1 ))
651 bb_error_msg_and_die ( "failed to load module %s", argv [optind] );
652
653 return EXIT_SUCCESS;
654}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini rmmod implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <stdio.h>
24#include <errno.h>
25#include <unistd.h>
26#include <stdlib.h>
27#include <getopt.h>
28#include <fcntl.h>
29#include <string.h>
30#include <sys/syscall.h>
31#include "busybox.h"
32
33#ifdef CONFIG_FEATURE_2_6_MODULES
34static inline void filename2modname(char *modname, const char *filename)
35{
36 const char *afterslash;
37 unsigned int i;
38
39 afterslash = strrchr(filename, '/');
40 if (!afterslash)
41 afterslash = filename;
42 else
43 afterslash++;
44
45 /* Convert to underscores, stop at first . */
46 for (i = 0; afterslash[i] && afterslash[i] != '.'; i++) {
47 if (afterslash[i] == '-')
48 modname[i] = '_';
49 else
50 modname[i] = afterslash[i];
51 }
52 modname[i] = '\0';
53}
54#endif
55
56extern int rmmod_main(int argc, char **argv)
57{
58 int n, ret = EXIT_SUCCESS;
59 size_t nmod = 0; /* number of modules */
60 size_t pnmod = -1; /* previous number of modules */
61 unsigned int flags = O_NONBLOCK|O_EXCL;
62#ifdef CONFIG_FEATURE_QUERY_MODULE_INTERFACE
63 void *buf; /* hold the module names which we ignore but must get */
64 size_t bufsize = 0;
65#endif
66
67 /* Parse command line. */
68 while ((n = getopt(argc, argv, "a")) != EOF) {
69 switch (n) {
70 case 'w': // --wait
71 flags &= ~O_NONBLOCK;
72 break;
73 case 'f': // --force
74 flags |= O_TRUNC;
75 break;
76 case 'a':
77 /* Unload _all_ unused modules via NULL delete_module() call */
78 /* until the number of modules does not change */
79#ifdef CONFIG_FEATURE_QUERY_MODULE_INTERFACE
80 buf = xmalloc(bufsize = 256);
81#endif
82 while (nmod != pnmod) {
83 if (syscall(__NR_delete_module, NULL, flags) < 0) {
84 if (errno==EFAULT)
85 return(ret);
86 bb_perror_msg_and_die("rmmod");
87 }
88 pnmod = nmod;
89#ifdef CONFIG_FEATURE_QUERY_MODULE_INTERFACE
90 /* 1 == QM_MODULES */
91 if (my_query_module(NULL, 1, &buf, &bufsize, &nmod)) {
92 bb_perror_msg_and_die("QM_MODULES");
93 }
94#endif
95 }
96#if defined CONFIG_FEATURE_CLEAN_UP && CONFIG_FEATURE_QUERY_MODULE_INTERFACE
97 free(buf);
98#endif
99 return EXIT_SUCCESS;
100 default:
101 bb_show_usage();
102 }
103 }
104
105 if (optind == argc)
106 bb_show_usage();
107
108 {
109 for (n = optind; n < argc; n++) {
110#ifdef CONFIG_FEATURE_2_6_MODULES
111 char module_name[strlen(argv[n]) + 1];
112 filename2modname(module_name, argv[n]);
113#else
114#define module_name argv[n]
115#endif
116 if (syscall(__NR_delete_module, module_name, flags) < 0) {
117 bb_perror_msg("%s", argv[n]);
118 ret = EXIT_FAILURE;
119 }
120 }
121 }
122
123 return(ret);
124}
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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Networking Utilities"
7
8config CONFIG_FEATURE_IPV6
9 bool "Enable IPv6 support"
10 default n
11 help
12 Enable IPv6 support to busybox. This makes applets that talk IP
13 able to work with IPv6.
14
15config CONFIG_ARPING
16 bool "arping"
17 default n
18 help
19 Ping hosts by ARP packets
20
21config CONFIG_FTPGET
22 bool "ftpget"
23 default n
24 help
25 Retrieve a remote file via FTP.
26
27config CONFIG_FTPPUT
28 bool "ftpput"
29 default n
30 help
31 Store a remote file via FTP.
32
33config CONFIG_HOSTNAME
34 bool "hostname"
35 default n
36 help
37 Show or set the system's host name
38
39config CONFIG_HTTPD
40 bool "httpd"
41 default n
42 help
43 Serve web pages via an HTTP server.
44
45config CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
46 bool " Support using httpd only from inetd"
47 default n
48 depends on CONFIG_HTTPD
49 help
50 This option disables uid and port options for the httpd applet
51 but requires inetd server daemon.
52
53config CONFIG_FEATURE_HTTPD_BASIC_AUTH
54 bool " Enable Basic http Authentication"
55 default y
56 depends on CONFIG_HTTPD
57 help
58 Utilizes password settings from /etc/httpd.conf for basic
59 authentication on a per url basis.
60
61config CONFIG_FEATURE_HTTPD_AUTH_MD5
62 bool " Support MD5 crypted passwords for http Authentication"
63 default n
64 depends on CONFIG_FEATURE_HTTPD_BASIC_AUTH
65 help
66 Enables basic per url authentication from /etc/httpd.conf
67 using md5 passwords.
68
69
70if !CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
71config CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
72 bool " Support reloading the global config file using hup signal"
73 default n
74 depends on CONFIG_HTTPD
75 help
76 This option enables processing of SIGHUP to reload cached
77 configuration settings.
78
79config CONFIG_FEATURE_HTTPD_SETUID
80 bool " Enable support -u <user> option"
81 default n
82 depends on CONFIG_HTTPD
83 help
84 This option allows the server to run as a specific user
85 rather than defaulting to the user that starts the server.
86 Use of this option requires special privileges to change to a
87 different user.
88endif
89
90config CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
91 bool " Support loading additional MIME types at run-time"
92 default n
93 depends on CONFIG_HTTPD
94 help
95 This option enables support for additional MIME types at
96 run-time to be specified in the configuration file.
97
98config CONFIG_FEATURE_HTTPD_CGI
99 bool " Support Common Gateway Interface (CGI)"
100 default y
101 depends on CONFIG_HTTPD
102 help
103 This option allows scripts and executables to be invoked
104 when specific urls are requested.
105
106config CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
107 bool " Support the REMOTE_PORT environment variable for CGI"
108 default n
109 depends on CONFIG_FEATURE_HTTPD_CGI
110 help
111 Use of this option can assist scripts in generating
112 references that contain a unique port number.
113
114config CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
115 bool " Enable the -e option for shell script CGI simplification."
116 default y
117 depends on CONFIG_HTTPD
118 help
119 After set, this option allows html encoding arbitrary
120 strings for display of the browser. Output goes to stdout.
121 For example, httpd -e "<Hello World>" as
122 "&#60Hello&#32World&#62".
123
124config CONFIG_IFCONFIG
125 bool "ifconfig"
126 default n
127 help
128 Ifconfig is used to configure the kernel-resident network interfaces.
129
130config CONFIG_FEATURE_IFCONFIG_STATUS
131 bool " Enable status reporting output (+7k)"
132 default y
133 depends on CONFIG_IFCONFIG
134 help
135 If ifconfig is called with no arguments it will display the status
136 of the currently active interfaces.
137
138config CONFIG_FEATURE_IFCONFIG_SLIP
139 bool " Enable slip-specific options \"keepalive\" and \"outfill\""
140 default n
141 depends on CONFIG_IFCONFIG
142 help
143 Allow "keepalive" and "outfill" support for SLIP. If you're not
144 planning on using serial lines, leave this unchecked.
145
146config CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
147 bool " Enable options \"mem_start\", \"io_addr\", and \"irq\""
148 default n
149 depends on CONFIG_IFCONFIG
150 help
151 Allow the start address for shared memory, start address for I/O,
152 and/or the interrupt line used by the specified device.
153
154config CONFIG_FEATURE_IFCONFIG_HW
155 bool " Enable option \"hw\" (ether only)"
156 default y
157 depends on CONFIG_IFCONFIG
158 help
159 Set the hardware address of this interface, if the device driver
160 supports this operation. Currently, we only support the 'ether'
161 class.
162
163config CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
164 bool " Set the broadcast automatically"
165 default n
166 depends on CONFIG_IFCONFIG
167 help
168 Setting this will make ifconfig attempt to find the broadcast
169 automatically if the value '+' is used.
170
171config CONFIG_IFUPDOWN
172 bool "ifupdown"
173 default n
174 help
175 Activate or deactivate the specified interfaces. This applet makes
176 use of either "ifconfig" and "route" or the "ip" command to actually
177 configure network interfaces. Therefore, you will probably also want
178 to enable either CONFIG_IFCONFIG and CONFIG_ROUTE, or enable
179 CONFIG_FEATURE_IFUPDOWN_IP and the various CONFIG_IP options. Of
180 course you could use non-busybox versions of these programs, so
181 against my better judgement (since this will surely result in plenty
182 of support questions on the mailing list), I do not force you to
183 enable these additional options. It is up to you to supply either
184 "ifconfig" and "route" or the "ip" command, either via busybox or via
185 standalone utilities.
186
187config CONFIG_FEATURE_IFUPDOWN_IP
188 bool " Use ip applet"
189 default n
190 depends on CONFIG_IFUPDOWN
191 help
192 Use the iproute "ip" command to implement "ifup" and "ifdown", rather
193 than the default of using the older 'ifconfig' and 'route' utilities.
194
195config CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN
196 bool " Use busybox ip applet"
197 default y
198 depends on CONFIG_FEATURE_IFUPDOWN_IP
199 select CONFIG_IP
200 select CONFIG_FEATURE_IP_ADDRESS
201 select CONFIG_FEATURE_IP_LINK
202 select CONFIG_FEATURE_IP_ROUTE
203 help
204 Use the busybox iproute "ip" applet to implement "ifupdown".
205
206 If leave this disabled, you must install the full-blown iproute2
207 utility or the "ifup" and "ifdown" applets will not work.
208
209config CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN
210 bool " Use busybox ifconfig and route applets"
211 default y
212 depends on CONFIG_IFUPDOWN && !CONFIG_FEATURE_IFUPDOWN_IP
213 select CONFIG_IFCONFIG
214 select CONFIG_ROUTE
215 help
216 Use the busybox iproute "ifconfig" and "route" applets to
217 implement the "ifup" and "ifdown" utilities.
218
219 If leave this disabled, you must install the full-blown ifconfig
220 and route utilities, or the "ifup" and "ifdown" applets will not
221 work.
222
223config CONFIG_FEATURE_IFUPDOWN_IPV4
224 bool " Enable support for IPv4"
225 default y
226 depends on CONFIG_IFUPDOWN
227 help
228 If you want busybox to talk IPv4, leave this on.
229
230config CONFIG_FEATURE_IFUPDOWN_IPV6
231 bool " Enable support for IPv6"
232 default n
233 depends on CONFIG_IFUPDOWN
234 help
235 If you need support for IPv6, turn this option on.
236
237config CONFIG_FEATURE_IFUPDOWN_IPX
238 bool " Enable support for IPX"
239 default n
240 depends on CONFIG_IFUPDOWN
241 help
242 If this option is selected you can use busybox to work with IPX
243 networks.
244
245config CONFIG_FEATURE_IFUPDOWN_MAPPING
246 bool " Enable mapping support"
247 default n
248 depends on CONFIG_IFUPDOWN
249 help
250 This enables support for the "mapping" stanza, unless you have
251 a weird network setup you don't need it.
252
253config CONFIG_INETD
254 bool "inetd"
255 default n
256 help
257 Internet superserver daemon
258
259config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
260 bool " Support echo service"
261 default y
262 depends on CONFIG_INETD
263 help
264 Echo received data internal inetd service
265
266config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
267 bool " Support discard service"
268 default y
269 depends on CONFIG_INETD
270 help
271 Internet /dev/null internal inetd service
272
273config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
274 bool " Support time service"
275 default y
276 depends on CONFIG_INETD
277 help
278 Return 32 bit time since 1900 internal inetd service
279
280config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
281 bool " Support daytime service"
282 default y
283 depends on CONFIG_INETD
284 help
285 Return human-readable time internal inetd service
286
287config CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
288 bool " Support chargen service"
289 default y
290 depends on CONFIG_INETD
291 help
292 Familiar character generator internal inetd service
293
294
295config CONFIG_IP
296 bool "ip"
297 default n
298 help
299 The "ip" applet is a TCP/IP interface configuration and routing
300 utility. You generally don't need "ip" to use busybox with
301 TCP/IP.
302
303if CONFIG_IP && CONFIG_IPADDR
304 config CONFIG_FEATURE_IP_ADDRESS
305 default y
306 comment " address (forced enabled for ipaddr)"
307endif
308if ! (CONFIG_IP && CONFIG_IPADDR)
309 config CONFIG_FEATURE_IP_ADDRESS
310 bool " address"
311 default y
312 depends on CONFIG_IP
313 help
314 Address manipulation support for the "ip" applet.
315endif
316
317if CONFIG_IP && CONFIG_IPLINK
318 config CONFIG_FEATURE_IP_LINK
319 default y
320 comment " link (forced enabled for iplink)"
321endif
322if !(CONFIG_IP && CONFIG_IPLINK)
323 config CONFIG_FEATURE_IP_LINK
324 bool " link"
325 default y
326 depends on CONFIG_IP
327 help
328 Configure network devices with "ip".
329endif
330
331if CONFIG_IP && CONFIG_IPROUTE
332 config CONFIG_FEATURE_IP_ROUTE
333 default y
334 comment " route (forced enabled for iproute)"
335endif
336if !(CONFIG_IP && CONFIG_IPROUTE)
337 config CONFIG_FEATURE_IP_ROUTE
338 bool " route"
339 default y
340 depends on CONFIG_IP
341 help
342 Add support for routing table management to "ip".
343endif
344
345if CONFIG_IP && CONFIG_IPTUNNEL
346 config CONFIG_FEATURE_IP_TUNNEL
347 default y
348 comment " tunnel (forced enabled for iptunnel)"
349endif
350if !(CONFIG_IP && CONFIG_IPTUNNEL)
351 config CONFIG_FEATURE_IP_TUNNEL
352 bool " tunnel"
353 default n
354 depends on CONFIG_IP
355 help
356 Add support for tunneling commands to "ip".
357endif
358
359config CONFIG_IPCALC
360 bool "ipcalc"
361 default n
362 help
363 ipcalc takes an IP address and netmask and calculates the
364 resulting broadcast, network, and host range.
365
366config CONFIG_FEATURE_IPCALC_FANCY
367 bool " Fancy IPCALC, more options, adds 1 kbyte"
368 default y
369 depends on CONFIG_IPCALC
370 help
371 Adds the options hostname, prefix and silent to the output of "ipcalc".
372
373config CONFIG_IPADDR
374 bool "ipaddr"
375 default n
376 help
377 Equivalent to selecting address support to "ip", above.
378
379config CONFIG_IPLINK
380 bool "iplink"
381 default n
382 help
383 Equivalent to selecting link support to "ip", above.
384
385config CONFIG_IPROUTE
386 bool "iproute"
387 default n
388 help
389 Equivalent to selecting route support to "ip", above.
390
391config CONFIG_IPTUNNEL
392 bool "iptunnel"
393 default n
394 help
395 Equivalent to selecting tunnel support to "ip", above.
396
397config CONFIG_NAMEIF
398 bool "nameif"
399 default n
400 help
401 nameif is used to rename network interface by its MAC address.
402 Renamed interfaces MUST be in the down state.
403 It is possible to use a file (default: /etc/mactab)
404 with list of new interface names and MACs.
405 Maximum interface name length: IF_NAMESIZE = 16
406 File fields are separated by space or tab.
407 File format:
408 # Comment
409 new_interface_name XX:XX:XX:XX:XX:XX
410
411config CONFIG_NC
412 bool "nc"
413 default n
414 help
415 A simple Unix utility which reads and writes data across network
416 connections.
417
418config CONFIG_NETSTAT
419 bool "netstat"
420 default n
421 help
422 netstat prints information about the Linux networking subsystem.
423
424config CONFIG_NSLOOKUP
425 bool "nslookup"
426 default n
427 help
428 nslookup is a tool to query Internet name servers.
429
430config CONFIG_PING
431 bool "ping"
432 default n
433 help
434 ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to
435 elicit an ICMP ECHO_RESPONSE from a host or gateway.
436
437config CONFIG_FEATURE_FANCY_PING
438 bool " Enable fancy ping output"
439 default y
440 depends on CONFIG_PING
441 help
442 Make the output from the ping applet include statistics, and at the
443 same time provide full support for ICMP packets.
444
445config CONFIG_PING6
446 bool "ping6"
447 default n
448 depends on CONFIG_FEATURE_IPV6
449 help
450 This will give you a ping that can talk IPv6.
451
452config CONFIG_FEATURE_FANCY_PING6
453 bool " Enable fancy ping6 output"
454 default y
455 depends on CONFIG_PING6
456 help
457 Make the output from the ping6 applet include statistics, and at the
458 same time provide full support for ICMP packets.
459
460config CONFIG_ROUTE
461 bool "route"
462 default n
463 help
464 Route displays or manipulates the kernel's IP routing tables.
465
466config CONFIG_TELNET
467 bool "telnet"
468 default n
469 help
470 Telnet is an interface to the TELNET protocol, but is also commonly
471 used to test other simple protocols.
472
473config CONFIG_FEATURE_TELNET_TTYPE
474 bool " Pass TERM type to remote host"
475 default y
476 depends on CONFIG_TELNET
477 help
478 Setting this option will forward the TERM environment variable to the
479 remote host you are connecting to. This is useful to make sure that
480 things like ANSI colors and other control sequences behave.
481
482config CONFIG_FEATURE_TELNET_AUTOLOGIN
483 bool " Pass USER type to remote host"
484 default y
485 depends on CONFIG_TELNET
486 help
487 Setting this option will forward the USER environment variable to the
488 remote host you are connecting to. This is useful when you need to
489 log into a machine without telling the username (autologin). This
490 option enables `-a' and `-l USER' arguments.
491
492config CONFIG_TELNETD
493 bool "telnetd"
494 default n
495 select CONFIG_LOGIN
496 help
497 A daemon for the TELNET protocol, allowing you to log onto the host
498 running the daemon. Please keep in mind that the TELNET protocol
499 sends passwords in plain text. If you can't afford the space for an
500 SSH daemon and you trust your network, you may say 'y' here. As a
501 more secure alternative, you should seriously consider installing the
502 very small Dropbear SSH daemon instead:
503 http://matt.ucc.asn.au/dropbear/dropbear.html
504
505 Note that for busybox telnetd to work you need several things:
506 First of all, your kernel needs:
507 CONFIG_UNIX98_PTYS=y
508 CONFIG_DEVPTS_FS=y
509
510 Next, you need a /dev/pts directory on your root filesystem:
511
512 $ ls -ld /dev/pts
513 drwxr-xr-x 2 root root 0 Sep 23 13:21 /dev/pts/
514
515 Next you need the pseudo terminal master multiplexer /dev/ptmx:
516
517 $ ls -la /dev/ptmx
518 crw-rw-rw- 1 root tty 5, 2 Sep 23 13:55 /dev/ptmx
519
520 Any /dev/ttyp[0-9]* files you may have can be removed.
521 Next, you need to mount the devpts filesystem on /dev/pts using:
522
523 mount -t devpts devpts /dev/pts
524
525 You need to be sure that Busybox has CONFIG_LOGIN and
526 CONFIG_FEATURE_SUID enabled. And finally, you should make
527 certain that Busybox has been installed setuid root:
528
529 chown root.root /bin/busybox
530 chmod 4755 /bin/busybox
531
532 with all that done, telnetd _should_ work....
533
534
535config CONFIG_FEATURE_TELNETD_INETD
536 bool " Support call from inetd only"
537 default n
538 depends on CONFIG_TELNETD
539 help
540 Selecting this will make telnetd only callable from inetd,
541 removing the standalone support.
542
543config CONFIG_TFTP
544 bool "tftp"
545 default n
546 help
547 This enables the Trivial File Transfer Protocol client program. TFTP
548 is usually used for simple, small transfers such as a root image
549 for a network-enabled bootloader.
550
551config CONFIG_FEATURE_TFTP_GET
552 bool " Enable \"get\" command"
553 default y
554 depends on CONFIG_TFTP
555 help
556 Add support for the GET command within the TFTP client. This allows
557 a client to retrieve a file from a TFTP server.
558
559config CONFIG_FEATURE_TFTP_PUT
560 bool " Enable \"put\" command"
561 default y
562 depends on CONFIG_TFTP
563 help
564 Add support for the PUT command within the TFTP client. This allows
565 a client to transfer a file to a TFTP server.
566
567config CONFIG_FEATURE_TFTP_BLOCKSIZE
568 bool " Enable \"blocksize\" command"
569 default n
570 depends on CONFIG_TFTP
571 help
572 Allow the client to specify the desired block size for transfers.
573
574config CONFIG_FEATURE_TFTP_DEBUG
575 bool " Enable debug"
576 default n
577 depends on CONFIG_TFTP
578 help
579 Enable debug settings for tftp. This is useful if you're running
580 into problems with tftp as the protocol doesn't help you much when
581 you run into problems.
582
583config CONFIG_TRACEROUTE
584 bool "traceroute"
585 default n
586 help
587 Utility to trace the route of IP packets
588
589config CONFIG_FEATURE_TRACEROUTE_VERBOSE
590 bool " Enable verbose output"
591 default n
592 depends on CONFIG_TRACEROUTE
593 help
594 Add some verbosity to traceroute. This includes amongst other things
595 hostnames and ICMP response types.
596
597config CONFIG_VCONFIG
598 bool "vconfig"
599 default n
600 help
601 Creates, removes, and configures VLAN interfaces
602
603config CONFIG_WGET
604 bool "wget"
605 default n
606 help
607 wget is a utility for non-interactive download of files from HTTP,
608 HTTPS, and FTP servers.
609
610config CONFIG_FEATURE_WGET_STATUSBAR
611 bool " Enable a nifty process meter (+2k)"
612 default y
613 depends on CONFIG_WGET
614 help
615 Enable the transfer progress bar for wget transfers.
616
617config CONFIG_FEATURE_WGET_AUTHENTICATION
618 bool " Enable HTTP authentication"
619 default y
620 depends on CONFIG_WGET
621 help
622 Support authenticated HTTP transfers.
623
624config CONFIG_FEATURE_WGET_IP6_LITERAL
625 bool " Enable IPv6 literal addresses"
626 default y
627 depends on CONFIG_WGET
628 help
629 Support IPv6 address literal notation in URLs.
630
631source networking/udhcp/Config.in
632
633endmenu
634
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/networking
23NETWORKING_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20NETWORKING_AR:=networking.a
21ifndef $(NETWORKING_DIR)
22NETWORKING_DIR:=$(top_builddir)/networking/
23endif
24srcdir=$(top_srcdir)/networking
25NETWORKING-y:=
26NETWORKING-$(CONFIG_ARPING) += arping.o
27NETWORKING-$(CONFIG_FTPGET) += ftpgetput.o
28NETWORKING-$(CONFIG_FTPPUT) += ftpgetput.o
29NETWORKING-$(CONFIG_HOSTNAME) += hostname.o
30NETWORKING-$(CONFIG_HTTPD) += httpd.o
31NETWORKING-$(CONFIG_IFCONFIG) += ifconfig.o
32NETWORKING-$(CONFIG_IFUPDOWN) += ifupdown.o
33NETWORKING-$(CONFIG_INETD) += inetd.o
34NETWORKING-$(CONFIG_IP) += ip.o
35NETWORKING-$(CONFIG_IPCALC) += ipcalc.o
36NETWORKING-$(CONFIG_IPADDR) += ipaddr.o
37NETWORKING-$(CONFIG_IPLINK) += iplink.o
38NETWORKING-$(CONFIG_IPROUTE) += iproute.o
39NETWORKING-$(CONFIG_IPTUNNEL) += iptunnel.o
40NETWORKING-$(CONFIG_NAMEIF) += nameif.o
41NETWORKING-$(CONFIG_NC) += nc.o
42NETWORKING-$(CONFIG_NETSTAT) += netstat.o
43NETWORKING-$(CONFIG_NSLOOKUP) += nslookup.o
44NETWORKING-$(CONFIG_PING) += ping.o
45NETWORKING-$(CONFIG_PING6) += ping6.o
46NETWORKING-$(CONFIG_ROUTE) += route.o
47NETWORKING-$(CONFIG_TELNET) += telnet.o
48NETWORKING-$(CONFIG_TELNETD) += telnetd.o
49NETWORKING-$(CONFIG_TFTP) += tftp.o
50NETWORKING-$(CONFIG_TRACEROUTE) += traceroute.o
51NETWORKING-$(CONFIG_VCONFIG) += vconfig.o
52NETWORKING-$(CONFIG_WGET) += wget.o
53
54libraries-y+=$(NETWORKING_DIR)$(NETWORKING_AR)
55
56needcrypt-y:=
57needcrypt-$(CONFIG_FEATURE_HTTPD_AUTH_MD5) := y
58
59ifeq ($(needcrypt-y),y)
60 LIBRARIES += -lcrypt
61endif
62
63$(NETWORKING_DIR)$(NETWORKING_AR): $(patsubst %,$(NETWORKING_DIR)%, $(NETWORKING-y))
64 $(AR) -ro $@ $(patsubst %,$(NETWORKING_DIR)%, $(NETWORKING-y))
65
66$(NETWORKING_DIR)%.o: $(srcdir)/%.c
67 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
68
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 @@
1/*
2 * arping.c - Ping hosts by ARP requests/replies
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Author: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
10 * Busybox port: Nick Fedchik <nick@fedchik.org.ua>
11 */
12
13#include <sys/ioctl.h>
14#include <sys/signal.h>
15#include <sys/time.h>
16
17#include <errno.h>
18#include <stdlib.h>
19#include <string.h>
20#include <unistd.h>
21
22#include <arpa/inet.h>
23#include <net/if.h>
24#include <netinet/ether.h>
25#include <netpacket/packet.h>
26
27#include "busybox.h"
28
29#define APPLET_NAME "arping"
30
31static struct in_addr src;
32static struct in_addr dst;
33static struct sockaddr_ll me;
34static struct sockaddr_ll he;
35static struct timeval last;
36static int dad;
37static int unsolicited;
38static int advert;
39static int quiet;
40static int quit_on_reply = 0;
41static int count = -1;
42static int timeout;
43static int unicasting;
44static int s;
45static int broadcast_only;
46static int sent;
47static int brd_sent;
48static int received;
49static int brd_recv;
50static int req_recv;
51
52#define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \
53 ((tv1).tv_usec-(tv2).tv_usec)/1000 )
54#if 0
55static void set_signal(int signo, void (*handler) (void))
56{
57 struct sigaction sa;
58
59 memset(&sa, 0, sizeof(sa));
60 sa.sa_handler = (void (*)(int)) handler;
61 sa.sa_flags = SA_RESTART;
62 sigaction(signo, &sa, NULL);
63}
64#endif
65
66static int send_pack(int sock, struct in_addr *src_addr,
67 struct in_addr *dst_addr, struct sockaddr_ll *ME,
68 struct sockaddr_ll *HE)
69{
70 int err;
71 struct timeval now;
72 unsigned char buf[256];
73 struct arphdr *ah = (struct arphdr *) buf;
74 unsigned char *p = (unsigned char *) (ah + 1);
75
76 ah->ar_hrd = htons(ME->sll_hatype);
77 ah->ar_hrd = htons(ARPHRD_ETHER);
78 ah->ar_pro = htons(ETH_P_IP);
79 ah->ar_hln = ME->sll_halen;
80 ah->ar_pln = 4;
81 ah->ar_op = advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST);
82
83 memcpy(p, &ME->sll_addr, ah->ar_hln);
84 p += ME->sll_halen;
85
86 memcpy(p, src_addr, 4);
87 p += 4;
88
89 if (advert)
90 memcpy(p, &ME->sll_addr, ah->ar_hln);
91 else
92 memcpy(p, &HE->sll_addr, ah->ar_hln);
93 p += ah->ar_hln;
94
95 memcpy(p, dst_addr, 4);
96 p += 4;
97
98 gettimeofday(&now, NULL);
99 err = sendto(sock, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE));
100 if (err == p - buf) {
101 last = now;
102 sent++;
103 if (!unicasting)
104 brd_sent++;
105 }
106 return err;
107}
108
109void finish(void)
110{
111 if (!quiet) {
112 printf("Sent %d probes (%d broadcast(s))\n", sent, brd_sent);
113 printf("Received %d repl%s", received, (received > 1) ? "ies" : "y");
114 if (brd_recv || req_recv) {
115 printf(" (");
116 if (req_recv)
117 printf("%d request(s)", req_recv);
118 if (brd_recv)
119 printf("%s%d broadcast(s)", req_recv ? ", " : "", brd_recv);
120 putchar(')');
121 }
122 putchar('\n');
123 fflush(stdout);
124 }
125 if (dad)
126 exit(!!received);
127 if (unsolicited)
128 exit(0);
129 exit(!received);
130}
131
132void catcher(void)
133{
134 struct timeval tv;
135 static struct timeval start;
136
137 gettimeofday(&tv, NULL);
138
139 if (start.tv_sec == 0)
140 start = tv;
141
142 if (count-- == 0
143 || (timeout && MS_TDIFF(tv, start) > timeout * 1000 + 500))
144 finish();
145
146 if (last.tv_sec == 0 || MS_TDIFF(tv, last) > 500) {
147 send_pack(s, &src, &dst, &me, &he);
148 if (count == 0 && unsolicited)
149 finish();
150 }
151 alarm(1);
152}
153
154int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
155{
156 struct timeval tv;
157 struct arphdr *ah = (struct arphdr *) buf;
158 unsigned char *p = (unsigned char *) (ah + 1);
159 struct in_addr src_ip, dst_ip;
160
161 gettimeofday(&tv, NULL);
162
163 /* Filter out wild packets */
164 if (FROM->sll_pkttype != PACKET_HOST &&
165 FROM->sll_pkttype != PACKET_BROADCAST &&
166 FROM->sll_pkttype != PACKET_MULTICAST)
167 return 0;
168
169 /* Only these types are recognised */
170 if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY))
171 return 0;
172
173 /* ARPHRD check and this darned FDDI hack here :-( */
174 if (ah->ar_hrd != htons(FROM->sll_hatype) &&
175 (FROM->sll_hatype != ARPHRD_FDDI
176 || ah->ar_hrd != htons(ARPHRD_ETHER)))
177 return 0;
178
179 /* Protocol must be IP. */
180 if (ah->ar_pro != htons(ETH_P_IP))
181 return 0;
182 if (ah->ar_pln != 4)
183 return 0;
184 if (ah->ar_hln != me.sll_halen)
185 return 0;
186 if (len < sizeof(*ah) + 2 * (4 + ah->ar_hln))
187 return 0;
188 memcpy(&src_ip, p + ah->ar_hln, 4);
189 memcpy(&dst_ip, p + ah->ar_hln + 4 + ah->ar_hln, 4);
190 if (!dad) {
191 if (src_ip.s_addr != dst.s_addr)
192 return 0;
193 if (src.s_addr != dst_ip.s_addr)
194 return 0;
195 if (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln))
196 return 0;
197 } else {
198 /* DAD packet was:
199 src_ip = 0 (or some src)
200 src_hw = ME
201 dst_ip = tested address
202 dst_hw = <unspec>
203
204 We fail, if receive request/reply with:
205 src_ip = tested_address
206 src_hw != ME
207 if src_ip in request was not zero, check
208 also that it matches to dst_ip, otherwise
209 dst_ip/dst_hw do not matter.
210 */
211 if (src_ip.s_addr != dst.s_addr)
212 return 0;
213 if (memcmp(p, &me.sll_addr, me.sll_halen) == 0)
214 return 0;
215 if (src.s_addr && src.s_addr != dst_ip.s_addr)
216 return 0;
217 }
218 if (!quiet) {
219 int s_printed = 0;
220
221 printf("%s ",
222 FROM->sll_pkttype == PACKET_HOST ? "Unicast" : "Broadcast");
223 printf("%s from ",
224 ah->ar_op == htons(ARPOP_REPLY) ? "reply" : "request");
225 printf("%s ", inet_ntoa(src_ip));
226 printf("[%s]", ether_ntoa((struct ether_addr *) p));
227 if (dst_ip.s_addr != src.s_addr) {
228 printf("for %s ", inet_ntoa(dst_ip));
229 s_printed = 1;
230 }
231 if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) {
232 if (!s_printed)
233 printf("for ");
234 printf("[%s]",
235 ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4));
236 }
237 if (last.tv_sec) {
238 long usecs = (tv.tv_sec - last.tv_sec) * 1000000 +
239 tv.tv_usec - last.tv_usec;
240 long msecs = (usecs + 500) / 1000;
241
242 usecs -= msecs * 1000 - 500;
243 printf(" %ld.%03ldms\n", msecs, usecs);
244 } else {
245 printf(" UNSOLICITED?\n");
246 }
247 fflush(stdout);
248 }
249 received++;
250 if (FROM->sll_pkttype != PACKET_HOST)
251 brd_recv++;
252 if (ah->ar_op == htons(ARPOP_REQUEST))
253 req_recv++;
254 if (quit_on_reply)
255 finish();
256 if (!broadcast_only) {
257 memcpy(he.sll_addr, p, me.sll_halen);
258 unicasting = 1;
259 }
260 return 1;
261}
262
263int arping_main(int argc, char **argv)
264{
265 int socket_errno;
266 int ch;
267 uid_t uid = getuid();
268 char *device = "eth0";
269 int ifindex = 0;
270 char *source = NULL;
271 char *target;
272
273 s = socket(PF_PACKET, SOCK_DGRAM, 0);
274 socket_errno = errno;
275
276 setuid(uid);
277
278 while ((ch = getopt(argc, argv, "h?bfDUAqc:w:s:I:")) != EOF) {
279 switch (ch) {
280 case 'b':
281 broadcast_only = 1;
282 break;
283 case 'D':
284 dad++;
285 quit_on_reply = 1;
286 break;
287 case 'U':
288 unsolicited++;
289 break;
290 case 'A':
291 advert++;
292 unsolicited++;
293 break;
294 case 'q':
295 quiet++;
296 break;
297 case 'c':
298 count = atoi(optarg);
299 break;
300 case 'w':
301 timeout = atoi(optarg);
302 break;
303 case 'I':
304 if (optarg == NULL)
305 bb_show_usage();
306 if (bb_strlen(optarg) > IF_NAMESIZE) {
307 bb_error_msg("Interface name `%s' must be less than %d", optarg,
308 IF_NAMESIZE);
309 exit(2);
310 }
311 device = optarg;
312 break;
313 case 'f':
314 quit_on_reply = 1;
315 break;
316 case 's':
317 source = optarg;
318 break;
319 case 'h':
320 case '?':
321 default:
322 bb_show_usage();
323 }
324 }
325 argc -= optind;
326 argv += optind;
327
328 if (argc != 1)
329 bb_show_usage();
330
331 target = *argv;
332
333
334 if (s < 0) {
335 bb_error_msg("socket");
336 exit(socket_errno);
337 }
338
339 {
340 struct ifreq ifr;
341
342 memset(&ifr, 0, sizeof(ifr));
343 strncpy(ifr.ifr_name, device, IFNAMSIZ - 1);
344 if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
345 bb_error_msg("Interface %s not found", device);
346 exit(2);
347 }
348 ifindex = ifr.ifr_ifindex;
349
350 if (ioctl(s, SIOCGIFFLAGS, (char *) &ifr)) {
351 bb_error_msg("SIOCGIFFLAGS");
352 exit(2);
353 }
354 if (!(ifr.ifr_flags & IFF_UP)) {
355 bb_error_msg("Interface %s is down", device);
356 exit(2);
357 }
358 if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) {
359 bb_error_msg("Interface %s is not ARPable", device);
360 exit(dad ? 0 : 2);
361 }
362 }
363
364 if (!inet_aton(target, &dst)) {
365 struct hostent *hp;
366
367 hp = gethostbyname2(target, AF_INET);
368 if (!hp) {
369 bb_error_msg("invalid or unknown target %s", target);
370 exit(2);
371 }
372 memcpy(&dst, hp->h_addr, 4);
373 }
374
375 if (source && !inet_aton(source, &src)) {
376 bb_error_msg("invalid source address %s", source);
377 exit(2);
378 }
379
380 if (!dad && unsolicited && src.s_addr == 0)
381 src = dst;
382
383 if (!dad || src.s_addr) {
384 struct sockaddr_in saddr;
385 int probe_fd = socket(AF_INET, SOCK_DGRAM, 0);
386
387 if (probe_fd < 0) {
388 bb_error_msg("socket");
389 exit(2);
390 }
391 if (device) {
392 if (setsockopt
393 (probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device,
394 strlen(device) + 1) == -1)
395 bb_error_msg("WARNING: interface %s is ignored", device);
396 }
397 memset(&saddr, 0, sizeof(saddr));
398 saddr.sin_family = AF_INET;
399 if (src.s_addr) {
400 saddr.sin_addr = src;
401 if (bind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr)) == -1) {
402 bb_error_msg("bind");
403 exit(2);
404 }
405 } else if (!dad) {
406 int on = 1;
407 int alen = sizeof(saddr);
408
409 saddr.sin_port = htons(1025);
410 saddr.sin_addr = dst;
411
412 if (setsockopt
413 (probe_fd, SOL_SOCKET, SO_DONTROUTE, (char *) &on,
414 sizeof(on)) == -1)
415 perror("WARNING: setsockopt(SO_DONTROUTE)");
416 if (connect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr))
417 == -1) {
418 bb_error_msg("connect");
419 exit(2);
420 }
421 if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) ==
422 -1) {
423 bb_error_msg("getsockname");
424 exit(2);
425 }
426 src = saddr.sin_addr;
427 }
428 close(probe_fd);
429 };
430
431 me.sll_family = AF_PACKET;
432 me.sll_ifindex = ifindex;
433 me.sll_protocol = htons(ETH_P_ARP);
434 if (bind(s, (struct sockaddr *) &me, sizeof(me)) == -1) {
435 bb_error_msg("bind");
436 exit(2);
437 }
438
439 {
440 int alen = sizeof(me);
441
442 if (getsockname(s, (struct sockaddr *) &me, &alen) == -1) {
443 bb_error_msg("getsockname");
444 exit(2);
445 }
446 }
447 if (me.sll_halen == 0) {
448 bb_error_msg("Interface \"%s\" is not ARPable (no ll address)", device);
449 exit(dad ? 0 : 2);
450 }
451 he = me;
452 memset(he.sll_addr, -1, he.sll_halen);
453
454 if (!quiet) {
455 printf("ARPING to %s", inet_ntoa(dst));
456 printf(" from %s via %s\n", inet_ntoa(src),
457 device ? device : "unknown");
458 }
459
460 if (!src.s_addr && !dad) {
461 bb_error_msg("no src address in the non-DAD mode");
462 exit(2);
463 }
464
465 {
466 struct sigaction sa;
467
468 memset(&sa, 0, sizeof(sa));
469 sa.sa_flags = SA_RESTART;
470
471 sa.sa_handler = (void (*)(int)) finish;
472 sigaction(SIGINT, &sa, NULL);
473
474 sa.sa_handler = (void (*)(int)) catcher;
475 sigaction(SIGALRM, &sa, NULL);
476 }
477
478 catcher();
479
480 while (1) {
481 sigset_t sset, osset;
482 char packet[4096];
483 struct sockaddr_ll from;
484 int alen = sizeof(from);
485 int cc;
486
487 if ((cc = recvfrom(s, packet, sizeof(packet), 0,
488 (struct sockaddr *) &from, &alen)) < 0) {
489 perror("recvfrom");
490 continue;
491 }
492 sigemptyset(&sset);
493 sigaddset(&sset, SIGALRM);
494 sigaddset(&sset, SIGINT);
495 sigprocmask(SIG_BLOCK, &sset, &osset);
496 recv_pack(packet, cc, &from);
497 sigprocmask(SIG_SETMASK, &osset, NULL);
498 }
499}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * ftpget
4 *
5 * Mini implementation of FTP to retrieve a remote file.
6 *
7 * Copyright (C) 2002 Jeff Angielski, The PTR Group <jeff@theptrgroup.com>
8 * Copyright (C) 2002 Glenn McGrath <bug1@iinet.net.au>
9 *
10 * Based on wget.c by Chip Rosenthal Covad Communications
11 * <chip@laserlink.net>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 *
27 */
28
29#include <sys/types.h>
30#include <sys/ioctl.h>
31#include <sys/time.h>
32#include <sys/stat.h>
33
34#include <ctype.h>
35#include <errno.h>
36#include <fcntl.h>
37#include <getopt.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <signal.h>
41#include <string.h>
42#include <unistd.h>
43
44#include <netinet/in.h>
45#include <netdb.h>
46#include <sys/socket.h>
47#include <arpa/inet.h>
48
49#include "busybox.h"
50
51typedef struct ftp_host_info_s {
52 char *user;
53 char *password;
54 struct sockaddr_in *s_in;
55} ftp_host_info_t;
56
57static char verbose_flag = 0;
58static char do_continue = 0;
59
60static int ftpcmd(const char *s1, const char *s2, FILE *stream, char *buf)
61{
62 if (verbose_flag) {
63 bb_error_msg("cmd %s%s", s1, s2);
64 }
65
66 if (s1) {
67 if (s2) {
68 fprintf(stream, "%s%s\n", s1, s2);
69 } else {
70 fprintf(stream, "%s\n", s1);
71 }
72 }
73 do {
74 char *buf_ptr;
75
76 if (fgets(buf, 510, stream) == NULL) {
77 bb_perror_msg_and_die("fgets()");
78 }
79 buf_ptr = strstr(buf, "\r\n");
80 if (buf_ptr) {
81 *buf_ptr = '\0';
82 }
83 } while (! isdigit(buf[0]) || buf[3] != ' ');
84
85 return atoi(buf);
86}
87
88static int xconnect_ftpdata(ftp_host_info_t *server, const char *buf)
89{
90 char *buf_ptr;
91 unsigned short port_num;
92
93 buf_ptr = strrchr(buf, ',');
94 *buf_ptr = '\0';
95 port_num = atoi(buf_ptr + 1);
96
97 buf_ptr = strrchr(buf, ',');
98 *buf_ptr = '\0';
99 port_num += atoi(buf_ptr + 1) * 256;
100
101 server->s_in->sin_port=htons(port_num);
102 return(xconnect(server->s_in));
103}
104
105static FILE *ftp_login(ftp_host_info_t *server)
106{
107 FILE *control_stream;
108 char buf[512];
109
110 /* Connect to the command socket */
111 control_stream = fdopen(xconnect(server->s_in), "r+");
112 if (control_stream == NULL) {
113 bb_perror_msg_and_die("Couldnt open control stream");
114 }
115
116 if (ftpcmd(NULL, NULL, control_stream, buf) != 220) {
117 bb_error_msg_and_die("%s", buf + 4);
118 }
119
120 /* Login to the server */
121 switch (ftpcmd("USER ", server->user, control_stream, buf)) {
122 case 230:
123 break;
124 case 331:
125 if (ftpcmd("PASS ", server->password, control_stream, buf) != 230) {
126 bb_error_msg_and_die("PASS error: %s", buf + 4);
127 }
128 break;
129 default:
130 bb_error_msg_and_die("USER error: %s", buf + 4);
131 }
132
133 ftpcmd("TYPE I", NULL, control_stream, buf);
134
135 return(control_stream);
136}
137
138#ifdef CONFIG_FTPGET
139static int ftp_recieve(ftp_host_info_t *server, FILE *control_stream,
140 const char *local_path, char *server_path)
141{
142 char buf[512];
143 off_t filesize = 0;
144 int fd_data;
145 int fd_local = -1;
146 off_t beg_range = 0;
147
148 /* Connect to the data socket */
149 if (ftpcmd("PASV", NULL, control_stream, buf) != 227) {
150 bb_error_msg_and_die("PASV error: %s", buf + 4);
151 }
152 fd_data = xconnect_ftpdata(server, buf);
153
154 if (ftpcmd("SIZE ", server_path, control_stream, buf) == 213) {
155 unsigned long value=filesize;
156 if (safe_strtoul(buf + 4, &value))
157 bb_error_msg_and_die("SIZE error: %s", buf + 4);
158 filesize = value;
159 }
160
161 if ((local_path[0] == '-') && (local_path[1] == '\0')) {
162 fd_local = STDOUT_FILENO;
163 do_continue = 0;
164 }
165
166 if (do_continue) {
167 struct stat sbuf;
168 if (lstat(local_path, &sbuf) < 0) {
169 bb_perror_msg_and_die("fstat()");
170 }
171 if (sbuf.st_size > 0) {
172 beg_range = sbuf.st_size;
173 } else {
174 do_continue = 0;
175 }
176 }
177
178 if (do_continue) {
179 sprintf(buf, "REST %ld", (long)beg_range);
180 if (ftpcmd(buf, NULL, control_stream, buf) != 350) {
181 do_continue = 0;
182 } else {
183 filesize -= beg_range;
184 }
185 }
186
187 if (ftpcmd("RETR ", server_path, control_stream, buf) > 150) {
188 bb_error_msg_and_die("RETR error: %s", buf + 4);
189 }
190
191 /* only make a local file if we know that one exists on the remote server */
192 if (fd_local == -1) {
193 if (do_continue) {
194 fd_local = bb_xopen(local_path, O_APPEND | O_WRONLY);
195 } else {
196 fd_local = bb_xopen(local_path, O_CREAT | O_TRUNC | O_WRONLY);
197 }
198 }
199
200 /* Copy the file */
201 if (bb_copyfd_size(fd_data, fd_local, filesize) == -1) {
202 exit(EXIT_FAILURE);
203 }
204
205 /* close it all down */
206 close(fd_data);
207 if (ftpcmd(NULL, NULL, control_stream, buf) != 226) {
208 bb_error_msg_and_die("ftp error: %s", buf + 4);
209 }
210 ftpcmd("QUIT", NULL, control_stream, buf);
211
212 return(EXIT_SUCCESS);
213}
214#endif
215
216#ifdef CONFIG_FTPPUT
217static int ftp_send(ftp_host_info_t *server, FILE *control_stream,
218 const char *server_path, char *local_path)
219{
220 struct stat sbuf;
221 char buf[512];
222 int fd_data;
223 int fd_local;
224 int response;
225
226 /* Connect to the data socket */
227 if (ftpcmd("PASV", NULL, control_stream, buf) != 227) {
228 bb_error_msg_and_die("PASV error: %s", buf + 4);
229 }
230 fd_data = xconnect_ftpdata(server, buf);
231
232 if (ftpcmd("CWD ", server_path, control_stream, buf) != 250) {
233 bb_error_msg_and_die("CWD error: %s", buf + 4);
234 }
235
236 /* get the local file */
237 if ((local_path[0] == '-') && (local_path[1] == '\0')) {
238 fd_local = STDIN_FILENO;
239 } else {
240 fd_local = bb_xopen(local_path, O_RDONLY);
241 fstat(fd_local, &sbuf);
242
243 sprintf(buf, "ALLO %lu", (unsigned long)sbuf.st_size);
244 response = ftpcmd(buf, NULL, control_stream, buf);
245 switch (response) {
246 case 200:
247 case 202:
248 break;
249 default:
250 close(fd_local);
251 bb_error_msg_and_die("ALLO error: %s", buf + 4);
252 break;
253 }
254 }
255 response = ftpcmd("STOR ", local_path, control_stream, buf);
256 switch (response) {
257 case 125:
258 case 150:
259 break;
260 default:
261 close(fd_local);
262 bb_error_msg_and_die("STOR error: %s", buf + 4);
263 }
264
265 /* transfer the file */
266 if (bb_copyfd_eof(fd_local, fd_data) == -1) {
267 exit(EXIT_FAILURE);
268 }
269
270 /* close it all down */
271 close(fd_data);
272 if (ftpcmd(NULL, NULL, control_stream, buf) != 226) {
273 bb_error_msg_and_die("error: %s", buf + 4);
274 }
275 ftpcmd("QUIT", NULL, control_stream, buf);
276
277 return(EXIT_SUCCESS);
278}
279#endif
280
281#define FTPGETPUT_OPT_CONTINUE 1
282#define FTPGETPUT_OPT_VERBOSE 2
283#define FTPGETPUT_OPT_USER 4
284#define FTPGETPUT_OPT_PASSWORD 8
285#define FTPGETPUT_OPT_PORT 16
286
287static const struct option ftpgetput_long_options[] = {
288 {"continue", 1, NULL, 'c'},
289 {"verbose", 0, NULL, 'v'},
290 {"username", 1, NULL, 'u'},
291 {"password", 1, NULL, 'p'},
292 {"port", 1, NULL, 'P'},
293 {0, 0, 0, 0}
294};
295
296int ftpgetput_main(int argc, char **argv)
297{
298 /* content-length of the file */
299 unsigned long opt;
300 char *port = "ftp";
301
302 /* socket to ftp server */
303 FILE *control_stream;
304 struct sockaddr_in s_in;
305
306 /* continue a prev transfer (-c) */
307 ftp_host_info_t *server;
308
309 int (*ftp_action)(ftp_host_info_t *, FILE *, const char *, char *) = NULL;
310
311 /* Check to see if the command is ftpget or ftput */
312#ifdef CONFIG_FTPPUT
313# ifdef CONFIG_FTPGET
314 if (bb_applet_name[3] == 'p') {
315 ftp_action = ftp_send;
316 }
317# else
318 ftp_action = ftp_send;
319# endif
320#endif
321#ifdef CONFIG_FTPGET
322# ifdef CONFIG_FTPPUT
323 if (bb_applet_name[3] == 'g') {
324 ftp_action = ftp_recieve;
325 }
326# else
327 ftp_action = ftp_recieve;
328# endif
329#endif
330
331 /* Set default values */
332 server = xmalloc(sizeof(ftp_host_info_t));
333 server->user = "anonymous";
334 server->password = "busybox@";
335 verbose_flag = 0;
336
337 /*
338 * Decipher the command line
339 */
340 bb_applet_long_options = ftpgetput_long_options;
341 opt = bb_getopt_ulflags(argc, argv, "cvu:p:P:", &server->user, &server->password, &port);
342
343 /* Process the non-option command line arguments */
344 if (argc - optind != 3) {
345 bb_show_usage();
346 }
347
348 if (opt & FTPGETPUT_OPT_CONTINUE) {
349 do_continue = 1;
350 }
351 if (opt & FTPGETPUT_OPT_VERBOSE) {
352 verbose_flag = 1;
353 }
354
355 /* We want to do exactly _one_ DNS lookup, since some
356 * sites (i.e. ftp.us.debian.org) use round-robin DNS
357 * and we want to connect to only one IP... */
358 server->s_in = &s_in;
359 bb_lookup_host(&s_in, argv[optind]);
360 s_in.sin_port = bb_lookup_port(port, "tcp", 21);
361 if (verbose_flag) {
362 printf("Connecting to %s[%s]:%d\n",
363 argv[optind], inet_ntoa(s_in.sin_addr), ntohs(s_in.sin_port));
364 }
365
366 /* Connect/Setup/Configure the FTP session */
367 control_stream = ftp_login(server);
368
369 return(ftp_action(server, control_stream, argv[optind + 1], argv[optind + 2]));
370}
371
372/*
373Local Variables:
374c-file-style: "linux"
375c-basic-offset: 4
376tab-width: 4
377End:
378*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * $Id: hostname.c,v 1.36 2003/07/14 21:21:01 andersen Exp $
4 * Mini hostname implementation for busybox
5 *
6 * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
7 *
8 * adjusted by Erik Andersen <andersen@codepoet.org> to remove
9 * use of long options and GNU getopt. Improved the usage info.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26#include <errno.h>
27#include <arpa/inet.h>
28#include <netdb.h>
29#include <unistd.h>
30#include <string.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <getopt.h>
34#include "busybox.h"
35
36extern char *optarg; /* in unistd.h */
37extern int optind, opterr, optopt; /* in unistd.h */
38
39static void do_sethostname(char *s, int isfile)
40{
41 FILE *f;
42 char buf[255];
43
44 if (!s)
45 return;
46 if (!isfile) {
47 if (sethostname(s, strlen(s)) < 0) {
48 if (errno == EPERM)
49 bb_error_msg_and_die("you must be root to change the hostname");
50 else
51 bb_perror_msg_and_die("sethostname");
52 }
53 } else {
54 f = bb_xfopen(s, "r");
55 while (fgets(buf, 255, f) != NULL) {
56 if (buf[0] =='#') {
57 continue;
58 }
59 chomp(buf);
60 do_sethostname(buf, 0);
61 }
62#ifdef CONFIG_FEATURE_CLEAN_UP
63 fclose(f);
64#endif
65 }
66}
67
68int hostname_main(int argc, char **argv)
69{
70 int opt;
71 int type = 0;
72 struct hostent *hp;
73 char *filename = NULL;
74 char buf[255];
75 char *p = NULL;
76
77 if (argc < 1)
78 bb_show_usage();
79
80 while ((opt = getopt(argc, argv, "dfisF:")) > 0) {
81 switch (opt) {
82 case 'd':
83 case 'f':
84 case 'i':
85 case 's':
86 type = opt;
87 break;
88 case 'F':
89 filename = optarg;
90 break;
91 default:
92 bb_show_usage();
93 }
94 }
95
96 /* Output in desired format */
97 if (type != 0) {
98 gethostname(buf, 255);
99 hp = xgethostbyname(buf);
100 p = strchr(hp->h_name, '.');
101 if (type == 'f') {
102 puts(hp->h_name);
103 } else if (type == 's') {
104 if (p != NULL) {
105 *p = 0;
106 }
107 puts(hp->h_name);
108 } else if (type == 'd') {
109 if (p) puts(p + 1);
110 } else if (type == 'i') {
111 while (hp->h_addr_list[0]) {
112 printf("%s ", inet_ntoa(*(struct in_addr *) (*hp->h_addr_list++)));
113 }
114 printf("\n");
115 }
116 }
117 /* Set the hostname */
118 else if (filename != NULL) {
119 do_sethostname(filename, 1);
120 } else if (optind < argc) {
121 do_sethostname(argv[optind], 0);
122 }
123 /* Or if all else fails,
124 * just print the current hostname */
125 else {
126 gethostname(buf, 255);
127 puts(buf);
128 }
129 return(0);
130}
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 @@
1/*
2 * httpd implementation for busybox
3 *
4 * Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org>
5 * Copyright (C) 2003,2004 Vladimir Oleynik <dzo@simtreas.ru>
6 *
7 * simplify patch stolen from libbb without using strdup
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 *****************************************************************************
24 *
25 * Typical usage:
26 * for non root user
27 * httpd -p 8080 -h $HOME/public_html
28 * or for daemon start from rc script with uid=0:
29 * httpd -u www
30 * This is equivalent if www user have uid=80 to
31 * httpd -p 80 -u 80 -h /www -c /etc/httpd.conf -r "Web Server Authentication"
32 *
33 *
34 * When a url contains "cgi-bin" it is assumed to be a cgi script. The
35 * server changes directory to the location of the script and executes it
36 * after setting QUERY_STRING and other environment variables.
37 *
38 * The server can also be invoked as a url arg decoder and html text encoder
39 * as follows:
40 * foo=`httpd -d $foo` # decode "Hello%20World" as "Hello World"
41 * bar=`httpd -e "<Hello World>"` # encode as "&#60Hello&#32World&#62"
42 * Note that url encoding for arguments is not the same as html encoding for
43 * presentation. -d decodes a url-encoded argument while -e encodes in html
44 * for page display.
45 *
46 * httpd.conf has the following format:
47 *
48 * A:172.20. # Allow address from 172.20.0.0/16
49 * A:10.0.0.0/25 # Allow any address from 10.0.0.0-10.0.0.127
50 * A:10.0.0.0/255.255.255.128 # Allow any address that previous set
51 * A:127.0.0.1 # Allow local loopback connections
52 * D:* # Deny from other IP connections
53 * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/
54 * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/
55 * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/
56 * .au:audio/basic # additional mime type for audio.au files
57 *
58 * A/D may be as a/d or allow/deny - first char case insensitive
59 * Deny IP rules take precedence over allow rules.
60 *
61 *
62 * The Deny/Allow IP logic:
63 *
64 * - Default is to allow all. No addresses are denied unless
65 * denied with a D: rule.
66 * - Order of Deny/Allow rules is significant
67 * - Deny rules take precedence over allow rules.
68 * - If a deny all rule (D:*) is used it acts as a catch-all for unmatched
69 * addresses.
70 * - Specification of Allow all (A:*) is a no-op
71 *
72 * Example:
73 * 1. Allow only specified addresses
74 * A:172.20 # Allow any address that begins with 172.20.
75 * A:10.10. # Allow any address that begins with 10.10.
76 * A:127.0.0.1 # Allow local loopback connections
77 * D:* # Deny from other IP connections
78 *
79 * 2. Only deny specified addresses
80 * D:1.2.3. # deny from 1.2.3.0 - 1.2.3.255
81 * D:2.3.4. # deny from 2.3.4.0 - 2.3.4.255
82 * A:* # (optional line added for clarity)
83 *
84 * If a sub directory contains a config file it is parsed and merged with
85 * any existing settings as if it was appended to the original configuration.
86 *
87 * subdir paths are relative to the containing subdir and thus cannot
88 * affect the parent rules.
89 *
90 * Note that since the sub dir is parsed in the forked thread servicing the
91 * subdir http request, any merge is discarded when the process exits. As a
92 * result, the subdir settings only have a lifetime of a single request.
93 *
94 *
95 * If -c is not set, an attempt will be made to open the default
96 * root configuration file. If -c is set and the file is not found, the
97 * server exits with an error.
98 *
99*/
100
101
102#include <stdio.h>
103#include <ctype.h> /* for isspace */
104#include <string.h>
105#include <stdlib.h> /* for malloc */
106#include <time.h>
107#include <unistd.h> /* for close */
108#include <signal.h>
109#include <sys/types.h>
110#include <sys/socket.h> /* for connect and socket*/
111#include <netinet/in.h> /* for sockaddr_in */
112#include <sys/time.h>
113#include <sys/stat.h>
114#include <sys/wait.h>
115#include <fcntl.h> /* for open modes */
116#include "busybox.h"
117
118
119static const char httpdVersion[] = "busybox httpd/1.35 6-Oct-2004";
120static const char default_path_httpd_conf[] = "/etc";
121static const char httpd_conf[] = "httpd.conf";
122static const char home[] = "./";
123
124#ifdef CONFIG_LFS
125# define cont_l_fmt "%lld"
126#else
127# define cont_l_fmt "%ld"
128#endif
129
130#define TIMEOUT 60
131
132// Note: busybox xfuncs are not used because we want the server to keep running
133// if something bad happens due to a malformed user request.
134// As a result, all memory allocation after daemonize
135// is checked rigorously
136
137//#define DEBUG 1
138
139/* Configure options, disabled by default as custom httpd feature */
140
141/* disabled as optional features */
142//#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
143//#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
144//#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
145//#define CONFIG_FEATURE_HTTPD_SETUID
146//#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
147
148/* If set, use this server from internet superserver only */
149//#define CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
150
151/* You can use this server as standalone, require libbb.a for linking */
152//#define HTTPD_STANDALONE
153
154/* Config options, disable this for do very small module */
155//#define CONFIG_FEATURE_HTTPD_CGI
156//#define CONFIG_FEATURE_HTTPD_BASIC_AUTH
157//#define CONFIG_FEATURE_HTTPD_AUTH_MD5
158
159#ifdef HTTPD_STANDALONE
160/* standalone, enable all features */
161#undef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
162/* unset config option for remove warning as redefined */
163#undef CONFIG_FEATURE_HTTPD_BASIC_AUTH
164#undef CONFIG_FEATURE_HTTPD_AUTH_MD5
165#undef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
166#undef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
167#undef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
168#undef CONFIG_FEATURE_HTTPD_CGI
169#undef CONFIG_FEATURE_HTTPD_SETUID
170#undef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
171/* enable all features now */
172#define CONFIG_FEATURE_HTTPD_BASIC_AUTH
173#define CONFIG_FEATURE_HTTPD_AUTH_MD5
174#define CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
175#define CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
176#define CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
177#define CONFIG_FEATURE_HTTPD_CGI
178#define CONFIG_FEATURE_HTTPD_SETUID
179#define CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
180
181/* require from libbb.a for linking */
182const char *bb_applet_name = "httpd";
183
184void bb_show_usage(void)
185{
186 fprintf(stderr, "Usage: %s [-p <port>] [-c configFile] [-d/-e <string>] "
187 "[-r realm] [-u user] [-h homedir]\n", bb_applet_name);
188 exit(1);
189}
190#endif
191
192#ifdef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
193#undef CONFIG_FEATURE_HTTPD_SETUID /* use inetd user.group config settings */
194#undef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP /* so is not daemon */
195/* inetd set stderr to accepted socket and we can`t true see debug messages */
196#undef DEBUG
197#endif
198
199#define MAX_MEMORY_BUFF 8192 /* IO buffer */
200
201typedef struct HT_ACCESS {
202 char *after_colon;
203 struct HT_ACCESS *next;
204 char before_colon[1]; /* really bigger, must last */
205} Htaccess;
206
207typedef struct HT_ACCESS_IP {
208 unsigned int ip;
209 unsigned int mask;
210 int allow_deny;
211 struct HT_ACCESS_IP *next;
212} Htaccess_IP;
213
214typedef struct
215{
216 char buf[MAX_MEMORY_BUFF];
217
218#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
219 const char *realm;
220 char *remoteuser;
221#endif
222
223 const char *query;
224
225#ifdef CONFIG_FEATURE_HTTPD_CGI
226 char *referer;
227#endif
228
229 const char *configFile;
230
231 unsigned int rmt_ip;
232#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG)
233 char rmt_ip_str[16]; /* for set env REMOTE_ADDR */
234#endif
235 unsigned port; /* server initial port and for
236 set env REMOTE_PORT */
237 union HTTPD_FOUND {
238 const char *found_mime_type;
239 const char *found_moved_temporarily;
240 } httpd_found;
241
242 off_t ContentLength; /* -1 - unknown */
243 time_t last_mod;
244
245 Htaccess_IP *ip_a_d; /* config allow/deny lines */
246 int flg_deny_all;
247#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
248 Htaccess *auth; /* config user:password lines */
249#endif
250#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
251 Htaccess *mime_a; /* config mime types */
252#endif
253
254#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
255 int accepted_socket;
256#define a_c_r config->accepted_socket
257#define a_c_w config->accepted_socket
258 int debugHttpd; /* if seted, don`t stay daemon */
259#else
260#define a_c_r 0
261#define a_c_w 1
262#endif
263 volatile int alarm_signaled;
264
265} HttpdConfig;
266
267static HttpdConfig *config;
268
269static const char request_GET[] = "GET"; /* size algorithic optimize */
270
271static const char* const suffixTable [] = {
272/* Warning: shorted equivalent suffix in one line must be first */
273 ".htm.html", "text/html",
274 ".jpg.jpeg", "image/jpeg",
275 ".gif", "image/gif",
276 ".png", "image/png",
277 ".txt.h.c.cc.cpp", "text/plain",
278 ".css", "text/css",
279 ".wav", "audio/wav",
280 ".avi", "video/x-msvideo",
281 ".qt.mov", "video/quicktime",
282 ".mpe.mpeg", "video/mpeg",
283 ".mid.midi", "audio/midi",
284 ".mp3", "audio/mpeg",
285#if 0 /* unpopular */
286 ".au", "audio/basic",
287 ".pac", "application/x-ns-proxy-autoconfig",
288 ".vrml.wrl", "model/vrml",
289#endif
290 0, "application/octet-stream" /* default */
291 };
292
293typedef enum
294{
295 HTTP_OK = 200,
296 HTTP_MOVED_TEMPORARILY = 302,
297 HTTP_BAD_REQUEST = 400, /* malformed syntax */
298 HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
299 HTTP_NOT_FOUND = 404,
300 HTTP_FORBIDDEN = 403,
301 HTTP_REQUEST_TIMEOUT = 408,
302 HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
303 HTTP_INTERNAL_SERVER_ERROR = 500,
304#if 0 /* future use */
305 HTTP_CONTINUE = 100,
306 HTTP_SWITCHING_PROTOCOLS = 101,
307 HTTP_CREATED = 201,
308 HTTP_ACCEPTED = 202,
309 HTTP_NON_AUTHORITATIVE_INFO = 203,
310 HTTP_NO_CONTENT = 204,
311 HTTP_MULTIPLE_CHOICES = 300,
312 HTTP_MOVED_PERMANENTLY = 301,
313 HTTP_NOT_MODIFIED = 304,
314 HTTP_PAYMENT_REQUIRED = 402,
315 HTTP_BAD_GATEWAY = 502,
316 HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
317 HTTP_RESPONSE_SETSIZE=0xffffffff
318#endif
319} HttpResponseNum;
320
321typedef struct
322{
323 HttpResponseNum type;
324 const char *name;
325 const char *info;
326} HttpEnumString;
327
328static const HttpEnumString httpResponseNames[] = {
329 { HTTP_OK, "OK" },
330 { HTTP_MOVED_TEMPORARILY, "Found", "Directories must end with a slash." },
331 { HTTP_REQUEST_TIMEOUT, "Request Timeout",
332 "No request appeared within a reasonable time period." },
333 { HTTP_NOT_IMPLEMENTED, "Not Implemented",
334 "The requested method is not recognized by this server." },
335#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
336 { HTTP_UNAUTHORIZED, "Unauthorized", "" },
337#endif
338 { HTTP_NOT_FOUND, "Not Found",
339 "The requested URL was not found on this server." },
340 { HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." },
341 { HTTP_FORBIDDEN, "Forbidden", "" },
342 { HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error",
343 "Internal Server Error" },
344#if 0 /* not implemented */
345 { HTTP_CREATED, "Created" },
346 { HTTP_ACCEPTED, "Accepted" },
347 { HTTP_NO_CONTENT, "No Content" },
348 { HTTP_MULTIPLE_CHOICES, "Multiple Choices" },
349 { HTTP_MOVED_PERMANENTLY, "Moved Permanently" },
350 { HTTP_NOT_MODIFIED, "Not Modified" },
351 { HTTP_BAD_GATEWAY, "Bad Gateway", "" },
352 { HTTP_SERVICE_UNAVAILABLE, "Service Unavailable", "" },
353#endif
354};
355
356
357static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
358static const char Content_length[] = "Content-length:";
359
360
361static int
362scan_ip (const char **ep, unsigned int *ip, unsigned char endc)
363{
364 const char *p = *ep;
365 int auto_mask = 8;
366 int j;
367
368 *ip = 0;
369 for (j = 0; j < 4; j++) {
370 unsigned int octet;
371
372 if ((*p < '0' || *p > '9') && (*p != '/' || j == 0) && *p != 0)
373 return -auto_mask;
374 octet = 0;
375 while (*p >= '0' && *p <= '9') {
376 octet *= 10;
377 octet += *p - '0';
378 if (octet > 255)
379 return -auto_mask;
380 p++;
381 }
382 if (*p == '.')
383 p++;
384 if (*p != '/' && *p != 0)
385 auto_mask += 8;
386 *ip = ((*ip) << 8) | octet;
387 }
388 if (*p != 0) {
389 if (*p != endc)
390 return -auto_mask;
391 p++;
392 if(*p == 0)
393 return -auto_mask;
394 }
395 *ep = p;
396 return auto_mask;
397}
398
399static int
400scan_ip_mask (const char *ipm, unsigned int *ip, unsigned int *mask)
401{
402 int i;
403 unsigned int msk;
404
405 i = scan_ip(&ipm, ip, '/');
406 if(i < 0)
407 return i;
408 if(*ipm) {
409 const char *p = ipm;
410
411 i = 0;
412 while (*p) {
413 if (*p < '0' || *p > '9') {
414 if (*p == '.') {
415 i = scan_ip (&ipm, mask, 0);
416 return i != 32;
417 }
418 return -1;
419 }
420 i *= 10;
421 i += *p - '0';
422 p++;
423 }
424 }
425 if (i > 32 || i < 0)
426 return -1;
427 msk = 0x80000000;
428 *mask = 0;
429 while (i > 0) {
430 *mask |= msk;
431 msk >>= 1;
432 i--;
433 }
434 return 0;
435}
436
437#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES)
438static void free_config_lines(Htaccess **pprev)
439{
440 Htaccess *prev = *pprev;
441
442 while( prev ) {
443 Htaccess *cur = prev;
444
445 prev = cur->next;
446 free(cur);
447 }
448 *pprev = NULL;
449}
450#endif
451
452/* flag */
453#define FIRST_PARSE 0
454#define SUBDIR_PARSE 1
455#define SIGNALED_PARSE 2
456#define FIND_FROM_HTTPD_ROOT 3
457/****************************************************************************
458 *
459 > $Function: parse_conf()
460 *
461 * $Description: parse configuration file into in-memory linked list.
462 *
463 * The first non-white character is examined to determine if the config line
464 * is one of the following:
465 * .ext:mime/type # new mime type not compiled into httpd
466 * [adAD]:from # ip address allow/deny, * for wildcard
467 * /path:user:pass # username/password
468 *
469 * Any previous IP rules are discarded.
470 * If the flag argument is not SUBDIR_PARSE then all /path and mime rules
471 * are also discarded. That is, previous settings are retained if flag is
472 * SUBDIR_PARSE.
473 *
474 * $Parameters:
475 * (const char *) path . . null for ip address checks, path for password
476 * checks.
477 * (int) flag . . . . . . the source of the parse request.
478 *
479 * $Return: (None)
480 *
481 ****************************************************************************/
482static void parse_conf(const char *path, int flag)
483{
484 FILE *f;
485#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
486 Htaccess *prev, *cur;
487#elif CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
488 Htaccess *cur;
489#endif
490
491 const char *cf = config->configFile;
492 char buf[160];
493 char *p0 = NULL;
494 char *c, *p;
495
496 /* free previous ip setup if present */
497 Htaccess_IP *pip = config->ip_a_d;
498
499 while( pip ) {
500 Htaccess_IP *cur_ipl = pip;
501
502 pip = cur_ipl->next;
503 free(cur_ipl);
504 }
505 config->ip_a_d = NULL;
506
507 config->flg_deny_all = 0;
508
509#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES)
510 /* retain previous auth and mime config only for subdir parse */
511 if(flag != SUBDIR_PARSE) {
512#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
513 free_config_lines(&config->auth);
514#endif
515#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
516 free_config_lines(&config->mime_a);
517#endif
518 }
519#endif
520
521 if(flag == SUBDIR_PARSE || cf == NULL) {
522 cf = alloca(strlen(path) + sizeof(httpd_conf) + 2);
523 if(cf == NULL) {
524 if(flag == FIRST_PARSE)
525 bb_error_msg_and_die(bb_msg_memory_exhausted);
526 return;
527 }
528 sprintf((char *)cf, "%s/%s", path, httpd_conf);
529 }
530
531 while((f = fopen(cf, "r")) == NULL) {
532 if(flag == SUBDIR_PARSE || flag == FIND_FROM_HTTPD_ROOT) {
533 /* config file not found, no changes to config */
534 return;
535 }
536 if(config->configFile && flag == FIRST_PARSE) /* if -c option given */
537 bb_perror_msg_and_die("%s", cf);
538 flag = FIND_FROM_HTTPD_ROOT;
539 cf = httpd_conf;
540 }
541
542#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
543 prev = config->auth;
544#endif
545 /* This could stand some work */
546 while ( (p0 = fgets(buf, sizeof(buf), f)) != NULL) {
547 c = NULL;
548 for(p = p0; *p0 != 0 && *p0 != '#'; p0++) {
549 if(!isspace(*p0)) {
550 *p++ = *p0;
551 if(*p0 == ':' && c == NULL)
552 c = p;
553 }
554 }
555 *p = 0;
556
557 /* test for empty or strange line */
558 if (c == NULL || *c == 0)
559 continue;
560 p0 = buf;
561 if(*p0 == 'd')
562 *p0 = 'D';
563 if(*c == '*') {
564 if(*p0 == 'D') {
565 /* memorize deny all */
566 config->flg_deny_all++;
567 }
568 /* skip default other "word:*" config lines */
569 continue;
570 }
571
572 if(*p0 == 'a')
573 *p0 = 'A';
574 else if(*p0 != 'D' && *p0 != 'A'
575#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
576 && *p0 != '/'
577#endif
578#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
579 && *p0 != '.'
580#endif
581 )
582 continue;
583 if(*p0 == 'A' || *p0 == 'D') {
584 /* storing current config IP line */
585 pip = calloc(1, sizeof(Htaccess_IP));
586 if(pip) {
587 if(scan_ip_mask (c, &(pip->ip), &(pip->mask))) {
588 /* syntax IP{/mask} error detected, protect all */
589 *p0 = 'D';
590 pip->mask = 0;
591 }
592 pip->allow_deny = *p0;
593 if(*p0 == 'D') {
594 /* Deny:form_IP move top */
595 pip->next = config->ip_a_d;
596 config->ip_a_d = pip;
597 } else {
598 /* add to bottom A:form_IP config line */
599 Htaccess_IP *prev_IP = config->ip_a_d;
600
601 if(prev_IP == NULL) {
602 config->ip_a_d = pip;
603 } else {
604 while(prev_IP->next)
605 prev_IP = prev_IP->next;
606 prev_IP->next = pip;
607 }
608 }
609 }
610 continue;
611 }
612#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
613 if(*p0 == '/') {
614 /* make full path from httpd root / curent_path / config_line_path */
615 cf = flag == SUBDIR_PARSE ? path : "";
616 p0 = malloc(strlen(cf) + (c - buf) + 2 + strlen(c));
617 if(p0 == NULL)
618 continue;
619 c[-1] = 0;
620 sprintf(p0, "/%s%s", cf, buf);
621
622 /* another call bb_simplify_path */
623 cf = p = p0;
624
625 do {
626 if (*p == '/') {
627 if (*cf == '/') { /* skip duplicate (or initial) slash */
628 continue;
629 } else if (*cf == '.') {
630 if (cf[1] == '/' || cf[1] == 0) { /* remove extra '.' */
631 continue;
632 } else if ((cf[1] == '.') && (cf[2] == '/' || cf[2] == 0)) {
633 ++cf;
634 if (p > p0) {
635 while (*--p != '/'); /* omit previous dir */
636 }
637 continue;
638 }
639 }
640 }
641 *++p = *cf;
642 } while (*++cf);
643
644 if ((p == p0) || (*p != '/')) { /* not a trailing slash */
645 ++p; /* so keep last character */
646 }
647 *p = 0;
648 sprintf(p0, "%s:%s", p0, c);
649 }
650#endif
651
652#if defined(CONFIG_FEATURE_HTTPD_BASIC_AUTH) || defined(CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES)
653 /* storing current config line */
654 cur = calloc(1, sizeof(Htaccess) + strlen(p0));
655 if(cur) {
656 cf = strcpy(cur->before_colon, p0);
657 c = strchr(cf, ':');
658 *c++ = 0;
659 cur->after_colon = c;
660#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
661 if(*cf == '.') {
662 /* config .mime line move top for overwrite previous */
663 cur->next = config->mime_a;
664 config->mime_a = cur;
665 continue;
666 }
667#endif
668#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
669 free(p0);
670 if(prev == NULL) {
671 /* first line */
672 config->auth = prev = cur;
673 } else {
674 /* sort path, if current lenght eq or bigger then move up */
675 Htaccess *prev_hti = config->auth;
676 int l = strlen(cf);
677 Htaccess *hti;
678
679 for(hti = prev_hti; hti; hti = hti->next) {
680 if(l >= strlen(hti->before_colon)) {
681 /* insert before hti */
682 cur->next = hti;
683 if(prev_hti != hti) {
684 prev_hti->next = cur;
685 } else {
686 /* insert as top */
687 config->auth = cur;
688 }
689 break;
690 }
691 if(prev_hti != hti)
692 prev_hti = prev_hti->next;
693 }
694 if(!hti) { /* not inserted, add to bottom */
695 prev->next = cur;
696 prev = cur;
697 }
698 }
699#endif
700 }
701#endif
702 }
703 fclose(f);
704}
705
706#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
707/****************************************************************************
708 *
709 > $Function: encodeString()
710 *
711 * $Description: Given a string, html encode special characters.
712 * This is used for the -e command line option to provide an easy way
713 * for scripts to encode result data without confusing browsers. The
714 * returned string pointer is memory allocated by malloc().
715 *
716 * $Parameters:
717 * (const char *) string . . The first string to encode.
718 *
719 * $Return: (char *) . . . .. . . A pointer to the encoded string.
720 *
721 * $Errors: Returns a null string ("") if memory is not available.
722 *
723 ****************************************************************************/
724static char *encodeString(const char *string)
725{
726 /* take the simple route and encode everything */
727 /* could possibly scan once to get length. */
728 int len = strlen(string);
729 char *out = malloc(len*5 +1);
730 char *p=out;
731 char ch;
732
733 if (!out) return "";
734 while ((ch = *string++)) {
735 // very simple check for what to encode
736 if (isalnum(ch)) *p++ = ch;
737 else p += sprintf(p, "&#%d", (unsigned char) ch);
738 }
739 *p=0;
740 return out;
741}
742#endif /* CONFIG_FEATURE_HTTPD_ENCODE_URL_STR */
743
744/****************************************************************************
745 *
746 > $Function: decodeString()
747 *
748 * $Description: Given a URL encoded string, convert it to plain ascii.
749 * Since decoding always makes strings smaller, the decode is done in-place.
750 * Thus, callers should strdup() the argument if they do not want the
751 * argument modified. The return is the original pointer, allowing this
752 * function to be easily used as arguments to other functions.
753 *
754 * $Parameters:
755 * (char *) string . . . The first string to decode.
756 * (int) flag . . . 1 if require decode '+' as ' ' for CGI
757 *
758 * $Return: (char *) . . . . A pointer to the decoded string (same as input).
759 *
760 * $Errors: None
761 *
762 ****************************************************************************/
763static char *decodeString(char *orig, int flag_plus_to_space)
764{
765 /* note that decoded string is always shorter than original */
766 char *string = orig;
767 char *ptr = string;
768
769 while (*ptr)
770 {
771 if (*ptr == '+' && flag_plus_to_space) { *string++ = ' '; ptr++; }
772 else if (*ptr != '%') *string++ = *ptr++;
773 else {
774 unsigned int value;
775 sscanf(ptr+1, "%2X", &value);
776 *string++ = value;
777 ptr += 3;
778 }
779 }
780 *string = '\0';
781 return orig;
782}
783
784
785#ifdef CONFIG_FEATURE_HTTPD_CGI
786/****************************************************************************
787 *
788 > $Function: addEnv()
789 *
790 * $Description: Add an environment variable setting to the global list.
791 * A NAME=VALUE string is allocated, filled, and added to the list of
792 * environment settings passed to the cgi execution script.
793 *
794 * $Parameters:
795 * (char *) name_before_underline - The first part environment variable name.
796 * (char *) name_after_underline - The second part environment variable name.
797 * (char *) value . . The value to which the env variable is set.
798 *
799 * $Return: (void)
800 *
801 * $Errors: Silently returns if the env runs out of space to hold the new item
802 *
803 ****************************************************************************/
804static void addEnv(const char *name_before_underline,
805 const char *name_after_underline, const char *value)
806{
807 char *s = NULL;
808 const char *underline;
809
810 if (!value)
811 value = "";
812 underline = *name_after_underline ? "_" : "";
813 asprintf(&s, "%s%s%s=%s", name_before_underline, underline,
814 name_after_underline, value);
815 if(s) {
816 putenv(s);
817 }
818}
819
820#if defined(CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV) || !defined(CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY)
821/* set environs SERVER_PORT and REMOTE_PORT */
822static void addEnvPort(const char *port_name)
823{
824 char buf[16];
825
826 sprintf(buf, "%u", config->port);
827 addEnv(port_name, "PORT", buf);
828}
829#endif
830#endif /* CONFIG_FEATURE_HTTPD_CGI */
831
832
833#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
834/****************************************************************************
835 *
836 > $Function: decodeBase64()
837 *
838 > $Description: Decode a base 64 data stream as per rfc1521.
839 * Note that the rfc states that none base64 chars are to be ignored.
840 * Since the decode always results in a shorter size than the input, it is
841 * OK to pass the input arg as an output arg.
842 *
843 * $Parameter:
844 * (char *) Data . . . . A pointer to a base64 encoded string.
845 * Where to place the decoded data.
846 *
847 * $Return: void
848 *
849 * $Errors: None
850 *
851 ****************************************************************************/
852static void decodeBase64(char *Data)
853{
854
855 const unsigned char *in = Data;
856 // The decoded size will be at most 3/4 the size of the encoded
857 unsigned long ch = 0;
858 int i = 0;
859
860 while (*in) {
861 int t = *in++;
862
863 if(t >= '0' && t <= '9')
864 t = t - '0' + 52;
865 else if(t >= 'A' && t <= 'Z')
866 t = t - 'A';
867 else if(t >= 'a' && t <= 'z')
868 t = t - 'a' + 26;
869 else if(t == '+')
870 t = 62;
871 else if(t == '/')
872 t = 63;
873 else if(t == '=')
874 t = 0;
875 else
876 continue;
877
878 ch = (ch << 6) | t;
879 i++;
880 if (i == 4) {
881 *Data++ = (char) (ch >> 16);
882 *Data++ = (char) (ch >> 8);
883 *Data++ = (char) ch;
884 i = 0;
885 }
886 }
887 *Data = 0;
888}
889#endif
890
891
892#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
893/****************************************************************************
894 *
895 > $Function: openServer()
896 *
897 * $Description: create a listen server socket on the designated port.
898 *
899 * $Return: (int) . . . A connection socket. -1 for errors.
900 *
901 * $Errors: None
902 *
903 ****************************************************************************/
904static int openServer(void)
905{
906 struct sockaddr_in lsocket;
907 int fd;
908
909 /* create the socket right now */
910 /* inet_addr() returns a value that is already in network order */
911 memset(&lsocket, 0, sizeof(lsocket));
912 lsocket.sin_family = AF_INET;
913 lsocket.sin_addr.s_addr = INADDR_ANY;
914 lsocket.sin_port = htons(config->port) ;
915 fd = socket(AF_INET, SOCK_STREAM, 0);
916 if (fd >= 0) {
917 /* tell the OS it's OK to reuse a previous address even though */
918 /* it may still be in a close down state. Allows bind to succeed. */
919 int on = 1;
920#ifdef SO_REUSEPORT
921 setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)) ;
922#else
923 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) ;
924#endif
925 if (bind(fd, (struct sockaddr *)&lsocket, sizeof(lsocket)) == 0) {
926 listen(fd, 9);
927 signal(SIGCHLD, SIG_IGN); /* prevent zombie (defunct) processes */
928 } else {
929 bb_perror_msg_and_die("bind");
930 }
931 } else {
932 bb_perror_msg_and_die("create socket");
933 }
934 return fd;
935}
936#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
937
938/****************************************************************************
939 *
940 > $Function: sendHeaders()
941 *
942 * $Description: Create and send HTTP response headers.
943 * The arguments are combined and sent as one write operation. Note that
944 * IE will puke big-time if the headers are not sent in one packet and the
945 * second packet is delayed for any reason.
946 *
947 * $Parameter:
948 * (HttpResponseNum) responseNum . . . The result code to send.
949 *
950 * $Return: (int) . . . . writing errors
951 *
952 ****************************************************************************/
953static int sendHeaders(HttpResponseNum responseNum)
954{
955 char *buf = config->buf;
956 const char *responseString = "";
957 const char *infoString = 0;
958 const char *mime_type;
959 unsigned int i;
960 time_t timer = time(0);
961 char timeStr[80];
962 int len;
963
964 for (i = 0;
965 i < (sizeof(httpResponseNames)/sizeof(httpResponseNames[0])); i++) {
966 if (httpResponseNames[i].type == responseNum) {
967 responseString = httpResponseNames[i].name;
968 infoString = httpResponseNames[i].info;
969 break;
970 }
971 }
972 /* error message is HTML */
973 mime_type = responseNum == HTTP_OK ?
974 config->httpd_found.found_mime_type : "text/html";
975
976 /* emit the current date */
977 strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer));
978 len = sprintf(buf,
979 "HTTP/1.0 %d %s\nContent-type: %s\r\n"
980 "Date: %s\r\nConnection: close\r\n",
981 responseNum, responseString, mime_type, timeStr);
982
983#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
984 if (responseNum == HTTP_UNAUTHORIZED) {
985 len += sprintf(buf+len, "WWW-Authenticate: Basic realm=\"%s\"\r\n",
986 config->realm);
987 }
988#endif
989 if(responseNum == HTTP_MOVED_TEMPORARILY) {
990 len += sprintf(buf+len, "Location: %s/%s%s\r\n",
991 config->httpd_found.found_moved_temporarily,
992 (config->query ? "?" : ""),
993 (config->query ? config->query : ""));
994 }
995
996 if (config->ContentLength != -1) { /* file */
997 strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&config->last_mod));
998 len += sprintf(buf+len, "Last-Modified: %s\r\n%s " cont_l_fmt "\r\n",
999 timeStr, Content_length, config->ContentLength);
1000 }
1001 strcat(buf, "\r\n");
1002 len += 2;
1003 if (infoString) {
1004 len += sprintf(buf+len,
1005 "<HEAD><TITLE>%d %s</TITLE></HEAD>\n"
1006 "<BODY><H1>%d %s</H1>\n%s\n</BODY>\n",
1007 responseNum, responseString,
1008 responseNum, responseString, infoString);
1009 }
1010#ifdef DEBUG
1011 if (config->debugHttpd) fprintf(stderr, "Headers: '%s'", buf);
1012#endif
1013 return bb_full_write(a_c_w, buf, len);
1014}
1015
1016/****************************************************************************
1017 *
1018 > $Function: getLine()
1019 *
1020 * $Description: Read from the socket until an end of line char found.
1021 *
1022 * Characters are read one at a time until an eol sequence is found.
1023 *
1024 * $Return: (int) . . . . number of characters read. -1 if error.
1025 *
1026 ****************************************************************************/
1027static int getLine(void)
1028{
1029 int count = 0;
1030 char *buf = config->buf;
1031
1032 while (read(a_c_r, buf + count, 1) == 1) {
1033 if (buf[count] == '\r') continue;
1034 if (buf[count] == '\n') {
1035 buf[count] = 0;
1036 return count;
1037 }
1038 if(count < (MAX_MEMORY_BUFF-1)) /* check owerflow */
1039 count++;
1040 }
1041 if (count) return count;
1042 else return -1;
1043}
1044
1045#ifdef CONFIG_FEATURE_HTTPD_CGI
1046/****************************************************************************
1047 *
1048 > $Function: sendCgi()
1049 *
1050 * $Description: Execute a CGI script and send it's stdout back
1051 *
1052 * Environment variables are set up and the script is invoked with pipes
1053 * for stdin/stdout. If a post is being done the script is fed the POST
1054 * data in addition to setting the QUERY_STRING variable (for GETs or POSTs).
1055 *
1056 * $Parameters:
1057 * (const char *) url . . . . . . The requested URL (with leading /).
1058 * (int bodyLen) . . . . . . . . Length of the post body.
1059 * (const char *cookie) . . . . . For set HTTP_COOKIE.
1060 * (const char *content_type) . . For set CONTENT_TYPE.
1061
1062 *
1063 * $Return: (char *) . . . . A pointer to the decoded string (same as input).
1064 *
1065 * $Errors: None
1066 *
1067 ****************************************************************************/
1068static int sendCgi(const char *url,
1069 const char *request, int bodyLen, const char *cookie,
1070 const char *content_type)
1071{
1072 int fromCgi[2]; /* pipe for reading data from CGI */
1073 int toCgi[2]; /* pipe for sending data to CGI */
1074
1075 static char * argp[] = { 0, 0 };
1076 int pid = 0;
1077 int inFd;
1078 int outFd;
1079 int firstLine = 1;
1080
1081 do {
1082 if (pipe(fromCgi) != 0) {
1083 break;
1084 }
1085 if (pipe(toCgi) != 0) {
1086 break;
1087 }
1088
1089 pid = fork();
1090 if (pid < 0) {
1091 pid = 0;
1092 break;
1093 }
1094
1095 if (!pid) {
1096 /* child process */
1097 char *script;
1098 char *purl = strdup( url );
1099 char realpath_buff[MAXPATHLEN];
1100
1101 if(purl == NULL)
1102 _exit(242);
1103
1104 inFd = toCgi[0];
1105 outFd = fromCgi[1];
1106
1107 dup2(inFd, 0); // replace stdin with the pipe
1108 dup2(outFd, 1); // replace stdout with the pipe
1109
1110#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1111 if (!config->debugHttpd)
1112#endif
1113 dup2(outFd, 2); // replace stderr with the pipe
1114
1115 close(toCgi[0]);
1116 close(toCgi[1]);
1117 close(fromCgi[0]);
1118 close(fromCgi[1]);
1119
1120 /*
1121 * Find PATH_INFO.
1122 */
1123 script = purl;
1124 while((script = strchr( script + 1, '/' )) != NULL) {
1125 /* have script.cgi/PATH_INFO or dirs/script.cgi[/PATH_INFO] */
1126 struct stat sb;
1127
1128 *script = '\0';
1129 if(is_directory(purl + 1, 1, &sb) == 0) {
1130 /* not directory, found script.cgi/PATH_INFO */
1131 *script = '/';
1132 break;
1133 }
1134 *script = '/'; /* is directory, find next '/' */
1135 }
1136 addEnv("PATH", "INFO", script); /* set /PATH_INFO or NULL */
1137 addEnv("PATH", "", getenv("PATH"));
1138 addEnv("REQUEST", "METHOD", request);
1139 if(config->query) {
1140 char *uri = alloca(strlen(purl) + 2 + strlen(config->query));
1141 if(uri)
1142 sprintf(uri, "%s?%s", purl, config->query);
1143 addEnv("REQUEST", "URI", uri);
1144 } else {
1145 addEnv("REQUEST", "URI", purl);
1146 }
1147 if(script != NULL)
1148 *script = '\0'; /* reduce /PATH_INFO */
1149 /* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
1150 addEnv("SCRIPT_NAME", "", purl);
1151 addEnv("QUERY_STRING", "", config->query);
1152 addEnv("SERVER", "SOFTWARE", httpdVersion);
1153 addEnv("SERVER", "PROTOCOL", "HTTP/1.0");
1154 addEnv("GATEWAY_INTERFACE", "", "CGI/1.1");
1155 addEnv("REMOTE", "ADDR", config->rmt_ip_str);
1156#ifdef CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
1157 addEnvPort("REMOTE");
1158#endif
1159 if(bodyLen) {
1160 char sbl[32];
1161
1162 sprintf(sbl, "%d", bodyLen);
1163 addEnv("CONTENT", "LENGTH", sbl);
1164 }
1165 if(cookie)
1166 addEnv("HTTP", "COOKIE", cookie);
1167 if(content_type)
1168 addEnv("CONTENT", "TYPE", content_type);
1169#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1170 if(config->remoteuser) {
1171 addEnv("REMOTE", "USER", config->remoteuser);
1172 addEnv("AUTH_TYPE", "", "Basic");
1173 }
1174#endif
1175 if(config->referer)
1176 addEnv("HTTP", "REFERER", config->referer);
1177
1178 /* set execve argp[0] without path */
1179 argp[0] = strrchr( purl, '/' ) + 1;
1180 /* but script argp[0] must have absolute path and chdiring to this */
1181 if(realpath(purl + 1, realpath_buff) != NULL) {
1182 script = strrchr(realpath_buff, '/');
1183 if(script) {
1184 *script = '\0';
1185 if(chdir(realpath_buff) == 0) {
1186 *script = '/';
1187 // now run the program. If it fails,
1188 // use _exit() so no destructors
1189 // get called and make a mess.
1190 execv(realpath_buff, argp);
1191 }
1192 }
1193 }
1194#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1195 config->accepted_socket = 1; /* send to stdout */
1196#endif
1197 sendHeaders(HTTP_NOT_FOUND);
1198 _exit(242);
1199 } /* end child */
1200
1201 } while (0);
1202
1203 if (pid) {
1204 /* parent process */
1205 int status;
1206 size_t post_readed_size = 0, post_readed_idx = 0;
1207
1208 inFd = fromCgi[0];
1209 outFd = toCgi[1];
1210 close(fromCgi[1]);
1211 close(toCgi[0]);
1212 signal(SIGPIPE, SIG_IGN);
1213
1214 while (1) {
1215 fd_set readSet;
1216 fd_set writeSet;
1217 char wbuf[128];
1218 int nfound;
1219 int count;
1220
1221 FD_ZERO(&readSet);
1222 FD_ZERO(&writeSet);
1223 FD_SET(inFd, &readSet);
1224 if(bodyLen > 0 || post_readed_size > 0) {
1225 FD_SET(outFd, &writeSet);
1226 nfound = outFd > inFd ? outFd : inFd;
1227 if(post_readed_size == 0) {
1228 FD_SET(a_c_r, &readSet);
1229 if(nfound < a_c_r)
1230 nfound = a_c_r;
1231 }
1232 /* Now wait on the set of sockets! */
1233 nfound = select(nfound + 1, &readSet, &writeSet, 0, NULL);
1234 } else {
1235 if(!bodyLen) {
1236 close(outFd);
1237 bodyLen = -1;
1238 }
1239 nfound = select(inFd + 1, &readSet, 0, 0, NULL);
1240 }
1241
1242 if (nfound <= 0) {
1243 if (waitpid(pid, &status, WNOHANG) > 0) {
1244 close(inFd);
1245#ifdef DEBUG
1246 if (config->debugHttpd) {
1247 if (WIFEXITED(status))
1248 bb_error_msg("piped has exited with status=%d", WEXITSTATUS(status));
1249 if (WIFSIGNALED(status))
1250 bb_error_msg("piped has exited with signal=%d", WTERMSIG(status));
1251 }
1252#endif
1253 break;
1254 }
1255 } else if(post_readed_size > 0 && FD_ISSET(outFd, &writeSet)) {
1256 count = bb_full_write(outFd, wbuf + post_readed_idx, post_readed_size);
1257 if(count > 0) {
1258 post_readed_size -= count;
1259 post_readed_idx += count;
1260 if(post_readed_size == 0)
1261 post_readed_idx = 0;
1262 }
1263 } else if(bodyLen > 0 && post_readed_size == 0 && FD_ISSET(a_c_r, &readSet)) {
1264 count = bodyLen > sizeof(wbuf) ? sizeof(wbuf) : bodyLen;
1265 count = safe_read(a_c_r, wbuf, count);
1266 if(count > 0) {
1267 post_readed_size += count;
1268 bodyLen -= count;
1269 } else {
1270 bodyLen = 0; /* closed */
1271 }
1272 }
1273 if(FD_ISSET(inFd, &readSet)) {
1274 int s = a_c_w;
1275 char *rbuf = config->buf;
1276
1277#ifndef PIPE_BUF
1278# define PIPESIZE 4096 /* amount of buffering in a pipe */
1279#else
1280# define PIPESIZE PIPE_BUF
1281#endif
1282#if PIPESIZE >= MAX_MEMORY_BUFF
1283# error "PIPESIZE >= MAX_MEMORY_BUFF"
1284#endif
1285
1286 // There is something to read
1287 count = safe_read(inFd, rbuf, PIPESIZE);
1288 if (count == 0)
1289 break; /* closed */
1290 if (count > 0) {
1291 if (firstLine) {
1292 rbuf[count] = 0;
1293 /* check to see if the user script added headers */
1294 if(strncmp(rbuf, "HTTP/1.0 200 OK\n", 4) != 0) {
1295 bb_full_write(s, "HTTP/1.0 200 OK\n", 16);
1296 }
1297 if (strstr(rbuf, "ontent-") == 0) {
1298 bb_full_write(s, "Content-type: text/plain\n\n", 26);
1299 }
1300 firstLine = 0;
1301 }
1302 if (bb_full_write(s, rbuf, count) != count)
1303 break;
1304
1305#ifdef DEBUG
1306 if (config->debugHttpd)
1307 fprintf(stderr, "cgi read %d bytes\n", count);
1308#endif
1309 }
1310 }
1311 }
1312 }
1313 return 0;
1314}
1315#endif /* CONFIG_FEATURE_HTTPD_CGI */
1316
1317/****************************************************************************
1318 *
1319 > $Function: sendFile()
1320 *
1321 * $Description: Send a file response to an HTTP request
1322 *
1323 * $Parameter:
1324 * (const char *) url . . The URL requested.
1325 *
1326 * $Return: (int) . . . . . . Always 0.
1327 *
1328 ****************************************************************************/
1329static int sendFile(const char *url)
1330{
1331 char * suffix;
1332 int f;
1333 const char * const * table;
1334 const char * try_suffix;
1335
1336 suffix = strrchr(url, '.');
1337
1338 for (table = suffixTable; *table; table += 2)
1339 if(suffix != NULL && (try_suffix = strstr(*table, suffix)) != 0) {
1340 try_suffix += strlen(suffix);
1341 if(*try_suffix == 0 || *try_suffix == '.')
1342 break;
1343 }
1344 /* also, if not found, set default as "application/octet-stream"; */
1345 config->httpd_found.found_mime_type = *(table+1);
1346#ifdef CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES
1347 if (suffix) {
1348 Htaccess * cur;
1349
1350 for (cur = config->mime_a; cur; cur = cur->next) {
1351 if(strcmp(cur->before_colon, suffix) == 0) {
1352 config->httpd_found.found_mime_type = cur->after_colon;
1353 break;
1354 }
1355 }
1356 }
1357#endif /* CONFIG_FEATURE_HTTPD_CONFIG_WITH_MIME_TYPES */
1358
1359#ifdef DEBUG
1360 if (config->debugHttpd)
1361 fprintf(stderr, "Sending file '%s' Content-type: %s\n",
1362 url, config->httpd_found.found_mime_type);
1363#endif
1364
1365 f = open(url, O_RDONLY);
1366 if (f >= 0) {
1367 int count;
1368 char *buf = config->buf;
1369
1370 sendHeaders(HTTP_OK);
1371 while ((count = bb_full_read(f, buf, MAX_MEMORY_BUFF)) > 0) {
1372 if (bb_full_write(a_c_w, buf, count) != count)
1373 break;
1374 }
1375 close(f);
1376 } else {
1377#ifdef DEBUG
1378 if (config->debugHttpd)
1379 bb_perror_msg("Unable to open '%s'", url);
1380#endif
1381 sendHeaders(HTTP_NOT_FOUND);
1382 }
1383
1384 return 0;
1385}
1386
1387static int checkPermIP(void)
1388{
1389 Htaccess_IP * cur;
1390
1391 /* This could stand some work */
1392 for (cur = config->ip_a_d; cur; cur = cur->next) {
1393#ifdef DEBUG
1394 if (config->debugHttpd) {
1395 fprintf(stderr, "checkPermIP: '%s' ? ", config->rmt_ip_str);
1396 fprintf(stderr, "'%u.%u.%u.%u/%u.%u.%u.%u'\n",
1397 (unsigned char)(cur->ip >> 24),
1398 (unsigned char)(cur->ip >> 16),
1399 (unsigned char)(cur->ip >> 8),
1400 cur->ip & 0xff,
1401 (unsigned char)(cur->mask >> 24),
1402 (unsigned char)(cur->mask >> 16),
1403 (unsigned char)(cur->mask >> 8),
1404 cur->mask & 0xff);
1405 }
1406#endif
1407 if((config->rmt_ip & cur->mask) == cur->ip)
1408 return cur->allow_deny == 'A'; /* Allow/Deny */
1409 }
1410
1411 /* if unconfigured, return 1 - access from all */
1412 return !config->flg_deny_all;
1413}
1414
1415/****************************************************************************
1416 *
1417 > $Function: checkPerm()
1418 *
1419 * $Description: Check the permission file for access password protected.
1420 *
1421 * If config file isn't present, everything is allowed.
1422 * Entries are of the form you can see example from header source
1423 *
1424 * $Parameters:
1425 * (const char *) path . . . . The file path.
1426 * (const char *) request . . . User information to validate.
1427 *
1428 * $Return: (int) . . . . . . . . . 1 if request OK, 0 otherwise.
1429 *
1430 ****************************************************************************/
1431
1432#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1433static int checkPerm(const char *path, const char *request)
1434{
1435 Htaccess * cur;
1436 const char *p;
1437 const char *p0;
1438
1439 const char *prev = NULL;
1440
1441 /* This could stand some work */
1442 for (cur = config->auth; cur; cur = cur->next) {
1443 p0 = cur->before_colon;
1444 if(prev != NULL && strcmp(prev, p0) != 0)
1445 continue; /* find next identical */
1446 p = cur->after_colon;
1447#ifdef DEBUG
1448 if (config->debugHttpd)
1449 fprintf(stderr,"checkPerm: '%s' ? '%s'\n", p0, request);
1450#endif
1451 {
1452 int l = strlen(p0);
1453
1454 if(strncmp(p0, path, l) == 0 &&
1455 (l == 1 || path[l] == '/' || path[l] == 0)) {
1456 char *u;
1457 /* path match found. Check request */
1458 /* for check next /path:user:password */
1459 prev = p0;
1460 u = strchr(request, ':');
1461 if(u == NULL) {
1462 /* bad request, ':' required */
1463 break;
1464 }
1465
1466#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
1467 {
1468 char *cipher;
1469 char *pp;
1470
1471 if(strncmp(p, request, u-request) != 0) {
1472 /* user uncompared */
1473 continue;
1474 }
1475 pp = strchr(p, ':');
1476 if(pp && pp[1] == '$' && pp[2] == '1' &&
1477 pp[3] == '$' && pp[4]) {
1478 pp++;
1479 cipher = pw_encrypt(u+1, pp);
1480 if (strcmp(cipher, pp) == 0)
1481 goto set_remoteuser_var; /* Ok */
1482 /* unauthorized */
1483 continue;
1484 }
1485 }
1486#endif
1487 if (strcmp(p, request) == 0) {
1488#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
1489set_remoteuser_var:
1490#endif
1491 config->remoteuser = strdup(request);
1492 if(config->remoteuser)
1493 config->remoteuser[(u - request)] = 0;
1494 return 1; /* Ok */
1495 }
1496 /* unauthorized */
1497 }
1498 }
1499 } /* for */
1500
1501 return prev == NULL;
1502}
1503
1504#endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
1505
1506/****************************************************************************
1507 *
1508 > $Function: handleIncoming()
1509 *
1510 * $Description: Handle an incoming http request.
1511 *
1512 ****************************************************************************/
1513
1514static void
1515handle_sigalrm( int sig )
1516{
1517 sendHeaders(HTTP_REQUEST_TIMEOUT);
1518 config->alarm_signaled = sig;
1519}
1520
1521/****************************************************************************
1522 *
1523 > $Function: handleIncoming()
1524 *
1525 * $Description: Handle an incoming http request.
1526 *
1527 ****************************************************************************/
1528static void handleIncoming(void)
1529{
1530 char *buf = config->buf;
1531 char *url;
1532 char *purl;
1533 int blank = -1;
1534 char *test;
1535 struct stat sb;
1536 int ip_allowed;
1537#ifdef CONFIG_FEATURE_HTTPD_CGI
1538 const char *prequest = request_GET;
1539 long length=0;
1540 char *cookie = 0;
1541 char *content_type = 0;
1542#endif
1543#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1544 fd_set s_fd;
1545 struct timeval tv;
1546 int retval;
1547#endif
1548 struct sigaction sa;
1549
1550#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1551 int credentials = -1; /* if not requred this is Ok */
1552#endif
1553
1554 sa.sa_handler = handle_sigalrm;
1555 sigemptyset(&sa.sa_mask);
1556 sa.sa_flags = 0; /* no SA_RESTART */
1557 sigaction(SIGALRM, &sa, NULL);
1558
1559 do {
1560 int count;
1561
1562 (void) alarm( TIMEOUT );
1563 if (getLine() <= 0)
1564 break; /* closed */
1565
1566 purl = strpbrk(buf, " \t");
1567 if(purl == NULL) {
1568BAD_REQUEST:
1569 sendHeaders(HTTP_BAD_REQUEST);
1570 break;
1571 }
1572 *purl = 0;
1573#ifdef CONFIG_FEATURE_HTTPD_CGI
1574 if(strcasecmp(buf, prequest) != 0) {
1575 prequest = "POST";
1576 if(strcasecmp(buf, prequest) != 0) {
1577 sendHeaders(HTTP_NOT_IMPLEMENTED);
1578 break;
1579 }
1580 }
1581#else
1582 if(strcasecmp(buf, request_GET) != 0) {
1583 sendHeaders(HTTP_NOT_IMPLEMENTED);
1584 break;
1585 }
1586#endif
1587 *purl = ' ';
1588 count = sscanf(purl, " %[^ ] HTTP/%d.%*d", buf, &blank);
1589
1590 decodeString(buf, 0);
1591 if (count < 1 || buf[0] != '/') {
1592 /* Garbled request/URL */
1593 goto BAD_REQUEST;
1594 }
1595 url = alloca(strlen(buf) + 12); /* + sizeof("/index.html\0") */
1596 if(url == NULL) {
1597 sendHeaders(HTTP_INTERNAL_SERVER_ERROR);
1598 break;
1599 }
1600 strcpy(url, buf);
1601 /* extract url args if present */
1602 test = strchr(url, '?');
1603 if (test) {
1604 *test++ = 0;
1605 config->query = test;
1606 }
1607
1608 /* algorithm stolen from libbb bb_simplify_path(),
1609 but don`t strdup and reducing trailing slash and protect out root */
1610 purl = test = url;
1611
1612 do {
1613 if (*purl == '/') {
1614 if (*test == '/') { /* skip duplicate (or initial) slash */
1615 continue;
1616 } else if (*test == '.') {
1617 if (test[1] == '/' || test[1] == 0) { /* skip extra '.' */
1618 continue;
1619 } else if ((test[1] == '.') && (test[2] == '/' || test[2] == 0)) {
1620 ++test;
1621 if (purl == url) {
1622 /* protect out root */
1623 goto BAD_REQUEST;
1624 }
1625 while (*--purl != '/'); /* omit previous dir */
1626 continue;
1627 }
1628 }
1629 }
1630 *++purl = *test;
1631 } while (*++test);
1632
1633 *++purl = 0; /* so keep last character */
1634 test = purl; /* end ptr */
1635
1636 /* If URL is directory, adding '/' */
1637 /* If URL is directory, adding '/' */
1638 if(test[-1] != '/') {
1639 if ( is_directory(url + 1, 1, &sb) ) {
1640 config->httpd_found.found_moved_temporarily = url;
1641 }
1642 }
1643#ifdef DEBUG
1644 if (config->debugHttpd)
1645 fprintf(stderr, "url='%s', args=%s\n", url, config->query);
1646#endif
1647
1648 test = url;
1649 ip_allowed = checkPermIP();
1650 while(ip_allowed && (test = strchr( test + 1, '/' )) != NULL) {
1651 /* have path1/path2 */
1652 *test = '\0';
1653 if( is_directory(url + 1, 1, &sb) ) {
1654 /* may be having subdir config */
1655 parse_conf(url + 1, SUBDIR_PARSE);
1656 ip_allowed = checkPermIP();
1657 }
1658 *test = '/';
1659 }
1660
1661 // read until blank line for HTTP version specified, else parse immediate
1662 while (blank >= 0 && alarm(TIMEOUT) >= 0 && (count = getLine()) > 0) {
1663
1664#ifdef DEBUG
1665 if (config->debugHttpd) fprintf(stderr, "Header: '%s'\n", buf);
1666#endif
1667
1668#ifdef CONFIG_FEATURE_HTTPD_CGI
1669 /* try and do our best to parse more lines */
1670 if ((strncasecmp(buf, Content_length, 15) == 0)) {
1671 if(prequest != request_GET)
1672 length = strtol(buf + 15, 0, 0); // extra read only for POST
1673 } else if ((strncasecmp(buf, "Cookie:", 7) == 0)) {
1674 for(test = buf + 7; isspace(*test); test++)
1675 ;
1676 cookie = strdup(test);
1677 } else if ((strncasecmp(buf, "Content-Type:", 13) == 0)) {
1678 for(test = buf + 13; isspace(*test); test++)
1679 ;
1680 content_type = strdup(test);
1681 } else if ((strncasecmp(buf, "Referer:", 8) == 0)) {
1682 for(test = buf + 8; isspace(*test); test++)
1683 ;
1684 config->referer = strdup(test);
1685 }
1686#endif
1687
1688#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1689 if (strncasecmp(buf, "Authorization:", 14) == 0) {
1690 /* We only allow Basic credentials.
1691 * It shows up as "Authorization: Basic <userid:password>" where
1692 * the userid:password is base64 encoded.
1693 */
1694 for(test = buf + 14; isspace(*test); test++)
1695 ;
1696 if (strncasecmp(test, "Basic", 5) != 0)
1697 continue;
1698
1699 test += 5; /* decodeBase64() skiping space self */
1700 decodeBase64(test);
1701 credentials = checkPerm(url, test);
1702 }
1703#endif /* CONFIG_FEATURE_HTTPD_BASIC_AUTH */
1704
1705 } /* while extra header reading */
1706
1707 (void) alarm( 0 );
1708 if(config->alarm_signaled)
1709 break;
1710
1711 if (strcmp(strrchr(url, '/') + 1, httpd_conf) == 0 || ip_allowed == 0) {
1712 /* protect listing [/path]/httpd_conf or IP deny */
1713#ifdef CONFIG_FEATURE_HTTPD_CGI
1714FORBIDDEN: /* protect listing /cgi-bin */
1715#endif
1716 sendHeaders(HTTP_FORBIDDEN);
1717 break;
1718 }
1719
1720#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1721 if (credentials <= 0 && checkPerm(url, ":") == 0) {
1722 sendHeaders(HTTP_UNAUTHORIZED);
1723 break;
1724 }
1725#endif
1726
1727 if(config->httpd_found.found_moved_temporarily) {
1728 sendHeaders(HTTP_MOVED_TEMPORARILY);
1729#ifdef DEBUG
1730 /* clear unforked memory flag */
1731 if(config->debugHttpd)
1732 config->httpd_found.found_moved_temporarily = NULL;
1733#endif
1734 break;
1735 }
1736
1737 test = url + 1; /* skip first '/' */
1738
1739#ifdef CONFIG_FEATURE_HTTPD_CGI
1740 /* if strange Content-Length */
1741 if (length < 0)
1742 break;
1743
1744 if (strncmp(test, "cgi-bin", 7) == 0) {
1745 if(test[7] == '/' && test[8] == 0)
1746 goto FORBIDDEN; // protect listing cgi-bin/
1747 sendCgi(url, prequest, length, cookie, content_type);
1748 } else {
1749 if (prequest != request_GET)
1750 sendHeaders(HTTP_NOT_IMPLEMENTED);
1751 else {
1752#endif /* CONFIG_FEATURE_HTTPD_CGI */
1753 if(purl[-1] == '/')
1754 strcpy(purl, "index.html");
1755 if ( stat(test, &sb ) == 0 ) {
1756 config->ContentLength = sb.st_size;
1757 config->last_mod = sb.st_mtime;
1758 }
1759 sendFile(test);
1760#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1761 /* unset if non inetd looped */
1762 config->ContentLength = -1;
1763#endif
1764
1765#ifdef CONFIG_FEATURE_HTTPD_CGI
1766 }
1767 }
1768#endif
1769
1770 } while (0);
1771
1772
1773#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1774/* from inetd don`t looping: freeing, closing automatic from exit always */
1775# ifdef DEBUG
1776 if (config->debugHttpd) fprintf(stderr, "closing socket\n");
1777# endif
1778# ifdef CONFIG_FEATURE_HTTPD_CGI
1779 free(cookie);
1780 free(content_type);
1781 free(config->referer);
1782#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1783 free(config->remoteuser);
1784#endif
1785# endif
1786 shutdown(a_c_w, SHUT_WR);
1787
1788 /* Properly wait for remote to closed */
1789 FD_ZERO (&s_fd) ;
1790 FD_SET (a_c_w, &s_fd) ;
1791
1792 do {
1793 tv.tv_sec = 2 ;
1794 tv.tv_usec = 0 ;
1795 retval = select (a_c_w + 1, &s_fd, NULL, NULL, &tv);
1796 } while (retval > 0 && (read (a_c_w, buf, sizeof (config->buf)) > 0));
1797
1798 shutdown(a_c_r, SHUT_RD);
1799 close(config->accepted_socket);
1800#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
1801}
1802
1803/****************************************************************************
1804 *
1805 > $Function: miniHttpd()
1806 *
1807 * $Description: The main http server function.
1808 *
1809 * Given an open socket fildes, listen for new connections and farm out
1810 * the processing as a forked process.
1811 *
1812 * $Parameters:
1813 * (int) server. . . The server socket fildes.
1814 *
1815 * $Return: (int) . . . . Always 0.
1816 *
1817 ****************************************************************************/
1818#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1819static int miniHttpd(int server)
1820{
1821 fd_set readfd, portfd;
1822
1823 FD_ZERO(&portfd);
1824 FD_SET(server, &portfd);
1825
1826 /* copy the ports we are watching to the readfd set */
1827 while (1) {
1828 readfd = portfd;
1829
1830 /* Now wait INDEFINITELY on the set of sockets! */
1831 if (select(server + 1, &readfd, 0, 0, 0) > 0) {
1832 if (FD_ISSET(server, &readfd)) {
1833 int on;
1834 struct sockaddr_in fromAddr;
1835
1836 socklen_t fromAddrLen = sizeof(fromAddr);
1837 int s = accept(server,
1838 (struct sockaddr *)&fromAddr, &fromAddrLen);
1839
1840 if (s < 0) {
1841 continue;
1842 }
1843 config->accepted_socket = s;
1844 config->rmt_ip = ntohl(fromAddr.sin_addr.s_addr);
1845#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG)
1846 sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
1847 (unsigned char)(config->rmt_ip >> 24),
1848 (unsigned char)(config->rmt_ip >> 16),
1849 (unsigned char)(config->rmt_ip >> 8),
1850 config->rmt_ip & 0xff);
1851 config->port = ntohs(fromAddr.sin_port);
1852#ifdef DEBUG
1853 if (config->debugHttpd) {
1854 bb_error_msg("connection from IP=%s, port %u\n",
1855 config->rmt_ip_str, config->port);
1856 }
1857#endif
1858#endif /* CONFIG_FEATURE_HTTPD_CGI */
1859
1860 /* set the KEEPALIVE option to cull dead connections */
1861 on = 1;
1862 setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof (on));
1863
1864 if (config->debugHttpd || fork() == 0) {
1865 /* This is the spawned thread */
1866#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
1867 /* protect reload config, may be confuse checking */
1868 signal(SIGHUP, SIG_IGN);
1869#endif
1870 handleIncoming();
1871 if(!config->debugHttpd)
1872 exit(0);
1873 }
1874 close(s);
1875 }
1876 }
1877 } // while (1)
1878 return 0;
1879}
1880
1881#else
1882 /* from inetd */
1883
1884static int miniHttpd(void)
1885{
1886 struct sockaddr_in fromAddrLen;
1887 socklen_t sinlen = sizeof (struct sockaddr_in);
1888
1889 getpeername (0, (struct sockaddr *)&fromAddrLen, &sinlen);
1890 config->rmt_ip = ntohl(fromAddrLen.sin_addr.s_addr);
1891#if defined(CONFIG_FEATURE_HTTPD_CGI) || defined(DEBUG)
1892 sprintf(config->rmt_ip_str, "%u.%u.%u.%u",
1893 (unsigned char)(config->rmt_ip >> 24),
1894 (unsigned char)(config->rmt_ip >> 16),
1895 (unsigned char)(config->rmt_ip >> 8),
1896 config->rmt_ip & 0xff);
1897#endif
1898 config->port = ntohs(fromAddrLen.sin_port);
1899 handleIncoming();
1900 return 0;
1901}
1902#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
1903
1904#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
1905static void sighup_handler(int sig)
1906{
1907 /* set and reset */
1908 struct sigaction sa;
1909
1910 parse_conf(default_path_httpd_conf,
1911 sig == SIGHUP ? SIGNALED_PARSE : FIRST_PARSE);
1912 sa.sa_handler = sighup_handler;
1913 sigemptyset(&sa.sa_mask);
1914 sa.sa_flags = SA_RESTART;
1915 sigaction(SIGHUP, &sa, NULL);
1916}
1917#endif
1918
1919
1920static const char httpd_opts[]="c:d:h:"
1921#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
1922 "e:"
1923#define OPT_INC_1 1
1924#else
1925#define OPT_INC_1 0
1926#endif
1927#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1928 "r:"
1929# ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
1930 "m:"
1931# define OPT_INC_2 2
1932# else
1933# define OPT_INC_2 1
1934#endif
1935#else
1936#define OPT_INC_2 0
1937#endif
1938#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1939 "p:v"
1940#ifdef CONFIG_FEATURE_HTTPD_SETUID
1941 "u:"
1942#endif
1943#endif /* CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY */
1944 ;
1945
1946#define OPT_CONFIG_FILE (1<<0)
1947#define OPT_DECODE_URL (1<<1)
1948#define OPT_HOME_HTTPD (1<<2)
1949#define OPT_ENCODE_URL (1<<(2+OPT_INC_1))
1950#define OPT_REALM (1<<(3+OPT_INC_1))
1951#define OPT_MD5 (1<<(4+OPT_INC_1))
1952#define OPT_PORT (1<<(3+OPT_INC_1+OPT_INC_2))
1953#define OPT_DEBUG (1<<(4+OPT_INC_1+OPT_INC_2))
1954#define OPT_SETUID (1<<(5+OPT_INC_1+OPT_INC_2))
1955
1956
1957#ifdef HTTPD_STANDALONE
1958int main(int argc, char *argv[])
1959#else
1960int httpd_main(int argc, char *argv[])
1961#endif
1962{
1963 unsigned long opt;
1964 const char *home_httpd = home;
1965 char *url_for_decode;
1966#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
1967 const char *url_for_encode;
1968#endif
1969#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1970 const char *s_port;
1971#endif
1972
1973#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1974 int server;
1975#endif
1976
1977#ifdef CONFIG_FEATURE_HTTPD_SETUID
1978 const char *s_uid;
1979 long uid = -1;
1980#endif
1981
1982#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
1983 const char *pass;
1984#endif
1985
1986 config = xcalloc(1, sizeof(*config));
1987#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
1988 config->realm = "Web Server Authentication";
1989#endif
1990
1991#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
1992 config->port = 80;
1993#endif
1994
1995 config->ContentLength = -1;
1996
1997 opt = bb_getopt_ulflags(argc, argv, httpd_opts,
1998 &(config->configFile), &url_for_decode, &home_httpd
1999#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
2000 , &url_for_encode
2001#endif
2002#ifdef CONFIG_FEATURE_HTTPD_BASIC_AUTH
2003 , &(config->realm)
2004# ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
2005 , &pass
2006# endif
2007#endif
2008#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
2009 , &s_port
2010#ifdef CONFIG_FEATURE_HTTPD_SETUID
2011 , &s_uid
2012#endif
2013#endif
2014 );
2015
2016 if(opt & OPT_DECODE_URL) {
2017 printf("%s", decodeString(url_for_decode, 1));
2018 return 0;
2019 }
2020#ifdef CONFIG_FEATURE_HTTPD_ENCODE_URL_STR
2021 if(opt & OPT_ENCODE_URL) {
2022 printf("%s", encodeString(url_for_encode));
2023 return 0;
2024 }
2025#endif
2026#ifdef CONFIG_FEATURE_HTTPD_AUTH_MD5
2027 if(opt & OPT_MD5) {
2028 printf("%s\n", pw_encrypt(pass, "$1$"));
2029 return 0;
2030 }
2031#endif
2032#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
2033 if(opt & OPT_PORT)
2034 config->port = bb_xgetlarg(s_port, 10, 1, 0xffff);
2035 config->debugHttpd = opt & OPT_DEBUG;
2036#ifdef CONFIG_FEATURE_HTTPD_SETUID
2037 if(opt & OPT_SETUID) {
2038 char *e;
2039
2040 uid = strtol(s_uid, &e, 0);
2041 if(*e != '\0') {
2042 /* not integer */
2043 uid = my_getpwnam(s_uid);
2044 }
2045 }
2046#endif
2047#endif
2048
2049 if(chdir(home_httpd)) {
2050 bb_perror_msg_and_die("can`t chdir to %s", home_httpd);
2051 }
2052#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
2053 server = openServer();
2054# ifdef CONFIG_FEATURE_HTTPD_SETUID
2055 /* drop privileges */
2056 if(uid > 0)
2057 setuid(uid);
2058# endif
2059#endif
2060
2061#ifdef CONFIG_FEATURE_HTTPD_CGI
2062 {
2063 char *p = getenv("PATH");
2064 if(p) {
2065 p = bb_xstrdup(p);
2066 }
2067 clearenv();
2068 if(p)
2069 setenv("PATH", p, 1);
2070# ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
2071 addEnvPort("SERVER");
2072# endif
2073 }
2074#endif
2075
2076#ifdef CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP
2077 sighup_handler(0);
2078#else
2079 parse_conf(default_path_httpd_conf, FIRST_PARSE);
2080#endif
2081
2082#ifndef CONFIG_FEATURE_HTTPD_USAGE_FROM_INETD_ONLY
2083 if (!config->debugHttpd) {
2084 if (daemon(1, 0) < 0) /* don`t change curent directory */
2085 bb_perror_msg_and_die("daemon");
2086 }
2087 return miniHttpd(server);
2088#else
2089 return miniHttpd();
2090#endif
2091}
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 @@
1/* ifconfig
2 *
3 * Similar to the standard Unix ifconfig, but with only the necessary
4 * parts for AF_INET, and without any printing of if info (for now).
5 *
6 * Bjorn Wesen, Axis Communications AB
7 *
8 *
9 * Authors of the original ifconfig was:
10 * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
11 *
12 * This program is free software; you can redistribute it
13 * and/or modify it under the terms of the GNU General
14 * Public License as published by the Free Software
15 * Foundation; either version 2 of the License, or (at
16 * your option) any later version.
17 *
18 * $Id: ifconfig.c,v 1.30 2004/03/31 11:30:08 andersen Exp $
19 *
20 */
21
22/*
23 * Heavily modified by Manuel Novoa III Mar 6, 2001
24 *
25 * From initial port to busybox, removed most of the redundancy by
26 * converting to a table-driven approach. Added several (optional)
27 * args missing from initial port.
28 *
29 * Still missing: media, tunnel.
30 *
31 * 2002-04-20
32 * IPV6 support added by Bart Visscher <magick@linux-fan.com>
33 */
34
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h> /* strcmp and friends */
38#include <ctype.h> /* isdigit and friends */
39#include <stddef.h> /* offsetof */
40#include <netdb.h>
41#include <sys/ioctl.h>
42#include <net/if.h>
43#include <net/if_arp.h>
44#include <netinet/in.h>
45#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
46#include <netpacket/packet.h>
47#include <net/ethernet.h>
48#else
49#include <asm/types.h>
50#include <linux/if_ether.h>
51#endif
52#include "inet_common.h"
53#include "busybox.h"
54
55#ifdef CONFIG_FEATURE_IFCONFIG_SLIP
56# include <net/if_slip.h>
57#endif
58
59/* I don't know if this is needed for busybox or not. Anyone? */
60#define QUESTIONABLE_ALIAS_CASE
61
62
63/* Defines for glibc2.0 users. */
64#ifndef SIOCSIFTXQLEN
65# define SIOCSIFTXQLEN 0x8943
66# define SIOCGIFTXQLEN 0x8942
67#endif
68
69/* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
70#ifndef ifr_qlen
71# define ifr_qlen ifr_ifru.ifru_mtu
72#endif
73
74#ifndef IFF_DYNAMIC
75# define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses */
76#endif
77
78#ifdef CONFIG_FEATURE_IPV6
79struct in6_ifreq {
80 struct in6_addr ifr6_addr;
81 uint32_t ifr6_prefixlen;
82 int ifr6_ifindex;
83};
84#endif
85
86/*
87 * Here are the bit masks for the "flags" member of struct options below.
88 * N_ signifies no arg prefix; M_ signifies arg prefixed by '-'.
89 * CLR clears the flag; SET sets the flag; ARG signifies (optional) arg.
90 */
91#define N_CLR 0x01
92#define M_CLR 0x02
93#define N_SET 0x04
94#define M_SET 0x08
95#define N_ARG 0x10
96#define M_ARG 0x20
97
98#define M_MASK (M_CLR | M_SET | M_ARG)
99#define N_MASK (N_CLR | N_SET | N_ARG)
100#define SET_MASK (N_SET | M_SET)
101#define CLR_MASK (N_CLR | M_CLR)
102#define SET_CLR_MASK (SET_MASK | CLR_MASK)
103#define ARG_MASK (M_ARG | N_ARG)
104
105/*
106 * Here are the bit masks for the "arg_flags" member of struct options below.
107 */
108
109/*
110 * cast type:
111 * 00 int
112 * 01 char *
113 * 02 HOST_COPY in_ether
114 * 03 HOST_COPY INET_resolve
115 */
116#define A_CAST_TYPE 0x03
117/*
118 * map type:
119 * 00 not a map type (mem_start, io_addr, irq)
120 * 04 memstart (unsigned long)
121 * 08 io_addr (unsigned short)
122 * 0C irq (unsigned char)
123 */
124#define A_MAP_TYPE 0x0C
125#define A_ARG_REQ 0x10 /* Set if an arg is required. */
126#define A_NETMASK 0x20 /* Set if netmask (check for multiple sets). */
127#define A_SET_AFTER 0x40 /* Set a flag at the end. */
128#define A_COLON_CHK 0x80 /* Is this needed? See below. */
129#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
130#define A_HOSTNAME 0x100 /* Set if it is ip addr. */
131#define A_BROADCAST 0x200 /* Set if it is broadcast addr. */
132#else
133#define A_HOSTNAME 0
134#define A_BROADCAST 0
135#endif
136
137/*
138 * These defines are for dealing with the A_CAST_TYPE field.
139 */
140#define A_CAST_CHAR_PTR 0x01
141#define A_CAST_RESOLVE 0x01
142#define A_CAST_HOST_COPY 0x02
143#define A_CAST_HOST_COPY_IN_ETHER A_CAST_HOST_COPY
144#define A_CAST_HOST_COPY_RESOLVE (A_CAST_HOST_COPY | A_CAST_RESOLVE)
145
146/*
147 * These defines are for dealing with the A_MAP_TYPE field.
148 */
149#define A_MAP_ULONG 0x04 /* memstart */
150#define A_MAP_USHORT 0x08 /* io_addr */
151#define A_MAP_UCHAR 0x0C /* irq */
152
153/*
154 * Define the bit masks signifying which operations to perform for each arg.
155 */
156
157#define ARG_METRIC (A_ARG_REQ /*| A_CAST_INT*/)
158#define ARG_MTU (A_ARG_REQ /*| A_CAST_INT*/)
159#define ARG_TXQUEUELEN (A_ARG_REQ /*| A_CAST_INT*/)
160#define ARG_MEM_START (A_ARG_REQ | A_MAP_ULONG)
161#define ARG_IO_ADDR (A_ARG_REQ | A_MAP_ULONG)
162#define ARG_IRQ (A_ARG_REQ | A_MAP_UCHAR)
163#define ARG_DSTADDR (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE)
164#define ARG_NETMASK (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_NETMASK)
165#define ARG_BROADCAST (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_BROADCAST)
166#define ARG_HW (A_ARG_REQ | A_CAST_HOST_COPY_IN_ETHER)
167#define ARG_POINTOPOINT (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
168#define ARG_KEEPALIVE (A_ARG_REQ | A_CAST_CHAR_PTR)
169#define ARG_OUTFILL (A_ARG_REQ | A_CAST_CHAR_PTR)
170#define ARG_HOSTNAME (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_COLON_CHK | A_HOSTNAME)
171#define ARG_ADD_DEL (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
172
173
174/*
175 * Set up the tables. Warning! They must have corresponding order!
176 */
177
178struct arg1opt {
179 const char *name;
180 unsigned short selector;
181 unsigned short ifr_offset;
182};
183
184struct options {
185 const char *name;
186#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
187 const unsigned int flags:6;
188 const unsigned int arg_flags:10;
189#else
190 const unsigned char flags;
191 const unsigned char arg_flags;
192#endif
193 const unsigned short selector;
194};
195
196#define ifreq_offsetof(x) offsetof(struct ifreq, x)
197
198static const struct arg1opt Arg1Opt[] = {
199 {"SIOCSIFMETRIC", SIOCSIFMETRIC, ifreq_offsetof(ifr_metric)},
200 {"SIOCSIFMTU", SIOCSIFMTU, ifreq_offsetof(ifr_mtu)},
201 {"SIOCSIFTXQLEN", SIOCSIFTXQLEN, ifreq_offsetof(ifr_qlen)},
202 {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)},
203 {"SIOCSIFNETMASK", SIOCSIFNETMASK, ifreq_offsetof(ifr_netmask)},
204 {"SIOCSIFBRDADDR", SIOCSIFBRDADDR, ifreq_offsetof(ifr_broadaddr)},
205#ifdef CONFIG_FEATURE_IFCONFIG_HW
206 {"SIOCSIFHWADDR", SIOCSIFHWADDR, ifreq_offsetof(ifr_hwaddr)},
207#endif
208 {"SIOCSIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr)},
209#ifdef SIOCSKEEPALIVE
210 {"SIOCSKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data)},
211#endif
212#ifdef SIOCSOUTFILL
213 {"SIOCSOUTFILL", SIOCSOUTFILL, ifreq_offsetof(ifr_data)},
214#endif
215#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
216 {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.mem_start)},
217 {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.base_addr)},
218 {"SIOCSIFMAP", SIOCSIFMAP, ifreq_offsetof(ifr_map.irq)},
219#endif
220 /* Last entry if for unmatched (possibly hostname) arg. */
221#ifdef CONFIG_FEATURE_IPV6
222 {"SIOCSIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */
223 {"SIOCDIFADDR", SIOCDIFADDR, ifreq_offsetof(ifr_addr)}, /* IPv6 version ignores the offset */
224#endif
225 {"SIOCSIFADDR", SIOCSIFADDR, ifreq_offsetof(ifr_addr)},
226};
227
228static const struct options OptArray[] = {
229 {"metric", N_ARG, ARG_METRIC, 0},
230 {"mtu", N_ARG, ARG_MTU, 0},
231 {"txqueuelen", N_ARG, ARG_TXQUEUELEN, 0},
232 {"dstaddr", N_ARG, ARG_DSTADDR, 0},
233 {"netmask", N_ARG, ARG_NETMASK, 0},
234 {"broadcast", N_ARG | M_CLR, ARG_BROADCAST, IFF_BROADCAST},
235#ifdef CONFIG_FEATURE_IFCONFIG_HW
236 {"hw", N_ARG, ARG_HW, 0},
237#endif
238 {"pointopoint", N_ARG | M_CLR, ARG_POINTOPOINT, IFF_POINTOPOINT},
239#ifdef SIOCSKEEPALIVE
240 {"keepalive", N_ARG, ARG_KEEPALIVE, 0},
241#endif
242#ifdef SIOCSOUTFILL
243 {"outfill", N_ARG, ARG_OUTFILL, 0},
244#endif
245#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
246 {"mem_start", N_ARG, ARG_MEM_START, 0},
247 {"io_addr", N_ARG, ARG_IO_ADDR, 0},
248 {"irq", N_ARG, ARG_IRQ, 0},
249#endif
250#ifdef CONFIG_FEATURE_IPV6
251 {"add", N_ARG, ARG_ADD_DEL, 0},
252 {"del", N_ARG, ARG_ADD_DEL, 0},
253#endif
254 {"arp", N_CLR | M_SET, 0, IFF_NOARP},
255 {"trailers", N_CLR | M_SET, 0, IFF_NOTRAILERS},
256 {"promisc", N_SET | M_CLR, 0, IFF_PROMISC},
257 {"multicast", N_SET | M_CLR, 0, IFF_MULTICAST},
258 {"allmulti", N_SET | M_CLR, 0, IFF_ALLMULTI},
259 {"dynamic", N_SET | M_CLR, 0, IFF_DYNAMIC},
260 {"up", N_SET, 0, (IFF_UP | IFF_RUNNING)},
261 {"down", N_CLR, 0, IFF_UP},
262 {NULL, 0, ARG_HOSTNAME, (IFF_UP | IFF_RUNNING)}
263};
264
265/*
266 * A couple of prototypes.
267 */
268
269#ifdef CONFIG_FEATURE_IFCONFIG_HW
270static int in_ether(char *bufp, struct sockaddr *sap);
271#endif
272
273#ifdef CONFIG_FEATURE_IFCONFIG_STATUS
274extern int interface_opt_a;
275extern int display_interfaces(char *ifname);
276#endif
277
278/*
279 * Our main function.
280 */
281
282int ifconfig_main(int argc, char **argv)
283{
284 struct ifreq ifr;
285 struct sockaddr_in sai;
286#ifdef CONFIG_FEATURE_IPV6
287 struct sockaddr_in6 sai6;
288#endif
289#ifdef CONFIG_FEATURE_IFCONFIG_HW
290 struct sockaddr sa;
291#endif
292 const struct arg1opt *a1op;
293 const struct options *op;
294 int sockfd; /* socket fd we use to manipulate stuff with */
295 int goterr;
296 int selector;
297#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
298 unsigned int mask;
299 unsigned int did_flags;
300 unsigned int sai_hostname, sai_netmask;
301#else
302 unsigned char mask;
303 unsigned char did_flags;
304#endif
305 char *p;
306 char host[128];
307
308 goterr = 0;
309 did_flags = 0;
310#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
311 sai_hostname = 0;
312 sai_netmask = 0;
313#endif
314
315 /* skip argv[0] */
316 ++argv;
317 --argc;
318
319#ifdef CONFIG_FEATURE_IFCONFIG_STATUS
320 if ((argc > 0) && (((*argv)[0] == '-') && ((*argv)[1] == 'a') && !(*argv)[2])) {
321 interface_opt_a = 1;
322 --argc;
323 ++argv;
324 }
325#endif
326
327 if (argc <= 1) {
328#ifdef CONFIG_FEATURE_IFCONFIG_STATUS
329 return display_interfaces(argc ? *argv : NULL);
330#else
331 bb_error_msg_and_die
332 ("ifconfig was not compiled with interface status display support.");
333#endif
334 }
335
336 /* Create a channel to the NET kernel. */
337 if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
338 bb_perror_msg_and_die("socket");
339 }
340
341 /* get interface name */
342 safe_strncpy(ifr.ifr_name, *argv, IFNAMSIZ);
343
344 /* Process the remaining arguments. */
345 while (*++argv != (char *) NULL) {
346 p = *argv;
347 mask = N_MASK;
348 if (*p == '-') { /* If the arg starts with '-'... */
349 ++p; /* advance past it and */
350 mask = M_MASK; /* set the appropriate mask. */
351 }
352 for (op = OptArray; op->name; op++) { /* Find table entry. */
353 if (strcmp(p, op->name) == 0) { /* If name matches... */
354 if ((mask &= op->flags)) { /* set the mask and go. */
355 goto FOUND_ARG;;
356 }
357 /* If we get here, there was a valid arg with an */
358 /* invalid '-' prefix. */
359 ++goterr;
360 goto LOOP;
361 }
362 }
363
364 /* We fell through, so treat as possible hostname. */
365 a1op = Arg1Opt + (sizeof(Arg1Opt) / sizeof(Arg1Opt[0])) - 1;
366 mask = op->arg_flags;
367 goto HOSTNAME;
368
369 FOUND_ARG:
370 if (mask & ARG_MASK) {
371 mask = op->arg_flags;
372 a1op = Arg1Opt + (op - OptArray);
373 if (mask & A_NETMASK & did_flags) {
374 bb_show_usage();
375 }
376 if (*++argv == NULL) {
377 if (mask & A_ARG_REQ) {
378 bb_show_usage();
379 } else {
380 --argv;
381 mask &= A_SET_AFTER; /* just for broadcast */
382 }
383 } else { /* got an arg so process it */
384 HOSTNAME:
385 did_flags |= (mask & (A_NETMASK|A_HOSTNAME));
386 if (mask & A_CAST_HOST_COPY) {
387#ifdef CONFIG_FEATURE_IFCONFIG_HW
388 if (mask & A_CAST_RESOLVE) {
389#endif
390#ifdef CONFIG_FEATURE_IPV6
391 char *prefix;
392 int prefix_len = 0;
393#endif
394
395 safe_strncpy(host, *argv, (sizeof host));
396#ifdef CONFIG_FEATURE_IPV6
397 if ((prefix = strchr(host, '/'))) {
398 if (safe_strtoi(prefix + 1, &prefix_len) ||
399 (prefix_len < 0) || (prefix_len > 128))
400 {
401 ++goterr;
402 goto LOOP;
403 }
404 *prefix = 0;
405 }
406#endif
407
408 sai.sin_family = AF_INET;
409 sai.sin_port = 0;
410 if (!strcmp(host, bb_INET_default)) {
411 /* Default is special, meaning 0.0.0.0. */
412 sai.sin_addr.s_addr = INADDR_ANY;
413#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
414 } else if (((host[0] == '+') && !host[1]) && (mask & A_BROADCAST) &&
415 (did_flags & (A_NETMASK|A_HOSTNAME)) == (A_NETMASK|A_HOSTNAME)) {
416 /* + is special, meaning broadcast is derived. */
417 sai.sin_addr.s_addr = (~sai_netmask) | (sai_hostname & sai_netmask);
418#endif
419#ifdef CONFIG_FEATURE_IPV6
420 } else if (inet_pton(AF_INET6, host, &sai6.sin6_addr) > 0) {
421 int sockfd6;
422 struct in6_ifreq ifr6;
423
424 memcpy((char *) &ifr6.ifr6_addr,
425 (char *) &sai6.sin6_addr,
426 sizeof(struct in6_addr));
427
428 /* Create a channel to the NET kernel. */
429 if ((sockfd6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
430 bb_perror_msg_and_die("socket6");
431 }
432 if (ioctl(sockfd6, SIOGIFINDEX, &ifr) < 0) {
433 perror("SIOGIFINDEX");
434 ++goterr;
435 continue;
436 }
437 ifr6.ifr6_ifindex = ifr.ifr_ifindex;
438 ifr6.ifr6_prefixlen = prefix_len;
439 if (ioctl(sockfd6, a1op->selector, &ifr6) < 0) {
440 perror(a1op->name);
441 ++goterr;
442 }
443 continue;
444#endif
445 } else if (inet_aton(host, &sai.sin_addr) == 0) {
446 /* It's not a dotted quad. */
447 struct hostent *hp;
448 if ((hp = gethostbyname(host)) == (struct hostent *)NULL) {
449 ++goterr;
450 continue;
451 }
452 memcpy((char *) &sai.sin_addr, (char *) hp->h_addr_list[0],
453 sizeof(struct in_addr));
454 }
455#ifdef CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS
456 if (mask & A_HOSTNAME) {
457 sai_hostname = sai.sin_addr.s_addr;
458 }
459 if (mask & A_NETMASK) {
460 sai_netmask = sai.sin_addr.s_addr;
461 }
462#endif
463 p = (char *) &sai;
464#ifdef CONFIG_FEATURE_IFCONFIG_HW
465 } else { /* A_CAST_HOST_COPY_IN_ETHER */
466 /* This is the "hw" arg case. */
467 if (strcmp("ether", *argv) || (*++argv == NULL)) {
468 bb_show_usage();
469 }
470 safe_strncpy(host, *argv, (sizeof host));
471 if (in_ether(host, &sa)) {
472 bb_error_msg("invalid hw-addr %s", host);
473 ++goterr;
474 continue;
475 }
476 p = (char *) &sa;
477 }
478#endif
479 memcpy((((char *) (&ifr)) + a1op->ifr_offset),
480 p, sizeof(struct sockaddr));
481 } else {
482 unsigned int i = strtoul(*argv, NULL, 0);
483
484 p = ((char *) (&ifr)) + a1op->ifr_offset;
485#ifdef CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
486 if (mask & A_MAP_TYPE) {
487 if (ioctl(sockfd, SIOCGIFMAP, &ifr) < 0) {
488 ++goterr;
489 continue;
490 }
491 if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR) {
492 *((unsigned char *) p) = i;
493 } else if (mask & A_MAP_USHORT) {
494 *((unsigned short *) p) = i;
495 } else {
496 *((unsigned long *) p) = i;
497 }
498 } else
499#endif
500 if (mask & A_CAST_CHAR_PTR) {
501 *((caddr_t *) p) = (caddr_t) i;
502 } else { /* A_CAST_INT */
503 *((int *) p) = i;
504 }
505 }
506
507 if (ioctl(sockfd, a1op->selector, &ifr) < 0) {
508 perror(a1op->name);
509 ++goterr;
510 continue;
511 }
512#ifdef QUESTIONABLE_ALIAS_CASE
513 if (mask & A_COLON_CHK) {
514 /*
515 * Don't do the set_flag() if the address is an alias with
516 * a - at the end, since it's deleted already! - Roman
517 *
518 * Should really use regex.h here, not sure though how well
519 * it'll go with the cross-platform support etc.
520 */
521 char *ptr;
522 short int found_colon = 0;
523
524 for (ptr = ifr.ifr_name; *ptr; ptr++) {
525 if (*ptr == ':') {
526 found_colon++;
527 }
528 }
529
530 if (found_colon && *(ptr - 1) == '-') {
531 continue;
532 }
533 }
534#endif
535 }
536 if (!(mask & A_SET_AFTER)) {
537 continue;
538 }
539 mask = N_SET;
540 }
541
542 if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
543 perror("SIOCGIFFLAGS");
544 ++goterr;
545 } else {
546 selector = op->selector;
547 if (mask & SET_MASK) {
548 ifr.ifr_flags |= selector;
549 } else {
550 ifr.ifr_flags &= ~selector;
551 }
552 if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
553 perror("SIOCSIFFLAGS");
554 ++goterr;
555 }
556 }
557 LOOP:
558 continue;
559 } /* end of while-loop */
560
561 return goterr;
562}
563
564#ifdef CONFIG_FEATURE_IFCONFIG_HW
565/* Input an Ethernet address and convert to binary. */
566static int in_ether(char *bufp, struct sockaddr *sap)
567{
568 unsigned char *ptr;
569 int i, j;
570 unsigned char val;
571 unsigned char c;
572
573 sap->sa_family = ARPHRD_ETHER;
574 ptr = sap->sa_data;
575
576 i = 0;
577 do {
578 j = val = 0;
579
580 /* We might get a semicolon here - not required. */
581 if (i && (*bufp == ':')) {
582 bufp++;
583 }
584
585 do {
586 c = *bufp;
587 if (((unsigned char)(c - '0')) <= 9) {
588 c -= '0';
589 } else if (((unsigned char)((c|0x20) - 'a')) <= 5) {
590 c = (c|0x20) - ('a'-10);
591 } else if (j && (c == ':' || c == 0)) {
592 break;
593 } else {
594 return -1;
595 }
596 ++bufp;
597 val <<= 4;
598 val += c;
599 } while (++j < 2);
600 *ptr++ = val;
601 } while (++i < ETH_ALEN);
602
603 return (int) (*bufp); /* Error if we don't end at end of string. */
604}
605#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * ifupdown for busybox
4 * Copyright (c) 2002 Glenn McGrath <bug1@iinet.net.au>
5 * Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org>
6 *
7 * Based on ifupdown v 0.6.4 by Anthony Towns
8 * Copyright (c) 1999 Anthony Towns <aj@azure.humbug.org.au>
9 *
10 * Changes to upstream version
11 * Remove checks for kernel version, assume kernel version 2.2.0 or better.
12 * Lines in the interfaces file cannot wrap.
13 * To adhere to the FHS, the default state file is /var/run/ifstate.
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 */
29
30/* TODO: standardise execute() return codes to return 0 for success and 1 for failure */
31
32#include <sys/stat.h>
33#include <sys/utsname.h>
34#include <sys/wait.h>
35
36#include <ctype.h>
37#include <errno.h>
38#include <fcntl.h>
39#include <fnmatch.h>
40#include <getopt.h>
41#include <stdarg.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47#include "libbb.h"
48
49#define MAX_OPT_DEPTH 10
50#define EUNBALBRACK 10001
51#define EUNDEFVAR 10002
52#define EUNBALPER 10000
53
54#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
55#define MAX_INTERFACE_LENGTH 10
56#endif
57
58#if 0
59#define debug_noise(fmt, args...) printf(fmt, ## args)
60#else
61#define debug_noise(fmt, args...)
62#endif
63
64/* Forward declaration */
65struct interface_defn_t;
66
67typedef int (execfn)(char *command);
68typedef int (command_set)(struct interface_defn_t *ifd, execfn *e);
69
70extern llist_t *llist_add_to_end(llist_t *list_head, char *data)
71{
72 llist_t *new_item, *tmp, *prev;
73
74 new_item = xmalloc(sizeof(llist_t));
75 new_item->data = data;
76 new_item->link = NULL;
77
78 prev = NULL;
79 tmp = list_head;
80 while(tmp) {
81 prev = tmp;
82 tmp = tmp->link;
83 }
84 if (prev) {
85 prev->link = new_item;
86 } else {
87 list_head = new_item;
88 }
89
90 return(list_head);
91}
92
93struct method_t
94{
95 char *name;
96 command_set *up;
97 command_set *down;
98};
99
100struct address_family_t
101{
102 char *name;
103 int n_methods;
104 struct method_t *method;
105};
106
107struct mapping_defn_t
108{
109 struct mapping_defn_t *next;
110
111 int max_matches;
112 int n_matches;
113 char **match;
114
115 char *script;
116
117 int max_mappings;
118 int n_mappings;
119 char **mapping;
120};
121
122struct variable_t
123{
124 char *name;
125 char *value;
126};
127
128struct interface_defn_t
129{
130 struct interface_defn_t *prev;
131 struct interface_defn_t *next;
132
133 char *iface;
134 struct address_family_t *address_family;
135 struct method_t *method;
136
137 int automatic;
138
139 int max_options;
140 int n_options;
141 struct variable_t *option;
142};
143
144struct interfaces_file_t
145{
146 llist_t *autointerfaces;
147 llist_t *ifaces;
148 struct mapping_defn_t *mappings;
149};
150
151static char no_act = 0;
152static char verbose = 0;
153static char **environ = NULL;
154
155#ifdef CONFIG_FEATURE_IFUPDOWN_IP
156
157static unsigned int count_bits(unsigned int a)
158{
159 unsigned int result;
160 result = (a & 0x55) + ((a >> 1) & 0x55);
161 result = (result & 0x33) + ((result >> 2) & 0x33);
162 return((result & 0x0F) + ((result >> 4) & 0x0F));
163}
164
165static int count_netmask_bits(char *dotted_quad)
166{
167 unsigned int result, a, b, c, d;
168 /* Found a netmask... Check if it is dotted quad */
169 if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
170 return -1;
171 result = count_bits(a);
172 result += count_bits(b);
173 result += count_bits(c);
174 result += count_bits(d);
175 return ((int)result);
176}
177#endif
178
179static void addstr(char **buf, size_t *len, size_t *pos, char *str, size_t str_length)
180{
181 if (*pos + str_length >= *len) {
182 char *newbuf;
183
184 newbuf = xrealloc(*buf, *len * 2 + str_length + 1);
185 *buf = newbuf;
186 *len = *len * 2 + str_length + 1;
187 }
188
189 while (str_length-- >= 1) {
190 (*buf)[(*pos)++] = *str;
191 str++;
192 }
193 (*buf)[*pos] = '\0';
194}
195
196static int strncmpz(char *l, char *r, size_t llen)
197{
198 int i = strncmp(l, r, llen);
199
200 if (i == 0) {
201 return(-r[llen]);
202 } else {
203 return(i);
204 }
205}
206
207static char *get_var(char *id, size_t idlen, struct interface_defn_t *ifd)
208{
209 int i;
210
211 if (strncmpz(id, "iface", idlen) == 0) {
212 char *result;
213 static char label_buf[20];
214 strncpy(label_buf, ifd->iface, 19);
215 label_buf[19]=0;
216 result = strchr(label_buf, ':');
217 if (result) {
218 *result=0;
219 }
220 return( label_buf);
221 } else if (strncmpz(id, "label", idlen) == 0) {
222 return (ifd->iface);
223 } else {
224 for (i = 0; i < ifd->n_options; i++) {
225 if (strncmpz(id, ifd->option[i].name, idlen) == 0) {
226 return (ifd->option[i].value);
227 }
228 }
229 }
230
231 return(NULL);
232}
233
234static char *parse(char *command, struct interface_defn_t *ifd)
235{
236
237 char *result = NULL;
238 size_t pos = 0, len = 0;
239 size_t old_pos[MAX_OPT_DEPTH] = { 0 };
240 int okay[MAX_OPT_DEPTH] = { 1 };
241 int opt_depth = 1;
242
243 while (*command) {
244 switch (*command) {
245
246 default:
247 addstr(&result, &len, &pos, command, 1);
248 command++;
249 break;
250 case '\\':
251 if (command[1]) {
252 addstr(&result, &len, &pos, command + 1, 1);
253 command += 2;
254 } else {
255 addstr(&result, &len, &pos, command, 1);
256 command++;
257 }
258 break;
259 case '[':
260 if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) {
261 old_pos[opt_depth] = pos;
262 okay[opt_depth] = 1;
263 opt_depth++;
264 command += 2;
265 } else {
266 addstr(&result, &len, &pos, "[", 1);
267 command++;
268 }
269 break;
270 case ']':
271 if (command[1] == ']' && opt_depth > 1) {
272 opt_depth--;
273 if (!okay[opt_depth]) {
274 pos = old_pos[opt_depth];
275 result[pos] = '\0';
276 }
277 command += 2;
278 } else {
279 addstr(&result, &len, &pos, "]", 1);
280 command++;
281 }
282 break;
283 case '%':
284 {
285 char *nextpercent;
286 char *varvalue;
287
288 command++;
289 nextpercent = strchr(command, '%');
290 if (!nextpercent) {
291 errno = EUNBALPER;
292 free(result);
293 return (NULL);
294 }
295
296 varvalue = get_var(command, nextpercent - command, ifd);
297
298 if (varvalue) {
299 addstr(&result, &len, &pos, varvalue, bb_strlen(varvalue));
300 } else {
301#ifdef CONFIG_FEATURE_IFUPDOWN_IP
302 /* Sigh... Add a special case for 'ip' to convert from
303 * dotted quad to bit count style netmasks. */
304 if (strncmp(command, "bnmask", 6)==0) {
305 int res;
306 varvalue = get_var("netmask", 7, ifd);
307 if (varvalue && (res=count_netmask_bits(varvalue)) > 0) {
308 char argument[255];
309 sprintf(argument, "%d", res);
310 addstr(&result, &len, &pos, argument, bb_strlen(argument));
311 command = nextpercent + 1;
312 break;
313 }
314 }
315#endif
316 okay[opt_depth - 1] = 0;
317 }
318
319 command = nextpercent + 1;
320 }
321 break;
322 }
323 }
324
325 if (opt_depth > 1) {
326 errno = EUNBALBRACK;
327 free(result);
328 return(NULL);
329 }
330
331 if (!okay[0]) {
332 errno = EUNDEFVAR;
333 free(result);
334 return(NULL);
335 }
336
337 return(result);
338}
339
340static int execute(char *command, struct interface_defn_t *ifd, execfn *exec)
341{
342 char *out;
343 int ret;
344
345 out = parse(command, ifd);
346 if (!out) {
347 return(0);
348 }
349 ret = (*exec) (out);
350
351 free(out);
352 if (ret != 1) {
353 return(0);
354 }
355 return(1);
356}
357
358#ifdef CONFIG_FEATURE_IFUPDOWN_IPX
359static int static_up_ipx(struct interface_defn_t *ifd, execfn *exec)
360{
361 return(execute("ipx_interface add %iface% %frame% %netnum%", ifd, exec));
362}
363
364static int static_down_ipx(struct interface_defn_t *ifd, execfn *exec)
365{
366 return(execute("ipx_interface del %iface% %frame%", ifd, exec));
367}
368
369static int dynamic_up(struct interface_defn_t *ifd, execfn *exec)
370{
371 return(execute("ipx_interface add %iface% %frame%", ifd, exec));
372}
373
374static int dynamic_down(struct interface_defn_t *ifd, execfn *exec)
375{
376 return(execute("ipx_interface del %iface% %frame%", ifd, exec));
377}
378
379static struct method_t methods_ipx[] = {
380 { "dynamic", dynamic_up, dynamic_down, },
381 { "static", static_up_ipx, static_down_ipx, },
382};
383
384struct address_family_t addr_ipx = {
385 "ipx",
386 sizeof(methods_ipx) / sizeof(struct method_t),
387 methods_ipx
388};
389#endif /* IFUP_FEATURE_IPX */
390
391#ifdef CONFIG_FEATURE_IFUPDOWN_IPV6
392static int loopback_up6(struct interface_defn_t *ifd, execfn *exec)
393{
394#ifdef CONFIG_FEATURE_IFUPDOWN_IP
395 int result;
396 result =execute("ip addr add ::1 dev %iface%", ifd, exec);
397 result += execute("ip link set %iface% up", ifd, exec);
398 return ((result == 2) ? 2 : 0);
399#else
400 return( execute("ifconfig %iface% add ::1", ifd, exec));
401#endif
402}
403
404static int loopback_down6(struct interface_defn_t *ifd, execfn *exec)
405{
406#ifdef CONFIG_FEATURE_IFUPDOWN_IP
407 return(execute("ip link set %iface% down", ifd, exec));
408#else
409 return(execute("ifconfig %iface% del ::1", ifd, exec));
410#endif
411}
412
413static int static_up6(struct interface_defn_t *ifd, execfn *exec)
414{
415 int result;
416#ifdef CONFIG_FEATURE_IFUPDOWN_IP
417 result = execute("ip addr add %address%/%netmask% dev %iface% [[label %label%]]", ifd, exec);
418 result += execute("ip link set [[mtu %mtu%]] [[address %hwaddress%]] %iface% up", ifd, exec);
419 result += execute("[[ ip route add ::/0 via %gateway% ]]", ifd, exec);
420#else
421 result = execute("ifconfig %iface% [[media %media%]] [[hw %hwaddress%]] [[mtu %mtu%]] up", ifd, exec);
422 result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec);
423 result += execute("[[ route -A inet6 add ::/0 gw %gateway% ]]", ifd, exec);
424#endif
425 return ((result == 3) ? 3 : 0);
426}
427
428static int static_down6(struct interface_defn_t *ifd, execfn *exec)
429{
430#ifdef CONFIG_FEATURE_IFUPDOWN_IP
431 return(execute("ip link set %iface% down", ifd, exec));
432#else
433 return(execute("ifconfig %iface% down", ifd, exec));
434#endif
435}
436
437#ifdef CONFIG_FEATURE_IFUPDOWN_IP
438static int v4tunnel_up(struct interface_defn_t *ifd, execfn *exec)
439{
440 int result;
441 result = execute("ip tunnel add %iface% mode sit remote "
442 "%endpoint% [[local %local%]] [[ttl %ttl%]]", ifd, exec);
443 result += execute("ip link set %iface% up", ifd, exec);
444 result += execute("ip addr add %address%/%netmask% dev %iface%", ifd, exec);
445 result += execute("[[ ip route add ::/0 via %gateway% ]]", ifd, exec);
446 return ((result == 4) ? 4 : 0);
447}
448
449static int v4tunnel_down(struct interface_defn_t * ifd, execfn * exec)
450{
451 return( execute("ip tunnel del %iface%", ifd, exec));
452}
453#endif
454
455static struct method_t methods6[] = {
456#ifdef CONFIG_FEATURE_IFUPDOWN_IP
457 { "v4tunnel", v4tunnel_up, v4tunnel_down, },
458#endif
459 { "static", static_up6, static_down6, },
460 { "loopback", loopback_up6, loopback_down6, },
461};
462
463struct address_family_t addr_inet6 = {
464 "inet6",
465 sizeof(methods6) / sizeof(struct method_t),
466 methods6
467};
468#endif /* CONFIG_FEATURE_IFUPDOWN_IPV6 */
469
470#ifdef CONFIG_FEATURE_IFUPDOWN_IPV4
471static int loopback_up(struct interface_defn_t *ifd, execfn *exec)
472{
473#ifdef CONFIG_FEATURE_IFUPDOWN_IP
474 int result;
475 result = execute("ip addr add 127.0.0.1/8 dev %iface%", ifd, exec);
476 result += execute("ip link set %iface% up", ifd, exec);
477 return ((result == 2) ? 2 : 0);
478#else
479 return( execute("ifconfig %iface% 127.0.0.1 up", ifd, exec));
480#endif
481}
482
483static int loopback_down(struct interface_defn_t *ifd, execfn *exec)
484{
485#ifdef CONFIG_FEATURE_IFUPDOWN_IP
486 int result;
487 result = execute("ip addr flush dev %iface%", ifd, exec);
488 result += execute("ip link set %iface% down", ifd, exec);
489 return ((result == 2) ? 2 : 0);
490#else
491 return( execute("ifconfig %iface% 127.0.0.1 down", ifd, exec));
492#endif
493}
494
495static int static_up(struct interface_defn_t *ifd, execfn *exec)
496{
497 int result;
498#ifdef CONFIG_FEATURE_IFUPDOWN_IP
499 result = execute("ip addr add %address%/%bnmask% [[broadcast %broadcast%]] "
500 "dev %iface% [[peer %pointopoint%]] [[label %label%]]", ifd, exec);
501 result += execute("ip link set [[mtu %mtu%]] [[address %hwaddress%]] %iface% up", ifd, exec);
502 result += execute("[[ ip route add default via %gateway% dev %iface% ]]", ifd, exec);
503 return ((result == 3) ? 3 : 0);
504#else
505 result = execute("ifconfig %iface% %address% netmask %netmask% "
506 "[[broadcast %broadcast%]] [[pointopoint %pointopoint%]] "
507 "[[media %media%]] [[mtu %mtu%]] [[hw %hwaddress%]] up",
508 ifd, exec);
509 result += execute("[[ route add default gw %gateway% %iface% ]]", ifd, exec);
510 return ((result == 2) ? 2 : 0);
511#endif
512}
513
514static int static_down(struct interface_defn_t *ifd, execfn *exec)
515{
516 int result;
517#ifdef CONFIG_FEATURE_IFUPDOWN_IP
518 result = execute("ip addr flush dev %iface%", ifd, exec);
519 result += execute("ip link set %iface% down", ifd, exec);
520#else
521 result = execute("[[ route del default gw %gateway% %iface% ]]", ifd, exec);
522 result += execute("ifconfig %iface% down", ifd, exec);
523#endif
524 return ((result == 2) ? 2 : 0);
525}
526
527static int execable(char *program)
528{
529 struct stat buf;
530 if (0 == stat(program, &buf)) {
531 if (S_ISREG(buf.st_mode) && (S_IXUSR & buf.st_mode)) {
532 return(1);
533 }
534 }
535 return(0);
536}
537
538static int dhcp_up(struct interface_defn_t *ifd, execfn *exec)
539{
540 if (execable("/sbin/udhcpc")) {
541 return( execute("udhcpc -n -p /var/run/udhcpc.%iface%.pid -i "
542 "%iface% [[-H %hostname%]] [[-c %clientid%]]", ifd, exec));
543 } else if (execable("/sbin/pump")) {
544 return( execute("pump -i %iface% [[-h %hostname%]] [[-l %leasehours%]]", ifd, exec));
545 } else if (execable("/sbin/dhclient")) {
546 return( execute("dhclient -pf /var/run/dhclient.%iface%.pid %iface%", ifd, exec));
547 } else if (execable("/sbin/dhcpcd")) {
548 return( execute("dhcpcd [[-h %hostname%]] [[-i %vendor%]] [[-I %clientid%]] "
549 "[[-l %leasetime%]] %iface%", ifd, exec));
550 }
551 return(0);
552}
553
554static int dhcp_down(struct interface_defn_t *ifd, execfn *exec)
555{
556 int result = 0;
557 if (execable("/sbin/udhcpc")) {
558 /* SIGUSR2 forces udhcpc to release the current lease and go inactive,
559 * and SIGTERM causes udhcpc to exit. Signals are queued and processed
560 * sequentially so we don't need to sleep */
561 result = execute("kill -USR2 `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
562 result += execute("kill -TERM `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
563 } else if (execable("/sbin/pump")) {
564 result = execute("pump -i %iface% -k", ifd, exec);
565 } else if (execable("/sbin/dhclient")) {
566 result = execute("kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null", ifd, exec);
567 } else if (execable("/sbin/dhcpcd")) {
568 result = execute("dhcpcd -k %iface%", ifd, exec);
569 }
570 return (result || static_down(ifd, exec));
571}
572
573static int bootp_up(struct interface_defn_t *ifd, execfn *exec)
574{
575 return( execute("bootpc [[--bootfile %bootfile%]] --dev %iface% "
576 "[[--server %server%]] [[--hwaddr %hwaddr%]] "
577 "--returniffail --serverbcast", ifd, exec));
578}
579
580static int ppp_up(struct interface_defn_t *ifd, execfn *exec)
581{
582 return( execute("pon [[%provider%]]", ifd, exec));
583}
584
585static int ppp_down(struct interface_defn_t *ifd, execfn *exec)
586{
587 return( execute("poff [[%provider%]]", ifd, exec));
588}
589
590static int wvdial_up(struct interface_defn_t *ifd, execfn *exec)
591{
592 return( execute("/sbin/start-stop-daemon --start -x /usr/bin/wvdial "
593 "-p /var/run/wvdial.%iface% -b -m -- [[ %provider% ]]", ifd, exec));
594}
595
596static int wvdial_down(struct interface_defn_t *ifd, execfn *exec)
597{
598 return( execute("/sbin/start-stop-daemon --stop -x /usr/bin/wvdial "
599 "-p /var/run/wvdial.%iface% -s 2", ifd, exec));
600}
601
602static struct method_t methods[] =
603{
604 { "wvdial", wvdial_up, wvdial_down, },
605 { "ppp", ppp_up, ppp_down, },
606 { "static", static_up, static_down, },
607 { "bootp", bootp_up, static_down, },
608 { "dhcp", dhcp_up, dhcp_down, },
609 { "loopback", loopback_up, loopback_down, },
610};
611
612struct address_family_t addr_inet =
613{
614 "inet",
615 sizeof(methods) / sizeof(struct method_t),
616 methods
617};
618
619#endif /* ifdef CONFIG_FEATURE_IFUPDOWN_IPV4 */
620
621static char *next_word(char **buf)
622{
623 unsigned short length;
624 char *word;
625
626 if ((buf == NULL) || (*buf == NULL) || (**buf == '\0')) {
627 return NULL;
628 }
629
630 /* Skip over leading whitespace */
631 word = *buf;
632 while (isspace(*word)) {
633 ++word;
634 }
635
636 /* Skip over comments */
637 if (*word == '#') {
638 return(NULL);
639 }
640
641 /* Find the length of this word */
642 length = strcspn(word, " \t\n");
643 if (length == 0) {
644 return(NULL);
645 }
646 *buf = word + length;
647 /*DBU:[dave@cray.com] if we are already at EOL dont't increment beyond it */
648 if (**buf) {
649 **buf = '\0';
650 (*buf)++;
651 }
652
653 return word;
654}
655
656static struct address_family_t *get_address_family(struct address_family_t *af[], char *name)
657{
658 int i;
659
660 for (i = 0; af[i]; i++) {
661 if (strcmp(af[i]->name, name) == 0) {
662 return af[i];
663 }
664 }
665 return NULL;
666}
667
668static struct method_t *get_method(struct address_family_t *af, char *name)
669{
670 int i;
671
672 for (i = 0; i < af->n_methods; i++) {
673 if (strcmp(af->method[i].name, name) == 0) {
674 return &af->method[i];
675 }
676 }
677 return(NULL);
678}
679
680static int duplicate_if(struct interface_defn_t *ifa, struct interface_defn_t *ifb)
681{
682 if (strcmp(ifa->iface, ifb->iface) != 0) {
683 return(0);
684 }
685 if (ifa->address_family != ifb->address_family) {
686 return(0);
687 }
688 return(1);
689}
690
691static const llist_t *find_list_string(const llist_t *list, const char *string)
692{
693 while (list) {
694 if (strcmp(list->data, string) == 0) {
695 return(list);
696 }
697 list = list->link;
698 }
699 return(NULL);
700}
701
702static struct interfaces_file_t *read_interfaces(const char *filename)
703{
704#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
705 struct mapping_defn_t *currmap = NULL;
706#endif
707 struct interface_defn_t *currif = NULL;
708 struct interfaces_file_t *defn;
709 FILE *f;
710 char *firstword;
711 char *buf;
712
713 enum { NONE, IFACE, MAPPING } currently_processing = NONE;
714
715 defn = xmalloc(sizeof(struct interfaces_file_t));
716 defn->autointerfaces = NULL;
717 defn->mappings = NULL;
718 defn->ifaces = NULL;
719
720 f = bb_xfopen(filename, "r");
721
722 while ((buf = bb_get_chomped_line_from_file(f)) != NULL) {
723 char *buf_ptr = buf;
724
725 firstword = next_word(&buf_ptr);
726 if (firstword == NULL) {
727 free(buf);
728 continue; /* blank line */
729 }
730
731 if (strcmp(firstword, "mapping") == 0) {
732#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
733 currmap = xmalloc(sizeof(struct mapping_defn_t));
734 currmap->max_matches = 0;
735 currmap->n_matches = 0;
736 currmap->match = NULL;
737
738 while ((firstword = next_word(&buf_ptr)) != NULL) {
739 if (currmap->max_matches == currmap->n_matches) {
740 currmap->max_matches = currmap->max_matches * 2 + 1;
741 currmap->match = xrealloc(currmap->match, sizeof(currmap->match) * currmap->max_matches);
742 }
743
744 currmap->match[currmap->n_matches++] = bb_xstrdup(firstword);
745 }
746 currmap->max_mappings = 0;
747 currmap->n_mappings = 0;
748 currmap->mapping = NULL;
749 currmap->script = NULL;
750 {
751 struct mapping_defn_t **where = &defn->mappings;
752 while (*where != NULL) {
753 where = &(*where)->next;
754 }
755 *where = currmap;
756 currmap->next = NULL;
757 }
758 debug_noise("Added mapping\n");
759#endif
760 currently_processing = MAPPING;
761 } else if (strcmp(firstword, "iface") == 0) {
762 {
763 char *iface_name;
764 char *address_family_name;
765 char *method_name;
766 struct address_family_t *addr_fams[] = {
767#ifdef CONFIG_FEATURE_IFUPDOWN_IPV4
768 &addr_inet,
769#endif
770#ifdef CONFIG_FEATURE_IFUPDOWN_IPV6
771 &addr_inet6,
772#endif
773#ifdef CONFIG_FEATURE_IFUPDOWN_IPX
774 &addr_ipx,
775#endif
776 NULL
777 };
778
779 currif = xmalloc(sizeof(struct interface_defn_t));
780 iface_name = next_word(&buf_ptr);
781 address_family_name = next_word(&buf_ptr);
782 method_name = next_word(&buf_ptr);
783
784 if (buf_ptr == NULL) {
785 bb_error_msg("too few parameters for line \"%s\"", buf);
786 return NULL;
787 }
788
789 /* ship any trailing whitespace */
790 while (isspace(*buf_ptr)) {
791 ++buf_ptr;
792 }
793
794 if (buf_ptr[0] != '\0') {
795 bb_error_msg("too many parameters \"%s\"", buf);
796 return NULL;
797 }
798
799 currif->iface = bb_xstrdup(iface_name);
800
801 currif->address_family = get_address_family(addr_fams, address_family_name);
802 if (!currif->address_family) {
803 bb_error_msg("unknown address type \"%s\"", address_family_name);
804 return NULL;
805 }
806
807 currif->method = get_method(currif->address_family, method_name);
808 if (!currif->method) {
809 bb_error_msg("unknown method \"%s\"", method_name);
810 return NULL;
811 }
812
813 currif->automatic = 1;
814 currif->max_options = 0;
815 currif->n_options = 0;
816 currif->option = NULL;
817
818 {
819 struct interface_defn_t *tmp;
820 llist_t *iface_list;
821 iface_list = defn->ifaces;
822 while (iface_list) {
823 tmp = (struct interface_defn_t *) iface_list->data;
824 if (duplicate_if(tmp, currif)) {
825 bb_error_msg("duplicate interface \"%s\"", tmp->iface);
826 return NULL;
827 }
828 iface_list = iface_list->link;
829 }
830
831 defn->ifaces = llist_add_to_end(defn->ifaces, (char*)currif);
832 }
833 debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name);
834 }
835 currently_processing = IFACE;
836 } else if (strcmp(firstword, "auto") == 0) {
837 while ((firstword = next_word(&buf_ptr)) != NULL) {
838
839 /* Check the interface isnt already listed */
840 if (find_list_string(defn->autointerfaces, firstword)) {
841 bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf);
842 }
843
844 /* Add the interface to the list */
845 defn->autointerfaces = llist_add_to_end(defn->autointerfaces, strdup(firstword));
846 debug_noise("\nauto %s\n", firstword);
847 }
848 currently_processing = NONE;
849 } else {
850 switch (currently_processing) {
851 case IFACE:
852 {
853 int i;
854
855 if (bb_strlen(buf_ptr) == 0) {
856 bb_error_msg("option with empty value \"%s\"", buf);
857 return NULL;
858 }
859
860 if (strcmp(firstword, "up") != 0
861 && strcmp(firstword, "down") != 0
862 && strcmp(firstword, "pre-up") != 0
863 && strcmp(firstword, "post-down") != 0) {
864 for (i = 0; i < currif->n_options; i++) {
865 if (strcmp(currif->option[i].name, firstword) == 0) {
866 bb_error_msg("duplicate option \"%s\"", buf);
867 return NULL;
868 }
869 }
870 }
871 }
872 if (currif->n_options >= currif->max_options) {
873 struct variable_t *opt;
874
875 currif->max_options = currif->max_options + 10;
876 opt = xrealloc(currif->option, sizeof(*opt) * currif->max_options);
877 currif->option = opt;
878 }
879 currif->option[currif->n_options].name = bb_xstrdup(firstword);
880 currif->option[currif->n_options].value = bb_xstrdup(buf_ptr);
881 if (!currif->option[currif->n_options].name) {
882 perror(filename);
883 return NULL;
884 }
885 if (!currif->option[currif->n_options].value) {
886 perror(filename);
887 return NULL;
888 }
889 debug_noise("\t%s=%s\n", currif->option[currif->n_options].name,
890 currif->option[currif->n_options].value);
891 currif->n_options++;
892 break;
893 case MAPPING:
894#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
895 if (strcmp(firstword, "script") == 0) {
896 if (currmap->script != NULL) {
897 bb_error_msg("duplicate script in mapping \"%s\"", buf);
898 return NULL;
899 } else {
900 currmap->script = bb_xstrdup(next_word(&buf_ptr));
901 }
902 } else if (strcmp(firstword, "map") == 0) {
903 if (currmap->max_mappings == currmap->n_mappings) {
904 currmap->max_mappings = currmap->max_mappings * 2 + 1;
905 currmap->mapping = xrealloc(currmap->mapping, sizeof(char *) * currmap->max_mappings);
906 }
907 currmap->mapping[currmap->n_mappings] = bb_xstrdup(next_word(&buf_ptr));
908 currmap->n_mappings++;
909 } else {
910 bb_error_msg("misplaced option \"%s\"", buf);
911 return NULL;
912 }
913#endif
914 break;
915 case NONE:
916 default:
917 bb_error_msg("misplaced option \"%s\"", buf);
918 return NULL;
919 }
920 }
921 free(buf);
922 }
923 if (ferror(f) != 0) {
924 bb_perror_msg_and_die("%s", filename);
925 }
926 fclose(f);
927
928 return defn;
929}
930
931static char *setlocalenv(char *format, char *name, char *value)
932{
933 char *result;
934 char *here;
935 char *there;
936
937 result = xmalloc(bb_strlen(format) + bb_strlen(name) + bb_strlen(value) + 1);
938
939 sprintf(result, format, name, value);
940
941 for (here = there = result; *there != '=' && *there; there++) {
942 if (*there == '-')
943 *there = '_';
944 if (isalpha(*there))
945 *there = toupper(*there);
946
947 if (isalnum(*there) || *there == '_') {
948 *here = *there;
949 here++;
950 }
951 }
952 memmove(here, there, bb_strlen(there) + 1);
953
954 return result;
955}
956
957static void set_environ(struct interface_defn_t *iface, char *mode)
958{
959 char **environend;
960 int i;
961 const int n_env_entries = iface->n_options + 5;
962 char **ppch;
963
964 if (environ != NULL) {
965 for (ppch = environ; *ppch; ppch++) {
966 free(*ppch);
967 *ppch = NULL;
968 }
969 free(environ);
970 environ = NULL;
971 }
972 environ = xmalloc(sizeof(char *) * (n_env_entries + 1 /* for final NULL */ ));
973 environend = environ;
974 *environend = NULL;
975
976 for (i = 0; i < iface->n_options; i++) {
977 if (strcmp(iface->option[i].name, "up") == 0
978 || strcmp(iface->option[i].name, "down") == 0
979 || strcmp(iface->option[i].name, "pre-up") == 0
980 || strcmp(iface->option[i].name, "post-down") == 0) {
981 continue;
982 }
983 *(environend++) = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value);
984 *environend = NULL;
985 }
986
987 *(environend++) = setlocalenv("%s=%s", "IFACE", iface->iface);
988 *environend = NULL;
989 *(environend++) = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name);
990 *environend = NULL;
991 *(environend++) = setlocalenv("%s=%s", "METHOD", iface->method->name);
992 *environend = NULL;
993 *(environend++) = setlocalenv("%s=%s", "MODE", mode);
994 *environend = NULL;
995 *(environend++) = setlocalenv("%s=%s", "PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
996 *environend = NULL;
997}
998
999static int doit(char *str)
1000{
1001 if (verbose || no_act) {
1002 printf("%s\n", str);
1003 }
1004 if (!no_act) {
1005 pid_t child;
1006 int status;
1007
1008 fflush(NULL);
1009 switch (child = fork()) {
1010 case -1: /* failure */
1011 return 0;
1012 case 0: /* child */
1013 execle(DEFAULT_SHELL, DEFAULT_SHELL, "-c", str, NULL, environ);
1014 exit(127);
1015 }
1016 waitpid(child, &status, 0);
1017 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
1018 return 0;
1019 }
1020 }
1021 return (1);
1022}
1023
1024static int execute_all(struct interface_defn_t *ifd, execfn *exec, const char *opt)
1025{
1026 int i;
1027 char *buf;
1028 for (i = 0; i < ifd->n_options; i++) {
1029 if (strcmp(ifd->option[i].name, opt) == 0) {
1030 if (!(*exec) (ifd->option[i].value)) {
1031 return 0;
1032 }
1033 }
1034 }
1035
1036 bb_xasprintf(&buf, "run-parts /etc/network/if-%s.d", opt);
1037 if ((*exec)(buf) != 1) {
1038 return 0;
1039 }
1040 return 1;
1041}
1042
1043static int check(char *str) {
1044 return str != NULL;
1045}
1046
1047static int iface_up(struct interface_defn_t *iface)
1048{
1049 if (!iface->method->up(iface,check)) return -1;
1050 set_environ(iface, "start");
1051 if (!execute_all(iface, doit, "pre-up")) return 0;
1052 if (!iface->method->up(iface, doit)) return 0;
1053 if (!execute_all(iface, doit, "up")) return 0;
1054 return 1;
1055}
1056
1057static int iface_down(struct interface_defn_t *iface)
1058{
1059 if (!iface->method->down(iface,check)) return -1;
1060 set_environ(iface, "stop");
1061 if (!execute_all(iface, doit, "down")) return 0;
1062 if (!iface->method->down(iface, doit)) return 0;
1063 if (!execute_all(iface, doit, "post-down")) return 0;
1064 return 1;
1065}
1066
1067#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
1068static int popen2(FILE **in, FILE **out, char *command, ...)
1069{
1070 va_list ap;
1071 char *argv[11] = { command };
1072 int argc;
1073 int infd[2], outfd[2];
1074 pid_t pid;
1075
1076 argc = 1;
1077 va_start(ap, command);
1078 while ((argc < 10) && (argv[argc] = va_arg(ap, char *))) {
1079 argc++;
1080 }
1081 argv[argc] = NULL; /* make sure */
1082 va_end(ap);
1083
1084 if (pipe(infd) != 0) {
1085 return 0;
1086 }
1087
1088 if (pipe(outfd) != 0) {
1089 close(infd[0]);
1090 close(infd[1]);
1091 return 0;
1092 }
1093
1094 fflush(NULL);
1095 switch (pid = fork()) {
1096 case -1: /* failure */
1097 close(infd[0]);
1098 close(infd[1]);
1099 close(outfd[0]);
1100 close(outfd[1]);
1101 return 0;
1102 case 0: /* child */
1103 dup2(infd[0], 0);
1104 dup2(outfd[1], 1);
1105 close(infd[0]);
1106 close(infd[1]);
1107 close(outfd[0]);
1108 close(outfd[1]);
1109 execvp(command, argv);
1110 exit(127);
1111 default: /* parent */
1112 *in = fdopen(infd[1], "w");
1113 *out = fdopen(outfd[0], "r");
1114 close(infd[0]);
1115 close(outfd[1]);
1116 return pid;
1117 }
1118 /* unreached */
1119}
1120
1121static char *run_mapping(char *physical, struct mapping_defn_t * map)
1122{
1123 FILE *in, *out;
1124 int i, status;
1125 pid_t pid;
1126
1127 char *logical = bb_xstrdup(physical);
1128
1129 /* Run the mapping script. */
1130 pid = popen2(&in, &out, map->script, physical, NULL);
1131
1132 /* popen2() returns 0 on failure. */
1133 if (pid == 0)
1134 return logical;
1135
1136 /* Write mappings to stdin of mapping script. */
1137 for (i = 0; i < map->n_mappings; i++) {
1138 fprintf(in, "%s\n", map->mapping[i]);
1139 }
1140 fclose(in);
1141 waitpid(pid, &status, 0);
1142
1143 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
1144 /* If the mapping script exited successfully, try to
1145 * grab a line of output and use that as the name of the
1146 * logical interface. */
1147 char *new_logical = (char *)xmalloc(MAX_INTERFACE_LENGTH);
1148
1149 if (fgets(new_logical, MAX_INTERFACE_LENGTH, out)) {
1150 /* If we are able to read a line of output from the script,
1151 * remove any trailing whitespace and use this value
1152 * as the name of the logical interface. */
1153 char *pch = new_logical + bb_strlen(new_logical) - 1;
1154
1155 while (pch >= new_logical && isspace(*pch))
1156 *(pch--) = '\0';
1157
1158 free(logical);
1159 logical = new_logical;
1160 } else {
1161 /* If we are UNABLE to read a line of output, discard are
1162 * freshly allocated memory. */
1163 free(new_logical);
1164 }
1165 }
1166
1167 fclose(out);
1168
1169 return logical;
1170}
1171#endif /* CONFIG_FEATURE_IFUPDOWN_MAPPING */
1172
1173static llist_t *find_iface_state(llist_t *state_list, const char *iface)
1174{
1175 unsigned short iface_len = bb_strlen(iface);
1176 llist_t *search = state_list;
1177
1178 while (search) {
1179 if ((strncmp(search->data, iface, iface_len) == 0) &&
1180 (search->data[iface_len] == '=')) {
1181 return(search);
1182 }
1183 search = search->link;
1184 }
1185 return(NULL);
1186}
1187
1188extern int ifupdown_main(int argc, char **argv)
1189{
1190 int (*cmds) (struct interface_defn_t *) = NULL;
1191 struct interfaces_file_t *defn;
1192 FILE *state_fp = NULL;
1193 llist_t *state_list = NULL;
1194 llist_t *target_list = NULL;
1195 const char *interfaces = "/etc/network/interfaces";
1196 const char *statefile = "/var/run/ifstate";
1197
1198#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
1199 int run_mappings = 1;
1200#endif
1201 int do_all = 0;
1202 int force = 0;
1203 int any_failures = 0;
1204 int i;
1205
1206 if (bb_applet_name[2] == 'u') {
1207 /* ifup command */
1208 cmds = iface_up;
1209 } else {
1210 /* ifdown command */
1211 cmds = iface_down;
1212 }
1213
1214#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
1215 while ((i = getopt(argc, argv, "i:hvnamf")) != -1)
1216#else
1217 while ((i = getopt(argc, argv, "i:hvnaf")) != -1)
1218#endif
1219 {
1220 switch (i) {
1221 case 'i': /* interfaces */
1222 interfaces = optarg;
1223 break;
1224 case 'v': /* verbose */
1225 verbose = 1;
1226 break;
1227 case 'a': /* all */
1228 do_all = 1;
1229 break;
1230 case 'n': /* no-act */
1231 no_act = 1;
1232 break;
1233#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
1234 case 'm': /* no-mappings */
1235 run_mappings = 0;
1236 break;
1237#endif
1238 case 'f': /* force */
1239 force = 1;
1240 break;
1241 default:
1242 bb_show_usage();
1243 break;
1244 }
1245 }
1246
1247 if (argc - optind > 0) {
1248 if (do_all) {
1249 bb_show_usage();
1250 }
1251 } else {
1252 if (!do_all) {
1253 bb_show_usage();
1254 }
1255 }
1256
1257 debug_noise("reading %s file:\n", interfaces);
1258 defn = read_interfaces(interfaces);
1259 debug_noise("\ndone reading %s\n\n", interfaces);
1260
1261 if (!defn) {
1262 exit(EXIT_FAILURE);
1263 }
1264
1265 if (no_act) {
1266 state_fp = fopen(statefile, "r");
1267 }
1268
1269 /* Create a list of interfaces to work on */
1270 if (do_all) {
1271 if (cmds == iface_up) {
1272 target_list = defn->autointerfaces;
1273 } else {
1274#if 0
1275 /* iface_down */
1276 llist_t *new_item;
1277 const llist_t *list = state_list;
1278 while (list) {
1279 new_item = xmalloc(sizeof(llist_t));
1280 new_item->data = strdup(list->data);
1281 new_item->link = NULL;
1282 list = target_list;
1283 if (list == NULL)
1284 target_list = new_item;
1285 else {
1286 while (list->link) {
1287 list = list->link;
1288 }
1289 list = new_item;
1290 }
1291 list = list->link;
1292 }
1293 target_list = defn->autointerfaces;
1294#else
1295
1296 /* iface_down */
1297 const llist_t *list = state_list;
1298 while (list) {
1299 target_list = llist_add_to_end(target_list, strdup(list->data));
1300 list = list->link;
1301 }
1302 target_list = defn->autointerfaces;
1303#endif
1304 }
1305 } else {
1306 target_list = llist_add_to_end(target_list, argv[optind]);
1307 }
1308
1309
1310 /* Update the interfaces */
1311 while (target_list) {
1312 llist_t *iface_list;
1313 struct interface_defn_t *currif;
1314 char *iface;
1315 char *liface;
1316 char *pch;
1317 int okay = 0;
1318 int cmds_ret;
1319
1320 iface = strdup(target_list->data);
1321 target_list = target_list->link;
1322
1323 pch = strchr(iface, '=');
1324 if (pch) {
1325 *pch = '\0';
1326 liface = strdup(pch + 1);
1327 } else {
1328 liface = strdup(iface);
1329 }
1330
1331 if (!force) {
1332 const llist_t *iface_state = find_iface_state(state_list, iface);
1333
1334 if (cmds == iface_up) {
1335 /* ifup */
1336 if (iface_state) {
1337 bb_error_msg("interface %s already configured", iface);
1338 continue;
1339 }
1340 } else {
1341 /* ifdown */
1342 if (iface_state) {
1343 bb_error_msg("interface %s not configured", iface);
1344 continue;
1345 }
1346 }
1347 }
1348
1349#ifdef CONFIG_FEATURE_IFUPDOWN_MAPPING
1350 if ((cmds == iface_up) && run_mappings) {
1351 struct mapping_defn_t *currmap;
1352
1353 for (currmap = defn->mappings; currmap; currmap = currmap->next) {
1354
1355 for (i = 0; i < currmap->n_matches; i++) {
1356 if (fnmatch(currmap->match[i], liface, 0) != 0)
1357 continue;
1358 if (verbose) {
1359 printf("Running mapping script %s on %s\n", currmap->script, liface);
1360 }
1361 liface = run_mapping(iface, currmap);
1362 break;
1363 }
1364 }
1365 }
1366#endif
1367
1368
1369 iface_list = defn->ifaces;
1370 while (iface_list) {
1371 currif = (struct interface_defn_t *) iface_list->data;
1372 if (strcmp(liface, currif->iface) == 0) {
1373 char *oldiface = currif->iface;
1374
1375 okay = 1;
1376 currif->iface = iface;
1377
1378 debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name);
1379
1380 /* Call the cmds function pointer, does either iface_up() or iface_down() */
1381 cmds_ret = cmds(currif);
1382 if (cmds_ret == -1) {
1383 bb_error_msg("Don't seem to have all the variables for %s/%s.",
1384 liface, currif->address_family->name);
1385 any_failures += 1;
1386 } else if (cmds_ret == 0) {
1387 any_failures += 1;
1388 }
1389
1390 currif->iface = oldiface;
1391 }
1392 iface_list = iface_list->link;
1393 }
1394 if (verbose) {
1395 printf("\n");
1396 }
1397
1398 if (!okay && !force) {
1399 bb_error_msg("Ignoring unknown interface %s", liface);
1400 any_failures += 1;
1401 } else {
1402 llist_t *iface_state = find_iface_state(state_list, iface);
1403
1404 if (cmds == iface_up) {
1405 char *newiface = xmalloc(bb_strlen(iface) + 1 + bb_strlen(liface) + 1);
1406 sprintf(newiface, "%s=%s", iface, liface);
1407 if (iface_state == NULL) {
1408 state_list = llist_add_to_end(state_list, newiface);
1409 } else {
1410 free(iface_state->data);
1411 iface_state->data = newiface;
1412 }
1413 } else if (cmds == iface_down) {
1414 /* Remove an interface from the linked list */
1415 if (iface_state) {
1416 /* This needs to be done better */
1417 free(iface_state->data);
1418 free(iface_state->link);
1419 if (iface_state->link) {
1420 iface_state->data = iface_state->link->data;
1421 iface_state->link = iface_state->link->link;
1422 } else {
1423 iface_state->data = NULL;
1424 iface_state->link = NULL;
1425 }
1426 }
1427 }
1428 }
1429 }
1430
1431 /* Actually write the new state */
1432 if (!no_act) {
1433
1434 if (state_fp)
1435 fclose(state_fp);
1436 state_fp = bb_xfopen(statefile, "a+");
1437
1438 if (ftruncate(fileno(state_fp), 0) < 0) {
1439 bb_error_msg_and_die("failed to truncate statefile %s: %s", statefile, strerror(errno));
1440 }
1441
1442 rewind(state_fp);
1443
1444 while (state_list) {
1445 if (state_list->data) {
1446 fputs(state_list->data, state_fp);
1447 fputc('\n', state_fp);
1448 }
1449 state_list = state_list->link;
1450 }
1451 fflush(state_fp);
1452 }
1453
1454 /* Cleanup */
1455 if (state_fp != NULL) {
1456 fclose(state_fp);
1457 state_fp = NULL;
1458 }
1459
1460 if (any_failures)
1461 return 1;
1462 return 0;
1463}
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 @@
1/*
2 * Copyright (c) 1983,1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * David A. Holland.
7 *
8 * Busybox port by Vladimir Oleynik (C) 2001-2003 <dzo@simtreas.ru>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25
26/*
27 * Inetd - Internet super-server
28 *
29 * This program invokes all internet services as needed.
30 * connection-oriented services are invoked each time a
31 * connection is made, by creating a process. This process
32 * is passed the connection as file descriptor 0 and is
33 * expected to do a getpeername to find out the source host
34 * and port.
35 *
36 * Datagram oriented services are invoked when a datagram
37 * arrives; a process is created and passed a pending message
38 * on file descriptor 0. Datagram servers may either connect
39 * to their peer, freeing up the original socket for inetd
40 * to receive further messages on, or ``take over the socket'',
41 * processing all arriving datagrams and, eventually, timing
42 * out. The first type of server is said to be ``multi-threaded'';
43 * the second type of server ``single-threaded''.
44 *
45 * Inetd uses a configuration file which is read at startup
46 * and, possibly, at some later time in response to a hangup signal.
47 * The configuration file is ``free format'' with fields given in the
48 * order shown below. Continuation lines for an entry must being with
49 * a space or tab. All fields must be present in each entry.
50 *
51 * service name must be in /etc/services
52 * socket type stream/dgram/raw/rdm/seqpacket
53 * protocol must be in /etc/protocols
54 * wait/nowait[.max] single-threaded/multi-threaded, max #
55 * user[.group] user/group to run daemon as
56 * server program full path name
57 * server program arguments maximum of MAXARGS (20)
58 *
59 * RPC services unsupported
60 *
61 * Comment lines are indicated by a `#' in column 1.
62 */
63
64/*
65 * Here's the scoop concerning the user.group feature:
66 *
67 * 1) No group listed.
68 *
69 * a) for root: NO setuid() or setgid() is done
70 *
71 * b) nonroot: setuid()
72 * setgid(primary group as found in passwd)
73 * initgroups(name, primary group)
74 *
75 * 2) set-group-option on.
76 *
77 * a) for root: NO setuid()
78 * setgid(specified group)
79 * setgroups(1, specified group)
80 *
81 * b) nonroot: setuid()
82 * setgid(specified group)
83 * initgroups(name, specified group)
84 *
85 * All supplementary groups are discarded at startup in case inetd was
86 * run manually.
87 */
88
89#define __USE_BSD_SIGNAL
90
91#include "busybox.h"
92
93
94#ifndef __linux__
95#ifndef RLIMIT_NOFILE
96#define RLIMIT_NOFILE RLIMIT_OFILE
97#endif
98#endif
99
100#include <sys/file.h>
101#include <sys/ioctl.h>
102#include <sys/param.h>
103#include <sys/resource.h>
104#include <sys/socket.h>
105#include <sys/stat.h>
106#include <sys/time.h>
107#include <sys/un.h>
108#include <sys/wait.h>
109
110#include <netinet/in.h>
111#include <netinet/ip.h>
112#include <arpa/inet.h>
113
114#include <errno.h>
115#include <signal.h>
116#include <netdb.h>
117#include <syslog.h>
118#include <stdio.h>
119#include <stdlib.h>
120#include <string.h>
121#include <getopt.h>
122#include <unistd.h>
123#include <stdarg.h>
124#include <time.h>
125
126#ifndef OPEN_MAX
127#define OPEN_MAX 64
128#endif
129
130#define _PATH_INETDCONF "/etc/inetd.conf"
131#define _PATH_INETDPID "/var/run/inetd.pid"
132
133#define TOOMANY 40 /* don't start more than TOOMANY */
134#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
135#define RETRYTIME (60*10) /* retry after bind or server fail */
136#define MAXARGV 20
137
138#define se_ctrladdr se_un.se_un_ctrladdr
139#define se_ctrladdr_in se_un.se_un_ctrladdr_in
140#define se_ctrladdr_un se_un.se_un_ctrladdr_un
141
142/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
143#define FD_MARGIN (8)
144
145/* Check unsupporting builtin */
146#if defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO || \
147 defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD || \
148 defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME || \
149 defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME || \
150 defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
151# define INETD_FEATURE_ENABLED
152#endif
153
154typedef struct servtab_s {
155 char *se_service; /* name of service */
156 int se_socktype; /* type of socket to use */
157 int se_family; /* address family */
158 char *se_proto; /* protocol used */
159 short se_wait; /* single threaded server */
160 short se_checked; /* looked at during merge */
161 char *se_user; /* user name to run as */
162 char *se_group; /* group name to run as */
163#ifdef INETD_FEATURE_ENABLED
164 const struct biltin *se_bi; /* if built-in, description */
165#endif
166 char *se_server; /* server program */
167 char *se_argv[MAXARGV+1]; /* program arguments */
168 int se_fd; /* open descriptor */
169 union {
170 struct sockaddr se_un_ctrladdr;
171 struct sockaddr_in se_un_ctrladdr_in;
172 struct sockaddr_un se_un_ctrladdr_un;
173 } se_un; /* bound address */
174 int se_ctrladdr_size;
175 int se_max; /* max # of instances of this service */
176 int se_count; /* number started since se_time */
177 struct timeval se_time; /* start of se_count */
178 struct servtab_s *se_next;
179} servtab_t;
180
181static servtab_t *servtab;
182
183#ifdef INETD_FEATURE_ENABLED
184struct biltin {
185 const char *bi_service; /* internally provided service name */
186 int bi_socktype; /* type of socket supported */
187 short bi_fork; /* 1 if should fork before call */
188 short bi_wait; /* 1 if should wait for child */
189 void (*bi_fn)(int, servtab_t *); /* fn which performs it */
190};
191
192 /* Echo received data */
193#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
194static void echo_stream(int, servtab_t *);
195static void echo_dg(int, servtab_t *);
196#endif
197 /* Internet /dev/null */
198#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
199static void discard_stream(int, servtab_t *);
200static void discard_dg(int, servtab_t *);
201#endif
202 /* Return 32 bit time since 1900 */
203#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
204static void machtime_stream(int, servtab_t *);
205static void machtime_dg(int, servtab_t *);
206#endif
207 /* Return human-readable time */
208#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
209static void daytime_stream(int, servtab_t *);
210static void daytime_dg(int, servtab_t *);
211#endif
212 /* Familiar character generator */
213#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
214static void chargen_stream(int, servtab_t *);
215static void chargen_dg(int, servtab_t *);
216#endif
217
218static const struct biltin biltins[] = {
219#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
220 /* Echo received data */
221 { "echo", SOCK_STREAM, 1, 0, echo_stream, },
222 { "echo", SOCK_DGRAM, 0, 0, echo_dg, },
223#endif
224#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
225 /* Internet /dev/null */
226 { "discard", SOCK_STREAM, 1, 0, discard_stream, },
227 { "discard", SOCK_DGRAM, 0, 0, discard_dg, },
228#endif
229#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
230 /* Return 32 bit time since 1900 */
231 { "time", SOCK_STREAM, 0, 0, machtime_stream, },
232 { "time", SOCK_DGRAM, 0, 0, machtime_dg, },
233#endif
234#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
235 /* Return human-readable time */
236 { "daytime", SOCK_STREAM, 0, 0, daytime_stream, },
237 { "daytime", SOCK_DGRAM, 0, 0, daytime_dg, },
238#endif
239#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
240 /* Familiar character generator */
241 { "chargen", SOCK_STREAM, 1, 0, chargen_stream, },
242 { "chargen", SOCK_DGRAM, 0, 0, chargen_dg, },
243#endif
244 { NULL, 0, 0, 0, NULL }
245};
246#endif /* INETD_FEATURE_ENABLED */
247
248#ifdef RLIMIT_NOFILE
249static struct rlimit rlim_ofile;
250#endif
251
252/* Length of socket listen queue. Should be per-service probably. */
253static int global_queuelen = 128;
254
255static FILE *fconfig;
256static sigset_t blockmask;
257static sigset_t emptymask;
258static fd_set allsock;
259static int nsock;
260static int maxsock;
261static int timingout;
262static int rlim_ofile_cur = OPEN_MAX;
263static const char *CONFIG = _PATH_INETDCONF;
264
265static void
266syslog_err_and_discard_dg(int se_socktype, const char *msg, ...)
267 __attribute__ ((noreturn, format (printf, 2, 3)));
268
269static void
270syslog_err_and_discard_dg(int se_socktype, const char *msg, ...)
271{
272 char buf[50];
273 va_list p;
274
275 va_start(p, msg);
276 vsyslog(LOG_ERR, msg, p);
277 if (se_socktype != SOCK_STREAM)
278 recv(0, buf, sizeof (buf), 0);
279 _exit(1);
280}
281
282static char * inetd_strdup(const char *s)
283{
284 char *ms = strdup(s);
285
286 if(ms == NULL)
287 syslog_err_and_discard_dg(SOCK_STREAM, "strdup: %m");
288 return ms;
289}
290
291
292static servtab_t *getconfigent(void)
293{
294 static servtab_t serv;
295 servtab_t *sep = &serv;
296 int argc;
297 char *cp = NULL;
298 char *cp_ptr;
299 char *cp_ptr_ptr = NULL;
300
301more:
302 free(cp);
303 cp = bb_get_chomped_line_from_file(fconfig);
304 if (feof(fconfig)) {
305 free(cp);
306 return (NULL);
307 }
308 if ((cp == NULL) || (*cp == '#')) {
309 goto more;
310 }
311 /* make bind 0.0.0.0 and other zero default */
312 memset((char *)sep, 0, sizeof *sep);
313
314 cp_ptr = strtok_r(cp, " \t", &cp_ptr_ptr);
315 if (cp_ptr == NULL) {
316 /* Error */
317 goto more;
318 }
319 sep->se_service = inetd_strdup(cp_ptr);
320
321 cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr);
322 if (cp_ptr == NULL) {
323 /* Error */
324 goto more;
325 }
326 if (strcmp(cp_ptr, "stream") == 0)
327 sep->se_socktype = SOCK_STREAM;
328 else if (strcmp(cp_ptr, "dgram") == 0)
329 sep->se_socktype = SOCK_DGRAM;
330 else if (strcmp(cp_ptr, "rdm") == 0)
331 sep->se_socktype = SOCK_RDM;
332 else if (strcmp(cp_ptr, "seqpacket") == 0)
333 sep->se_socktype = SOCK_SEQPACKET;
334 else if (strcmp(cp_ptr, "raw") == 0)
335 sep->se_socktype = SOCK_RAW;
336 else
337 sep->se_socktype = -1;
338
339 cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr);
340 if (cp_ptr == NULL) {
341 /* error */
342 goto more;
343 }
344 if (strcmp(cp_ptr, "unix") == 0) {
345 sep->se_family = AF_UNIX;
346 } else {
347 if (strncmp(cp_ptr, "rpc/", 4) == 0) {
348 syslog(LOG_ERR, "%s: rpc services not supported",
349 sep->se_service);
350 goto more;
351 }
352 sep->se_family = AF_INET;
353 }
354 sep->se_proto = inetd_strdup(cp_ptr);
355
356 cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr);
357 if (cp_ptr == NULL) {
358 /* error */
359 goto more;
360 }
361 {
362 char *s = strchr(cp_ptr, '.');
363 if (s) {
364 *s++ = '\0';
365 sep->se_max = atoi(s);
366 } else
367 sep->se_max = TOOMANY;
368 }
369 sep->se_wait = strcmp(cp_ptr, "wait") == 0;
370
371 cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr);
372 if (cp_ptr == NULL) {
373 /* error */
374 goto more;
375 }
376
377 sep->se_user = inetd_strdup(cp_ptr);
378 {
379 char *cp_ptr2 = strchr(sep->se_user, '.');
380
381 if (cp_ptr2) {
382 *cp_ptr2++ = '\0';
383 }
384 sep->se_group = cp_ptr2;
385 }
386
387 cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr);
388 if (cp_ptr == NULL) {
389 /* error */
390 goto more;
391 }
392 if (strcmp(cp_ptr, "internal") == 0) {
393#ifdef INETD_FEATURE_ENABLED
394 const struct biltin *bi;
395
396 for (bi = biltins; bi->bi_service; bi++) {
397 if ((bi->bi_socktype == sep->se_socktype) &&
398 (strcmp(bi->bi_service, sep->se_service) == 0)) {
399 break;
400 }
401 }
402 if (bi->bi_service == 0) {
403 syslog(LOG_ERR, "internal service %s unknown", sep->se_service);
404 goto more;
405 }
406 sep->se_bi = bi;
407 sep->se_wait = bi->bi_wait;
408#else
409 syslog(LOG_ERR, "internal service %s unknown", cp_ptr);
410 goto more;
411#endif
412 }
413#ifdef INETD_FEATURE_ENABLED
414 else {
415 sep->se_bi = NULL;
416 }
417#endif
418 sep->se_server = inetd_strdup(cp_ptr);
419
420 argc = 0;
421 while ((cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr)) != NULL) {
422 if (argc < MAXARGV) {
423 sep->se_argv[argc++] = inetd_strdup(cp_ptr);
424 }
425 }
426 free(cp);
427
428 return (sep);
429}
430
431static void freeconfig(servtab_t *cp)
432{
433 int i;
434
435 free(cp->se_service);
436 free(cp->se_proto);
437 free(cp->se_user);
438 /* Note: se_group is part of the newstr'ed se_user */
439 free(cp->se_server);
440 for (i = 0; i < MAXARGV; i++)
441 free(cp->se_argv[i]);
442}
443
444#ifdef INETD_FEATURE_ENABLED
445static char **Argv;
446static char *LastArg;
447
448static void setproctitle(char *a, int s)
449{
450 size_t size;
451 char *cp;
452 struct sockaddr_in sn;
453 char buf[80];
454
455 cp = Argv[0];
456 size = sizeof(sn);
457 if (getpeername(s, (struct sockaddr *)&sn, &size) == 0)
458 (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sn.sin_addr));
459 else
460 (void) sprintf(buf, "-%s", a);
461 strncpy(cp, buf, LastArg - cp);
462 cp += strlen(cp);
463 while (cp < LastArg)
464 *cp++ = ' ';
465}
466#endif /* INETD_FEATURE_ENABLED */
467
468
469static void setup(servtab_t *sep)
470{
471 int on = 1;
472
473 if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
474 syslog(LOG_ERR, "%s/%s: socket: %m",
475 sep->se_service, sep->se_proto);
476 return;
477 }
478 if (setsockopt(sep->se_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
479 sizeof(on)) < 0)
480 syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
481 if (bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) {
482 syslog(LOG_ERR, "%s/%s: bind: %m",
483 sep->se_service, sep->se_proto);
484 (void) close(sep->se_fd);
485 sep->se_fd = -1;
486 if (!timingout) {
487 timingout = 1;
488 alarm(RETRYTIME);
489 }
490 return;
491 }
492 if (sep->se_socktype == SOCK_STREAM)
493 listen(sep->se_fd, global_queuelen);
494
495 FD_SET(sep->se_fd, &allsock);
496 nsock++;
497 if (sep->se_fd > maxsock) {
498 maxsock = sep->se_fd;
499 if (maxsock > rlim_ofile_cur - FD_MARGIN) {
500#ifdef RLIMIT_NOFILE
501# define FD_CHUNK 32
502 struct rlimit rl;
503
504 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
505 syslog(LOG_ERR, "getrlimit: %m");
506 return;
507 }
508 rl.rlim_cur = rl.rlim_max < (rl.rlim_cur + FD_CHUNK) ? rl.rlim_max : (rl.rlim_cur + FD_CHUNK);
509 if (rl.rlim_cur <= rlim_ofile_cur) {
510 syslog(LOG_ERR,
511# if _FILE_OFFSET_BITS == 64
512 "bump_nofile: cannot extend file limit, max = %lld",
513# else
514 "bump_nofile: cannot extend file limit, max = %ld",
515# endif
516 rl.rlim_cur);
517 return;
518 }
519
520 if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
521 syslog(LOG_ERR, "setrlimit: %m");
522 return;
523 }
524
525 rlim_ofile_cur = rl.rlim_cur;
526 return;
527#else
528 syslog(LOG_ERR, "bump_nofile: cannot extend file limit");
529 return;
530#endif /* RLIMIT_NOFILE */
531 }
532 }
533}
534
535static void config(int signum)
536{
537 servtab_t *sep, *cp, **sepp;
538 sigset_t oldmask;
539 unsigned n;
540
541 (void)signum;
542
543 if (fconfig != NULL) {
544 fseek(fconfig, 0L, L_SET);
545 } else {
546 fconfig = fopen(CONFIG, "r");
547 if (fconfig == NULL) {
548 syslog(LOG_ERR, "%s: %m", CONFIG);
549 return;
550 }
551 }
552
553 for (sep = servtab; sep; sep = sep->se_next)
554 sep->se_checked = 0;
555 while ((cp = getconfigent()) != NULL) {
556 for (sep = servtab; sep; sep = sep->se_next)
557 if (strcmp(sep->se_service, cp->se_service) == 0 &&
558 strcmp(sep->se_proto, cp->se_proto) == 0)
559 break;
560 if (sep != 0) {
561 int i;
562
563#define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;}
564
565 sigprocmask(SIG_BLOCK, &emptymask, &oldmask);
566 /*
567 * sep->se_wait may be holding the pid of a daemon
568 * that we're waiting for. If so, don't overwrite
569 * it unless the config file explicitly says don't
570 * wait.
571 */
572 if (
573#ifdef INETD_FEATURE_ENABLED
574 cp->se_bi == 0 &&
575#endif
576 (sep->se_wait == 1 || cp->se_wait == 0))
577 sep->se_wait = cp->se_wait;
578 if (cp->se_max != sep->se_max)
579 SWAP(int, cp->se_max, sep->se_max);
580 if (cp->se_user)
581 SWAP(char *, sep->se_user, cp->se_user);
582 if (cp->se_group)
583 SWAP(char *, sep->se_group, cp->se_group);
584 if (cp->se_server)
585 SWAP(char *, sep->se_server, cp->se_server);
586 for (i = 0; i < MAXARGV; i++)
587 SWAP(char *, sep->se_argv[i], cp->se_argv[i]);
588#undef SWAP
589 sigprocmask(SIG_SETMASK, &oldmask, NULL);
590 // This freeconfig() is probably a bug, since it will try and free()
591 // each of the argv[] values, which are really just pointers
592 // into the middle of a single line buffer for the config file.
593 //freeconfig(cp); // BUG?
594 } else {
595 sep = (servtab_t *)xmalloc(sizeof (*sep));
596 *sep = *cp;
597 sep->se_fd = -1;
598 sigprocmask(SIG_BLOCK, &blockmask, &oldmask);
599 sep->se_next = servtab;
600 servtab = sep;
601 sigprocmask(SIG_SETMASK, &oldmask, NULL);
602 }
603 sep->se_checked = 1;
604
605 switch (sep->se_family) {
606 case AF_UNIX:
607 if (sep->se_fd != -1)
608 break;
609 (void)unlink(sep->se_service);
610 n = strlen(sep->se_service);
611 if (n > sizeof(sep->se_ctrladdr_un.sun_path) - 1)
612 n = sizeof(sep->se_ctrladdr_un.sun_path) - 1;
613 strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n);
614 sep->se_ctrladdr_un.sun_family = AF_UNIX;
615 sep->se_ctrladdr_size = n +
616 sizeof sep->se_ctrladdr_un.sun_family;
617 setup(sep);
618 break;
619 case AF_INET:
620 sep->se_ctrladdr_in.sin_family = AF_INET;
621 sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in;
622 {
623 u_short port = bb_lookup_port(sep->se_service, sep->se_proto, 0);
624
625 if (port == 0) {
626 syslog(LOG_ERR,
627 "%s/%s: unknown service",
628 sep->se_service, sep->se_proto);
629 continue;
630 }
631 if (port != sep->se_ctrladdr_in.sin_port) {
632 sep->se_ctrladdr_in.sin_port = port;
633 if (sep->se_fd != -1) {
634 FD_CLR(sep->se_fd, &allsock);
635 nsock--;
636 (void) close(sep->se_fd);
637 }
638 sep->se_fd = -1;
639 }
640 if (sep->se_fd == -1)
641 setup(sep);
642 }
643 }
644 }
645 if (fconfig) {
646 (void) fclose(fconfig);
647 fconfig = NULL;
648 }
649 /*
650 * Purge anything not looked at above.
651 */
652 sigprocmask(SIG_SETMASK, &blockmask, &oldmask);
653 sepp = &servtab;
654 while ((sep = *sepp) != NULL) {
655 if (sep->se_checked) {
656 sepp = &sep->se_next;
657 continue;
658 }
659 *sepp = sep->se_next;
660 if (sep->se_fd != -1) {
661 FD_CLR(sep->se_fd, &allsock);
662 nsock--;
663 (void) close(sep->se_fd);
664 }
665 if (sep->se_family == AF_UNIX)
666 (void)unlink(sep->se_service);
667 freeconfig(sep);
668 free((char *)sep);
669 }
670 sigprocmask(SIG_SETMASK, &oldmask, NULL);
671}
672
673
674
675static void reapchild(int signum)
676{
677 int status;
678 int pid;
679 servtab_t *sep;
680
681 (void)signum;
682 for (;;) {
683 pid = wait3(&status, WNOHANG, (struct rusage *)0);
684 if (pid <= 0)
685 break;
686 for (sep = servtab; sep; sep = sep->se_next)
687 if (sep->se_wait == pid) {
688 if (WIFEXITED(status) && WEXITSTATUS(status))
689 syslog(LOG_WARNING,
690 "%s: exit status 0x%x",
691 sep->se_server, WEXITSTATUS(status));
692 else if (WIFSIGNALED(status))
693 syslog(LOG_WARNING,
694 "%s: exit signal 0x%x",
695 sep->se_server, WTERMSIG(status));
696 sep->se_wait = 1;
697 FD_SET(sep->se_fd, &allsock);
698 nsock++;
699 }
700 }
701}
702
703static void retry(int signum)
704{
705 servtab_t *sep;
706
707 (void)signum;
708 timingout = 0;
709 for (sep = servtab; sep; sep = sep->se_next) {
710 if (sep->se_fd == -1) {
711 switch (sep->se_family) {
712 case AF_UNIX:
713 case AF_INET:
714 setup(sep);
715 break;
716 }
717 }
718 }
719}
720
721static void goaway(int signum)
722{
723 servtab_t *sep;
724
725 (void)signum;
726 for (sep = servtab; sep; sep = sep->se_next)
727 if (sep->se_fd != -1 && sep->se_family == AF_UNIX)
728 (void)unlink(sep->se_service);
729 (void)unlink(_PATH_INETDPID);
730 exit(0);
731}
732
733
734
735extern int inetd_main(int argc, char *argv[])
736{
737 servtab_t *sep;
738 struct group *grp = NULL;
739 struct sigaction sa;
740 int pid;
741 unsigned long opt;
742 char *sq;
743 gid_t gid;
744
745#ifdef INETD_FEATURE_ENABLED
746 extern char **environ;
747#endif
748
749 gid = getgid();
750 setgroups(1, &gid);
751
752#ifdef INETD_FEATURE_ENABLED
753 Argv = argv;
754 if (environ == 0 || *environ == 0)
755 environ = argv;
756 while (*environ)
757 environ++;
758 LastArg = environ[-1] + strlen(environ[-1]);
759#endif
760
761#if defined(__uClinux__)
762 opt = bb_getopt_ulflags(argc, argv, "q:f", &sq);
763 if (!(opt & 2)) {
764 daemon(0, 0);
765 /* reexec for vfork() do continue parent */
766 vfork_daemon_rexec(argc, argv, "-f");
767 }
768#else
769 opt = bb_getopt_ulflags(argc, argv, "q:", &sq);
770 daemon(0, 0);
771#endif /* uClinux */
772
773 if(opt & 1) {
774 global_queuelen = atoi(sq);
775 if (global_queuelen < 8) global_queuelen=8;
776 }
777 argc -= optind;
778 argv += optind;
779
780 if (argc > 0)
781 CONFIG = argv[0];
782
783 openlog(bb_applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
784 {
785 FILE *fp;
786
787 if ((fp = fopen(_PATH_INETDPID, "w")) != NULL) {
788 fprintf(fp, "%u\n", getpid());
789 (void)fclose(fp);
790 }
791 }
792
793#ifdef RLIMIT_NOFILE
794 if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
795 syslog(LOG_ERR, "getrlimit: %m");
796 } else {
797 rlim_ofile_cur = rlim_ofile.rlim_cur;
798 if (rlim_ofile_cur == RLIM_INFINITY) /* ! */
799 rlim_ofile_cur = OPEN_MAX;
800 }
801#endif
802
803 config(0);
804
805 sigemptyset(&emptymask);
806 sigemptyset(&blockmask);
807 sigaddset(&blockmask, SIGCHLD);
808 sigaddset(&blockmask, SIGHUP);
809 sigaddset(&blockmask, SIGALRM);
810
811 memset(&sa, 0, sizeof(sa));
812 sa.sa_mask = blockmask;
813 sa.sa_handler = retry;
814 sigaction(SIGALRM, &sa, NULL);
815 sa.sa_handler = config;
816 sigaction(SIGHUP, &sa, NULL);
817 sa.sa_handler = reapchild;
818 sigaction(SIGCHLD, &sa, NULL);
819 sa.sa_handler = goaway;
820 sigaction(SIGTERM, &sa, NULL);
821 sa.sa_handler = goaway;
822 sigaction(SIGINT, &sa, NULL);
823 sa.sa_handler = SIG_IGN;
824 sigaction(SIGPIPE, &sa, NULL);
825
826 {
827 /* space for daemons to overwrite environment for ps */
828#define DUMMYSIZE 100
829 char dummy[DUMMYSIZE];
830
831 (void)memset(dummy, 'x', DUMMYSIZE - 1);
832 dummy[DUMMYSIZE - 1] = '\0';
833
834 (void)setenv("inetd_dummy", dummy, 1);
835 }
836
837 for (;;) {
838 fd_set readable;
839 int ctrl;
840 int n;
841
842 if (nsock == 0) {
843 sigprocmask(SIG_BLOCK, &blockmask, NULL);
844 while (nsock == 0) {
845 sigsuspend(&emptymask);
846 }
847 sigprocmask(SIG_SETMASK, &emptymask, NULL);
848 }
849 readable = allsock;
850 n = select(maxsock + 1, &readable, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
851 if (n <= 0) {
852 if (n < 0 && errno != EINTR) {
853 syslog(LOG_WARNING, "select: %m");
854 }
855 sleep(1);
856 continue;
857 }
858 for (sep = servtab; n && sep; sep = sep->se_next) {
859 if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
860 n--;
861 if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
862 /* Fixed AGC */
863 fcntl(sep->se_fd, F_SETFL, O_NDELAY);
864 /* --------- */
865 ctrl = accept(sep->se_fd, NULL, NULL);
866 fcntl(sep->se_fd, F_SETFL, 0);
867 if (ctrl < 0) {
868 if (errno == EINTR || errno == EWOULDBLOCK) {
869 continue;
870 }
871 syslog(LOG_WARNING, "accept (for %s): %m",
872 sep->se_service);
873 continue;
874 }
875 } else {
876 ctrl = sep->se_fd;
877 }
878 sigprocmask(SIG_BLOCK, &blockmask, NULL);
879 pid = 0;
880#ifdef INETD_FEATURE_ENABLED
881 if (sep->se_bi == 0 || sep->se_bi->bi_fork)
882#endif
883 {
884 if (sep->se_count++ == 0) {
885 gettimeofday(&sep->se_time, (struct timezone *)0);
886 }
887 else if (sep->se_count >= sep->se_max) {
888 struct timeval now;
889
890 gettimeofday(&now, (struct timezone *)0);
891 if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) {
892 sep->se_time = now;
893 sep->se_count = 1;
894 } else {
895 syslog(LOG_ERR,
896 "%s/%s server failing (looping), service terminated",
897 sep->se_service, sep->se_proto);
898 FD_CLR(sep->se_fd, &allsock);
899 close(sep->se_fd);
900 sep->se_fd = -1;
901 sep->se_count = 0;
902 nsock--;
903 sigprocmask(SIG_SETMASK, &emptymask, NULL);
904 if (!timingout) {
905 timingout = 1;
906 alarm(RETRYTIME);
907 }
908 continue;
909 }
910 }
911 pid = fork();
912 if (pid < 0) {
913 syslog(LOG_ERR, "fork: %m");
914 if (sep->se_socktype == SOCK_STREAM) {
915 close(ctrl);
916 }
917 sigprocmask(SIG_SETMASK, &emptymask, NULL);
918 sleep(1);
919 continue;
920 }
921 if (pid && sep->se_wait) {
922 sep->se_wait = pid;
923 FD_CLR(sep->se_fd, &allsock);
924 nsock--;
925 }
926 }
927 sigprocmask(SIG_SETMASK, &emptymask, NULL);
928 if (pid == 0) {
929#ifdef INETD_FEATURE_ENABLED
930 if (sep->se_bi) {
931 (*sep->se_bi->bi_fn)(ctrl, sep);
932 } else
933#endif
934 {
935 struct passwd *pwd = getpwnam(sep->se_user);
936 if (pwd == NULL) {
937 syslog_err_and_discard_dg(
938 sep->se_socktype,
939 "getpwnam: %s: No such user",
940 sep->se_user);
941 }
942 if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) {
943 syslog_err_and_discard_dg(sep->se_socktype,
944 "getgrnam: %s: No such group", sep->se_group);
945 }
946 /*
947 * Ok. There are four cases here:
948 * 1. nonroot user, no group specified
949 * 2. nonroot user, some group specified
950 * 3. root user, no group specified
951 * 4. root user, some group specified
952 * In cases 2 and 4 we setgid to the specified
953 * group. In cases 1 and 2 we run initgroups
954 * to run with the groups of the given user.
955 * In case 4 we do setgroups to run with the
956 * given group. In case 3 we do nothing.
957 */
958 if (pwd->pw_uid) {
959 if (sep->se_group) {
960 pwd->pw_gid = grp->gr_gid;
961 }
962 setgid((gid_t)pwd->pw_gid);
963 initgroups(pwd->pw_name, pwd->pw_gid);
964 setuid((uid_t)pwd->pw_uid);
965 } else if (sep->se_group) {
966 setgid((gid_t)grp->gr_gid);
967 setgroups(1, &grp->gr_gid);
968 }
969 dup2(ctrl, 0);
970 close(ctrl);
971 dup2(0, 1);
972 dup2(0, 2);
973#ifdef RLIMIT_NOFILE
974 if (rlim_ofile.rlim_cur != rlim_ofile_cur) {
975 if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
976 syslog(LOG_ERR,"setrlimit: %m");
977 }
978 }
979#endif
980 for (ctrl = rlim_ofile_cur-1; --ctrl > 2; ) {
981 (void)close(ctrl);
982 }
983 memset(&sa, 0, sizeof(sa));
984 sa.sa_handler = SIG_DFL;
985 sigaction(SIGPIPE, &sa, NULL);
986
987 execv(sep->se_server, sep->se_argv);
988 syslog_err_and_discard_dg(sep->se_socktype, "execv %s: %m", sep->se_server);
989 }
990 }
991 if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
992 close(ctrl);
993 }
994 }
995 }
996 }
997}
998
999
1000/*
1001 * Internet services provided internally by inetd:
1002 */
1003#define BUFSIZE 4096
1004
1005#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO
1006/* Echo service -- echo data back */
1007static void echo_stream(int s, servtab_t *sep)
1008{
1009 char buffer[BUFSIZE];
1010 int i;
1011
1012 setproctitle(sep->se_service, s);
1013 while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
1014 write(s, buffer, i) > 0)
1015 ;
1016 exit(0);
1017}
1018
1019/* Echo service -- echo data back */
1020static void echo_dg(int s, servtab_t *sep)
1021{
1022 char buffer[BUFSIZE];
1023 int i;
1024 size_t size;
1025 struct sockaddr sa;
1026
1027 (void)sep;
1028
1029 size = sizeof(sa);
1030 if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
1031 return;
1032 (void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
1033}
1034#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO */
1035
1036
1037#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD
1038/* Discard service -- ignore data */
1039static void discard_stream(int s, servtab_t *sep)
1040{
1041 char buffer[BUFSIZE];
1042
1043 setproctitle(sep->se_service, s);
1044 while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) ||
1045 errno == EINTR)
1046 ;
1047 exit(0);
1048}
1049
1050/* Discard service -- ignore data */
1051static void discard_dg(int s, servtab_t *sep)
1052{
1053 char buffer[BUFSIZE];
1054 (void)sep;
1055 read(s, buffer, sizeof(buffer));
1056}
1057#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD */
1058
1059
1060#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN
1061#include <ctype.h>
1062#define LINESIZ 72
1063static char ring[128];
1064static char *endring;
1065
1066static void initring(void)
1067{
1068 int i;
1069
1070 endring = ring;
1071
1072 for (i = 0; i <= 128; ++i)
1073 if (isprint(i))
1074 *endring++ = i;
1075}
1076
1077/* Character generator */
1078static void chargen_stream(int s, servtab_t *sep)
1079{
1080 char *rs;
1081 int len;
1082 char text[LINESIZ+2];
1083
1084 setproctitle(sep->se_service, s);
1085
1086 if (!endring) {
1087 initring();
1088 rs = ring;
1089 }
1090
1091 text[LINESIZ] = '\r';
1092 text[LINESIZ + 1] = '\n';
1093 for (rs = ring;;) {
1094 if ((len = endring - rs) >= LINESIZ)
1095 memcpy(rs, text, LINESIZ);
1096 else {
1097 memcpy(rs, text, len);
1098 memcpy(ring, text + len, LINESIZ - len);
1099 }
1100 if (++rs == endring)
1101 rs = ring;
1102 if (write(s, text, sizeof(text)) != sizeof(text))
1103 break;
1104 }
1105 exit(0);
1106}
1107
1108/* Character generator */
1109static void chargen_dg(int s, servtab_t *sep)
1110{
1111 struct sockaddr sa;
1112 static char *rs;
1113 size_t len, size;
1114 char text[LINESIZ+2];
1115
1116 (void)sep;
1117
1118 if (endring == 0) {
1119 initring();
1120 rs = ring;
1121 }
1122
1123 size = sizeof(sa);
1124 if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
1125 return;
1126
1127 if ((len = endring - rs) >= LINESIZ)
1128 memcpy(rs, text, LINESIZ);
1129 else {
1130 memcpy(rs, text, len);
1131 memcpy(ring, text + len, LINESIZ - len);
1132 }
1133 if (++rs == endring)
1134 rs = ring;
1135 text[LINESIZ] = '\r';
1136 text[LINESIZ + 1] = '\n';
1137 (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
1138}
1139#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN */
1140
1141
1142#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME
1143/*
1144 * Return a machine readable date and time, in the form of the
1145 * number of seconds since midnight, Jan 1, 1900. Since gettimeofday
1146 * returns the number of seconds since midnight, Jan 1, 1970,
1147 * we must add 2208988800 seconds to this figure to make up for
1148 * some seventy years Bell Labs was asleep.
1149 */
1150
1151static long machtime(void)
1152{
1153 struct timeval tv;
1154
1155 if (gettimeofday(&tv, (struct timezone *)0) < 0) {
1156 fprintf(stderr, "Unable to get time of day\n");
1157 return (0L);
1158 }
1159 return (htonl((long)tv.tv_sec + 2208988800UL));
1160}
1161
1162static void machtime_stream(int s, servtab_t *sep)
1163{
1164 long result;
1165 (void)sep;
1166
1167 result = machtime();
1168 write(s, (char *) &result, sizeof(result));
1169}
1170
1171static void machtime_dg(int s, servtab_t *sep)
1172{
1173 long result;
1174 struct sockaddr sa;
1175 size_t size;
1176 (void)sep;
1177
1178 size = sizeof(sa);
1179 if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
1180 return;
1181 result = machtime();
1182 (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
1183}
1184#endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME */
1185
1186
1187#ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME
1188/* Return human-readable time of day */
1189static int human_readable_time_sprintf(char *buffer)
1190{
1191 time_t clocc = time(NULL);
1192
1193 return sprintf(buffer, "%.24s\r\n", ctime(&clocc));
1194}
1195
1196static void daytime_stream(int s, servtab_t *sep)
1197{
1198 char buffer[256];
1199 size_t st = human_readable_time_sprintf(buffer);
1200
1201 (void)sep;
1202
1203 write(s, buffer, st);
1204}
1205
1206/* Return human-readable time of day */
1207static void daytime_dg(int s, servtab_t *sep)
1208{
1209 char buffer[256];
1210 struct sockaddr sa;
1211 size_t size;
1212
1213 (void)sep;
1214
1215 size = sizeof(sa);
1216 if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
1217 return;
1218 size = human_readable_time_sprintf(buffer);
1219 sendto(s, buffer, size, 0, &sa, sizeof(sa));
1220}
1221#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 @@
1/*
2 * ip.c "ip" utility frontend.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 *
12 * Changes:
13 *
14 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <unistd.h>
20#include <syslog.h>
21#include <fcntl.h>
22#include <sys/socket.h>
23#include <netinet/in.h>
24#include <string.h>
25
26#include "./libiproute/utils.h"
27#include "./libiproute/ip_common.h"
28
29#include "busybox.h"
30
31#if 0
32int preferred_family = AF_UNSPEC;
33int oneline = 0;
34char * _SL_ = NULL;
35
36void ip_parse_common_args(int *argcp, char ***argvp)
37{
38 int argc = *argcp;
39 char **argv = *argvp;
40
41 while (argc > 1) {
42 char *opt = argv[1];
43
44 if (strcmp(opt,"--") == 0) {
45 argc--; argv++;
46 break;
47 }
48
49 if (opt[0] != '-')
50 break;
51
52 if (opt[1] == '-')
53 opt++;
54
55 if (matches(opt, "-family") == 0) {
56 argc--;
57 argv++;
58 if (strcmp(argv[1], "inet") == 0)
59 preferred_family = AF_INET;
60 else if (strcmp(argv[1], "inet6") == 0)
61 preferred_family = AF_INET6;
62 else if (strcmp(argv[1], "link") == 0)
63 preferred_family = AF_PACKET;
64 else
65 invarg(argv[1], "invalid protocol family");
66 } else if (strcmp(opt, "-4") == 0) {
67 preferred_family = AF_INET;
68 } else if (strcmp(opt, "-6") == 0) {
69 preferred_family = AF_INET6;
70 } else if (strcmp(opt, "-0") == 0) {
71 preferred_family = AF_PACKET;
72 } else if (matches(opt, "-oneline") == 0) {
73 ++oneline;
74 } else {
75 bb_show_usage();
76 }
77 argc--; argv++;
78 }
79 _SL_ = oneline ? "\\" : "\n" ;
80}
81#endif
82
83int ip_main(int argc, char **argv)
84{
85 int ret = EXIT_FAILURE;
86
87 ip_parse_common_args(&argc, &argv);
88
89 if (argc > 1) {
90#ifdef CONFIG_FEATURE_IP_ADDRESS
91 if (matches(argv[1], "address") == 0) {
92 ret = do_ipaddr(argc-2, argv+2);
93 }
94#endif
95#ifdef CONFIG_FEATURE_IP_ROUTE
96 if (matches(argv[1], "route") == 0) {
97 ret = do_iproute(argc-2, argv+2);
98 }
99#endif
100#ifdef CONFIG_FEATURE_IP_LINK
101 if (matches(argv[1], "link") == 0) {
102 ret = do_iplink(argc-2, argv+2);
103 }
104#endif
105#ifdef CONFIG_FEATURE_IP_TUNNEL
106 if (matches(argv[1], "tunnel") == 0 || strcmp(argv[1], "tunl") == 0) {
107 ret = do_iptunnel(argc-2, argv+2);
108 }
109#endif
110 }
111 if (ret) {
112 bb_show_usage();
113 }
114 return(EXIT_SUCCESS);
115}
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 @@
1/*
2 * ip.c "ip" utility frontend.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 *
12 * Changes:
13 *
14 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
15 */
16
17#include "./libiproute/utils.h"
18#include "./libiproute/ip_common.h"
19
20#include "busybox.h"
21
22int ipaddr_main(int argc, char **argv)
23{
24 ip_parse_common_args(&argc, &argv);
25
26 return do_ipaddr(argc-1, argv+1);
27}
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 @@
1/* vi: set sw=4 ts=4 ai: */
2/*
3 * Mini ipcalc implementation for busybox
4 *
5 * By Jordan Crouse <jordan@cosmicpenguin.net>
6 * Stephan Linz <linz@li-pro.net>
7 *
8 * This is a complete reimplementation of the ipcalc program
9 * from Red Hat. I didn't look at their source code, but there
10 * is no denying that this is a loving reimplementation
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <ctype.h>
16#include <getopt.h>
17#include <sys/socket.h>
18#include <netinet/in.h>
19#include <arpa/inet.h>
20
21#include "busybox.h"
22
23#define IPCALC_MSG(CMD,ALTCMD) if (mode & SILENT) {ALTCMD;} else {CMD;}
24
25#define CLASS_A_NETMASK ntohl(0xFF000000)
26#define CLASS_B_NETMASK ntohl(0xFFFF0000)
27#define CLASS_C_NETMASK ntohl(0xFFFFFF00)
28
29static unsigned long get_netmask(unsigned long ipaddr)
30{
31 ipaddr = htonl(ipaddr);
32
33 if ((ipaddr & 0xC0000000) == 0xC0000000)
34 return CLASS_C_NETMASK;
35 else if ((ipaddr & 0x80000000) == 0x80000000)
36 return CLASS_B_NETMASK;
37 else if ((ipaddr & 0x80000000) == 0)
38 return CLASS_A_NETMASK;
39 else
40 return 0;
41}
42
43#ifdef CONFIG_FEATURE_IPCALC_FANCY
44static int get_prefix(unsigned long netmask)
45{
46 unsigned long msk = 0x80000000;
47 int ret = 0;
48
49 netmask = htonl(netmask);
50 while(msk) {
51 if (netmask & msk)
52 ret++;
53 msk >>= 1;
54 }
55 return ret;
56}
57#endif
58
59#define NETMASK 0x01
60#define BROADCAST 0x02
61#define NETWORK 0x04
62#define NETPREFIX 0x08
63#define HOSTNAME 0x10
64#define SILENT 0x20
65
66
67int ipcalc_main(int argc, char **argv)
68{
69 unsigned long mode;
70
71 in_addr_t netmask;
72 in_addr_t broadcast;
73 in_addr_t network;
74 in_addr_t ipaddr;
75 struct in_addr a;
76
77#ifdef CONFIG_FEATURE_IPCALC_FANCY
78 unsigned long netprefix = 0;
79 int have_netmask = 0;
80 char *ipstr, *prefixstr;
81#endif
82
83 static const struct option long_options[] = {
84 {"netmask", no_argument, NULL, 'm'},
85 {"broadcast", no_argument, NULL, 'b'},
86 {"network", no_argument, NULL, 'n'},
87#ifdef CONFIG_FEATURE_IPCALC_FANCY
88 {"prefix", no_argument, NULL, 'p'},
89 {"hostname", no_argument, NULL, 'h'},
90 {"silent", no_argument, NULL, 's'},
91#endif
92 {NULL, 0, NULL, 0}
93 };
94
95 bb_applet_long_options = long_options;
96 mode = bb_getopt_ulflags(argc, argv,
97#ifdef CONFIG_FEATURE_IPCALC_FANCY
98 "mbnphs"
99#else
100 "mbn"
101#endif
102 );
103 if (mode & (BROADCAST | NETWORK | NETPREFIX)) {
104 if (argc - optind > 2) {
105 bb_show_usage();
106 }
107 } else {
108 if (argc - optind != 1) {
109 bb_show_usage();
110 }
111 }
112
113#ifdef CONFIG_FEATURE_IPCALC_FANCY
114 prefixstr = ipstr = argv[optind];
115
116 while(*prefixstr) {
117 if (*prefixstr == '/') {
118 *prefixstr = (char)0;
119 prefixstr++;
120 if (*prefixstr) {
121 unsigned int msk;
122
123 if (safe_strtoul(prefixstr, &netprefix) || netprefix > 32) {
124 IPCALC_MSG(bb_error_msg_and_die("bad IP prefix: %s\n", prefixstr),
125 exit(EXIT_FAILURE));
126 }
127 netmask = 0;
128 msk = 0x80000000;
129 while (netprefix > 0) {
130 netmask |= msk;
131 msk >>= 1;
132 netprefix--;
133 }
134 netmask = htonl(netmask);
135 /* Even if it was 0, we will signify that we have a netmask. This allows */
136 /* for specification of default routes, etc which have a 0 netmask/prefix */
137 have_netmask = 1;
138 }
139 break;
140 }
141 prefixstr++;
142 }
143 ipaddr = inet_aton(ipstr, &a);
144#else
145 ipaddr = inet_aton(argv[optind], &a);
146#endif
147
148 if (ipaddr == 0) {
149 IPCALC_MSG(bb_error_msg_and_die("bad IP address: %s", argv[optind]),
150 exit(EXIT_FAILURE));
151 }
152 ipaddr = a.s_addr;
153
154 if (argc - optind == 2) {
155#ifdef CONFIG_FEATURE_IPCALC_FANCY
156 if (have_netmask) {
157 IPCALC_MSG(bb_error_msg_and_die("Both prefix and netmask were specified, use one or the other.\n"),
158 exit(EXIT_FAILURE));
159 }
160
161#endif
162 netmask = inet_aton(argv[optind + 1], &a);
163 if (netmask == 0) {
164 IPCALC_MSG(bb_error_msg_and_die("bad netmask: %s", argv[optind + 1]),
165 exit(EXIT_FAILURE));
166 }
167 netmask = a.s_addr;
168 } else {
169#ifdef CONFIG_FEATURE_IPCALC_FANCY
170
171 if (!have_netmask)
172#endif
173 /* JHC - If the netmask wasn't provided then calculate it */
174 netmask = get_netmask(ipaddr);
175 }
176
177 if (mode & NETMASK) {
178 printf("NETMASK=%s\n", inet_ntoa((*(struct in_addr *) &netmask)));
179 }
180
181 if (mode & BROADCAST) {
182 broadcast = (ipaddr & netmask) | ~netmask;
183 printf("BROADCAST=%s\n", inet_ntoa((*(struct in_addr *) &broadcast)));
184 }
185
186 if (mode & NETWORK) {
187 network = ipaddr & netmask;
188 printf("NETWORK=%s\n", inet_ntoa((*(struct in_addr *) &network)));
189 }
190
191#ifdef CONFIG_FEATURE_IPCALC_FANCY
192 if (mode & NETPREFIX) {
193 printf("PREFIX=%i\n", get_prefix(netmask));
194 }
195
196 if (mode & HOSTNAME) {
197 struct hostent *hostinfo;
198 int x;
199
200 hostinfo = gethostbyaddr((char *) &ipaddr, sizeof(ipaddr), AF_INET);
201 if (!hostinfo) {
202 IPCALC_MSG(bb_herror_msg_and_die(
203 "cannot find hostname for %s", argv[optind]),);
204 exit(EXIT_FAILURE);
205 }
206 for (x = 0; hostinfo->h_name[x]; x++) {
207 hostinfo->h_name[x] = tolower(hostinfo->h_name[x]);
208 }
209
210 printf("HOSTNAME=%s\n", hostinfo->h_name);
211 }
212#endif
213
214 return EXIT_SUCCESS;
215}
216
217/* END CODE */
218/*
219Local Variables:
220c-file-style: "linux"
221c-basic-offset: 4
222tab-width: 4
223End:
224*/
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 @@
1/*
2 * ip.c "ip" utility frontend.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 *
12 * Changes:
13 *
14 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
15 */
16
17#include "./libiproute/utils.h"
18#include "./libiproute/ip_common.h"
19
20#include "busybox.h"
21
22int iplink_main(int argc, char **argv)
23{
24 ip_parse_common_args(&argc, &argv);
25
26 return do_iplink(argc-1, argv+1);
27}
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 @@
1/*
2 * ip.c "ip" utility frontend.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 *
12 * Changes:
13 *
14 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
15 */
16
17#include "./libiproute/utils.h"
18#include "./libiproute/ip_common.h"
19
20#include "busybox.h"
21
22int iproute_main(int argc, char **argv)
23{
24 ip_parse_common_args(&argc, &argv);
25
26 return do_iproute(argc-1, argv+1);
27}
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 @@
1/*
2 * ip.c "ip" utility frontend.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 *
12 * Changes:
13 *
14 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
15 */
16
17#include "./libiproute/utils.h"
18#include "./libiproute/ip_common.h"
19
20#include "busybox.h"
21
22int iptunnel_main(int argc, char **argv)
23{
24 ip_parse_common_args(&argc, &argv);
25
26 return do_iptunnel(argc-1, argv+1);
27}
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=../..
21top_builddir=../..
22srcdir=$(top_srcdir)/networking/libiproute
23LIBIPROUTE_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20LIBIPROUTE_AR:=libiproute.a
21ifndef $(LIBIPROUTE_DIR)
22LIBIPROUTE_DIR:=$(top_builddir)/networking/libiproute/
23endif
24srcdir=$(top_srcdir)/networking/libiproute
25
26LIBIPROUTE-$(CONFIG_IP) += \
27 ip_parse_common_args.o \
28 ipaddress.o \
29 iplink.o \
30 iproute.o \
31 iptunnel.o \
32 libnetlink.o \
33 ll_addr.o \
34 ll_map.o \
35 ll_proto.o \
36 ll_types.o \
37 rt_names.o \
38 rtm_map.o \
39 utils.o
40
41LIBIPROUTE-$(CONFIG_IPADDR) += \
42 ip_parse_common_args.o \
43 ipaddress.o \
44 libnetlink.o \
45 ll_addr.o \
46 ll_map.o \
47 ll_types.o \
48 rt_names.o \
49 utils.o
50
51LIBIPROUTE-$(CONFIG_IPLINK) += \
52 ip_parse_common_args.o \
53 ipaddress.o \
54 iplink.o \
55 libnetlink.o \
56 ll_addr.o \
57 ll_map.o \
58 ll_types.o \
59 rt_names.o \
60 utils.o
61
62LIBIPROUTE-$(CONFIG_IPROUTE) += \
63 ip_parse_common_args.o \
64 iproute.o \
65 libnetlink.o \
66 ll_map.o \
67 rt_names.o \
68 rtm_map.o \
69 utils.o
70
71LIBIPROUTE-$(CONFIG_IPTUNNEL) += \
72 ip_parse_common_args.o \
73 iptunnel.o \
74 rt_names.o \
75 utils.o
76
77libraries-y+=$(LIBIPROUTE_DIR)$(LIBIPROUTE_AR)
78
79$(LIBIPROUTE_DIR)$(LIBIPROUTE_AR): $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y))
80 $(AR) -ro $@ $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y))
81
82$(LIBIPROUTE_DIR)%.o: $(srcdir)/%.c
83 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
84
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 @@
1extern int preferred_family;
2extern char * _SL_;
3
4extern void ip_parse_common_args(int *argcp, char ***argvp);
5extern int print_neigh(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
6extern int ipaddr_list_or_flush(int argc, char **argv, int flush);
7extern int iproute_monitor(int argc, char **argv);
8extern void iplink_usage(void) __attribute__((noreturn));
9extern void ipneigh_reset_filter(void);
10extern int do_ipaddr(int argc, char **argv);
11extern int do_iproute(int argc, char **argv);
12extern int do_iprule(int argc, char **argv);
13extern int do_ipneigh(int argc, char **argv);
14extern int do_iptunnel(int argc, char **argv);
15extern int do_iplink(int argc, char **argv);
16extern int do_ipmonitor(int argc, char **argv);
17extern int do_multiaddr(int argc, char **argv);
18extern 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 @@
1/*
2 * ip.c "ip" utility frontend.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 *
12 * Changes:
13 *
14 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
15 */
16
17#include <string.h>
18
19#include "utils.h"
20#include "ip_common.h"
21
22#include "busybox.h"
23
24int preferred_family = AF_UNSPEC;
25int oneline = 0;
26char * _SL_ = NULL;
27
28void ip_parse_common_args(int *argcp, char ***argvp)
29{
30 int argc = *argcp;
31 char **argv = *argvp;
32
33 while (argc > 1) {
34 char *opt = argv[1];
35
36 if (strcmp(opt,"--") == 0) {
37 argc--; argv++;
38 break;
39 }
40
41 if (opt[0] != '-')
42 break;
43
44 if (opt[1] == '-')
45 opt++;
46
47 if (matches(opt, "-family") == 0) {
48 argc--;
49 argv++;
50 if (! argv[1])
51 bb_show_usage();
52 if (strcmp(argv[1], "inet") == 0)
53 preferred_family = AF_INET;
54 else if (strcmp(argv[1], "inet6") == 0)
55 preferred_family = AF_INET6;
56 else if (strcmp(argv[1], "link") == 0)
57 preferred_family = AF_PACKET;
58 else
59 invarg(argv[1], "invalid protocol family");
60 } else if (strcmp(opt, "-4") == 0) {
61 preferred_family = AF_INET;
62 } else if (strcmp(opt, "-6") == 0) {
63 preferred_family = AF_INET6;
64 } else if (strcmp(opt, "-0") == 0) {
65 preferred_family = AF_PACKET;
66 } else if (matches(opt, "-oneline") == 0) {
67 ++oneline;
68 } else {
69 bb_show_usage();
70 }
71 argc--; argv++;
72 }
73 _SL_ = oneline ? "\\" : "\n" ;
74 *argcp = argc;
75 *argvp = argv;
76}
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 @@
1/*
2 * ipaddress.c "ip address".
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 * Changes:
12 * Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated
13 */
14
15#include <sys/socket.h>
16#include <sys/ioctl.h>
17
18#include <fnmatch.h>
19#include <stdlib.h>
20#include <string.h>
21#include <unistd.h>
22
23#include <arpa/inet.h>
24
25#include <net/if.h>
26#include <net/if_arp.h>
27
28#include "rt_names.h"
29#include "utils.h"
30
31#include "libbb.h"
32
33static struct
34{
35 int ifindex;
36 int family;
37 int oneline;
38 int showqueue;
39 inet_prefix pfx;
40 int scope, scopemask;
41 int flags, flagmask;
42 int up;
43 char *label;
44 int flushed;
45 char *flushb;
46 int flushp;
47 int flushe;
48 struct rtnl_handle *rth;
49} filter;
50
51void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
52{
53 fprintf(fp, "<");
54 flags &= ~IFF_RUNNING;
55#define _PF(f) if (flags&IFF_##f) { \
56 flags &= ~IFF_##f ; \
57 fprintf(fp, #f "%s", flags ? "," : ""); }
58 _PF(LOOPBACK);
59 _PF(BROADCAST);
60 _PF(POINTOPOINT);
61 _PF(MULTICAST);
62 _PF(NOARP);
63#if 0
64 _PF(ALLMULTI);
65 _PF(PROMISC);
66 _PF(MASTER);
67 _PF(SLAVE);
68 _PF(DEBUG);
69 _PF(DYNAMIC);
70 _PF(AUTOMEDIA);
71 _PF(PORTSEL);
72 _PF(NOTRAILERS);
73#endif
74 _PF(UP);
75#undef _PF
76 if (flags)
77 fprintf(fp, "%x", flags);
78 if (mdown)
79 fprintf(fp, ",M-DOWN");
80 fprintf(fp, "> ");
81}
82
83static void print_queuelen(char *name)
84{
85 struct ifreq ifr;
86 int s;
87
88 s = socket(AF_INET, SOCK_STREAM, 0);
89 if (s < 0)
90 return;
91
92 memset(&ifr, 0, sizeof(ifr));
93 strcpy(ifr.ifr_name, name);
94 if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) {
95 perror("SIOCGIFXQLEN");
96 close(s);
97 return;
98 }
99 close(s);
100
101 if (ifr.ifr_qlen)
102 printf("qlen %d", ifr.ifr_qlen);
103}
104
105static int print_linkinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
106{
107 FILE *fp = (FILE*)arg;
108 struct ifinfomsg *ifi = NLMSG_DATA(n);
109 struct rtattr * tb[IFLA_MAX+1];
110 int len = n->nlmsg_len;
111 unsigned m_flag = 0;
112
113 if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
114 return 0;
115
116 len -= NLMSG_LENGTH(sizeof(*ifi));
117 if (len < 0)
118 return -1;
119
120 if (filter.ifindex && ifi->ifi_index != filter.ifindex)
121 return 0;
122 if (filter.up && !(ifi->ifi_flags&IFF_UP))
123 return 0;
124
125 memset(tb, 0, sizeof(tb));
126 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
127 if (tb[IFLA_IFNAME] == NULL) {
128 bb_error_msg("nil ifname");
129 return -1;
130 }
131 if (filter.label &&
132 (!filter.family || filter.family == AF_PACKET) &&
133 fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0))
134 return 0;
135
136 if (n->nlmsg_type == RTM_DELLINK)
137 fprintf(fp, "Deleted ");
138
139 fprintf(fp, "%d: %s", ifi->ifi_index,
140 tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>");
141
142 if (tb[IFLA_LINK]) {
143 SPRINT_BUF(b1);
144 int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
145 if (iflink == 0)
146 fprintf(fp, "@NONE: ");
147 else {
148 fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1));
149 m_flag = ll_index_to_flags(iflink);
150 m_flag = !(m_flag & IFF_UP);
151 }
152 } else {
153 fprintf(fp, ": ");
154 }
155 print_link_flags(fp, ifi->ifi_flags, m_flag);
156
157 if (tb[IFLA_MTU])
158 fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
159 if (tb[IFLA_QDISC])
160 fprintf(fp, "qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC]));
161#ifdef IFLA_MASTER
162 if (tb[IFLA_MASTER]) {
163 SPRINT_BUF(b1);
164 fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
165 }
166#endif
167 if (filter.showqueue)
168 print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME]));
169
170 if (!filter.family || filter.family == AF_PACKET) {
171 SPRINT_BUF(b1);
172 fprintf(fp, "%s", _SL_);
173 fprintf(fp, " link/%s ", ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
174
175 if (tb[IFLA_ADDRESS]) {
176 fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
177 RTA_PAYLOAD(tb[IFLA_ADDRESS]),
178 ifi->ifi_type,
179 b1, sizeof(b1)));
180 }
181 if (tb[IFLA_BROADCAST]) {
182 if (ifi->ifi_flags&IFF_POINTOPOINT)
183 fprintf(fp, " peer ");
184 else
185 fprintf(fp, " brd ");
186 fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
187 RTA_PAYLOAD(tb[IFLA_BROADCAST]),
188 ifi->ifi_type,
189 b1, sizeof(b1)));
190 }
191 }
192 fprintf(fp, "\n");
193 fflush(fp);
194 return 0;
195}
196
197static int flush_update(void)
198{
199 if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
200 perror("Failed to send flush request\n");
201 return -1;
202 }
203 filter.flushp = 0;
204 return 0;
205}
206
207static int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
208{
209 FILE *fp = (FILE*)arg;
210 struct ifaddrmsg *ifa = NLMSG_DATA(n);
211 int len = n->nlmsg_len;
212 struct rtattr * rta_tb[IFA_MAX+1];
213 char abuf[256];
214 SPRINT_BUF(b1);
215
216 if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR)
217 return 0;
218 len -= NLMSG_LENGTH(sizeof(*ifa));
219 if (len < 0) {
220 bb_error_msg("wrong nlmsg len %d", len);
221 return -1;
222 }
223
224 if (filter.flushb && n->nlmsg_type != RTM_NEWADDR)
225 return 0;
226
227 memset(rta_tb, 0, sizeof(rta_tb));
228 parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
229
230 if (!rta_tb[IFA_LOCAL])
231 rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
232 if (!rta_tb[IFA_ADDRESS])
233 rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
234
235 if (filter.ifindex && filter.ifindex != ifa->ifa_index)
236 return 0;
237 if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
238 return 0;
239 if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
240 return 0;
241 if (filter.label) {
242 const char *label;
243 if (rta_tb[IFA_LABEL])
244 label = RTA_DATA(rta_tb[IFA_LABEL]);
245 else
246 label = ll_idx_n2a(ifa->ifa_index, b1);
247 if (fnmatch(filter.label, label, 0) != 0)
248 return 0;
249 }
250 if (filter.pfx.family) {
251 if (rta_tb[IFA_LOCAL]) {
252 inet_prefix dst;
253 memset(&dst, 0, sizeof(dst));
254 dst.family = ifa->ifa_family;
255 memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL]));
256 if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
257 return 0;
258 }
259 }
260
261 if (filter.flushb) {
262 struct nlmsghdr *fn;
263 if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
264 if (flush_update())
265 return -1;
266 }
267 fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
268 memcpy(fn, n, n->nlmsg_len);
269 fn->nlmsg_type = RTM_DELADDR;
270 fn->nlmsg_flags = NLM_F_REQUEST;
271 fn->nlmsg_seq = ++filter.rth->seq;
272 filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
273 filter.flushed++;
274 return 0;
275 }
276
277 if (n->nlmsg_type == RTM_DELADDR)
278 fprintf(fp, "Deleted ");
279
280 if (filter.oneline)
281 fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index));
282 if (ifa->ifa_family == AF_INET)
283 fprintf(fp, " inet ");
284 else if (ifa->ifa_family == AF_INET6)
285 fprintf(fp, " inet6 ");
286 else
287 fprintf(fp, " family %d ", ifa->ifa_family);
288
289 if (rta_tb[IFA_LOCAL]) {
290 fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family,
291 RTA_PAYLOAD(rta_tb[IFA_LOCAL]),
292 RTA_DATA(rta_tb[IFA_LOCAL]),
293 abuf, sizeof(abuf)));
294
295 if (rta_tb[IFA_ADDRESS] == NULL ||
296 memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) {
297 fprintf(fp, "/%d ", ifa->ifa_prefixlen);
298 } else {
299 fprintf(fp, " peer %s/%d ",
300 rt_addr_n2a(ifa->ifa_family,
301 RTA_PAYLOAD(rta_tb[IFA_ADDRESS]),
302 RTA_DATA(rta_tb[IFA_ADDRESS]),
303 abuf, sizeof(abuf)),
304 ifa->ifa_prefixlen);
305 }
306 }
307
308 if (rta_tb[IFA_BROADCAST]) {
309 fprintf(fp, "brd %s ",
310 rt_addr_n2a(ifa->ifa_family,
311 RTA_PAYLOAD(rta_tb[IFA_BROADCAST]),
312 RTA_DATA(rta_tb[IFA_BROADCAST]),
313 abuf, sizeof(abuf)));
314 }
315 if (rta_tb[IFA_ANYCAST]) {
316 fprintf(fp, "any %s ",
317 rt_addr_n2a(ifa->ifa_family,
318 RTA_PAYLOAD(rta_tb[IFA_ANYCAST]),
319 RTA_DATA(rta_tb[IFA_ANYCAST]),
320 abuf, sizeof(abuf)));
321 }
322 fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
323 if (ifa->ifa_flags&IFA_F_SECONDARY) {
324 ifa->ifa_flags &= ~IFA_F_SECONDARY;
325 fprintf(fp, "secondary ");
326 }
327 if (ifa->ifa_flags&IFA_F_TENTATIVE) {
328 ifa->ifa_flags &= ~IFA_F_TENTATIVE;
329 fprintf(fp, "tentative ");
330 }
331 if (ifa->ifa_flags&IFA_F_DEPRECATED) {
332 ifa->ifa_flags &= ~IFA_F_DEPRECATED;
333 fprintf(fp, "deprecated ");
334 }
335 if (!(ifa->ifa_flags&IFA_F_PERMANENT)) {
336 fprintf(fp, "dynamic ");
337 } else
338 ifa->ifa_flags &= ~IFA_F_PERMANENT;
339 if (ifa->ifa_flags)
340 fprintf(fp, "flags %02x ", ifa->ifa_flags);
341 if (rta_tb[IFA_LABEL])
342 fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL]));
343 if (rta_tb[IFA_CACHEINFO]) {
344 struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
345 char buf[128];
346 fprintf(fp, "%s", _SL_);
347 if (ci->ifa_valid == 0xFFFFFFFFU)
348 sprintf(buf, "valid_lft forever");
349 else
350 sprintf(buf, "valid_lft %dsec", ci->ifa_valid);
351 if (ci->ifa_prefered == 0xFFFFFFFFU)
352 sprintf(buf+strlen(buf), " preferred_lft forever");
353 else
354 sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered);
355 fprintf(fp, " %s", buf);
356 }
357 fprintf(fp, "\n");
358 fflush(fp);
359 return 0;
360}
361
362
363struct nlmsg_list
364{
365 struct nlmsg_list *next;
366 struct nlmsghdr h;
367};
368
369static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp)
370{
371 for ( ;ainfo ; ainfo = ainfo->next) {
372 struct nlmsghdr *n = &ainfo->h;
373 struct ifaddrmsg *ifa = NLMSG_DATA(n);
374
375 if (n->nlmsg_type != RTM_NEWADDR)
376 continue;
377
378 if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa)))
379 return -1;
380
381 if (ifa->ifa_index != ifindex ||
382 (filter.family && filter.family != ifa->ifa_family))
383 continue;
384
385 print_addrinfo(NULL, n, fp);
386 }
387 return 0;
388}
389
390
391static int store_nlmsg(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
392{
393 struct nlmsg_list **linfo = (struct nlmsg_list**)arg;
394 struct nlmsg_list *h;
395 struct nlmsg_list **lp;
396
397 h = malloc(n->nlmsg_len+sizeof(void*));
398 if (h == NULL)
399 return -1;
400
401 memcpy(&h->h, n, n->nlmsg_len);
402 h->next = NULL;
403
404 for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */;
405 *lp = h;
406
407 ll_remember_index(who, n, NULL);
408 return 0;
409}
410
411static void ipaddr_reset_filter(int _oneline)
412{
413 memset(&filter, 0, sizeof(filter));
414 filter.oneline = _oneline;
415}
416
417extern int ipaddr_list_or_flush(int argc, char **argv, int flush)
418{
419 const char *option[] = { "to", "scope", "up", "label", "dev", 0 };
420 struct nlmsg_list *linfo = NULL;
421 struct nlmsg_list *ainfo = NULL;
422 struct nlmsg_list *l;
423 struct rtnl_handle rth;
424 char *filter_dev = NULL;
425 int no_link = 0;
426
427 ipaddr_reset_filter(oneline);
428 filter.showqueue = 1;
429
430 if (filter.family == AF_UNSPEC)
431 filter.family = preferred_family;
432
433 if (flush) {
434 if (argc <= 0) {
435 fprintf(stderr, "Flush requires arguments.\n");
436 return -1;
437 }
438 if (filter.family == AF_PACKET) {
439 fprintf(stderr, "Cannot flush link addresses.\n");
440 return -1;
441 }
442 }
443
444 while (argc > 0) {
445 const unsigned short option_num = compare_string_array(option, *argv);
446 switch (option_num) {
447 case 0: /* to */
448 NEXT_ARG();
449 get_prefix(&filter.pfx, *argv, filter.family);
450 if (filter.family == AF_UNSPEC) {
451 filter.family = filter.pfx.family;
452 }
453 break;
454 case 1: /* scope */
455 {
456 int scope = 0;
457 NEXT_ARG();
458 filter.scopemask = -1;
459 if (rtnl_rtscope_a2n(&scope, *argv)) {
460 if (strcmp(*argv, "all") != 0) {
461 invarg("invalid \"scope\"\n", *argv);
462 }
463 scope = RT_SCOPE_NOWHERE;
464 filter.scopemask = 0;
465 }
466 filter.scope = scope;
467 break;
468 }
469 case 2: /* up */
470 filter.up = 1;
471 break;
472 case 3: /* label */
473 NEXT_ARG();
474 filter.label = *argv;
475 break;
476 case 4: /* dev */
477 NEXT_ARG();
478 default:
479 if (filter_dev) {
480 duparg2("dev", *argv);
481 }
482 filter_dev = *argv;
483 }
484 argv++;
485 argc--;
486 }
487
488 if (rtnl_open(&rth, 0) < 0)
489 exit(1);
490
491 if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) {
492 bb_perror_msg_and_die("Cannot send dump request");
493 }
494
495 if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) {
496 bb_error_msg_and_die("Dump terminated");
497 }
498
499 if (filter_dev) {
500 filter.ifindex = ll_name_to_index(filter_dev);
501 if (filter.ifindex <= 0) {
502 bb_error_msg("Device \"%s\" does not exist.", filter_dev);
503 return -1;
504 }
505 }
506
507 if (flush) {
508 int round = 0;
509 char flushb[4096-512];
510
511 filter.flushb = flushb;
512 filter.flushp = 0;
513 filter.flushe = sizeof(flushb);
514 filter.rth = &rth;
515
516 for (;;) {
517 if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
518 perror("Cannot send dump request");
519 exit(1);
520 }
521 filter.flushed = 0;
522 if (rtnl_dump_filter(&rth, print_addrinfo, stdout, NULL, NULL) < 0) {
523 fprintf(stderr, "Flush terminated\n");
524 exit(1);
525 }
526 if (filter.flushed == 0) {
527#if 0
528 if (round == 0)
529 fprintf(stderr, "Nothing to flush.\n");
530#endif
531 fflush(stdout);
532 return 0;
533 }
534 round++;
535 if (flush_update() < 0)
536 exit(1);
537 }
538 }
539
540 if (filter.family != AF_PACKET) {
541 if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
542 bb_perror_msg_and_die("Cannot send dump request");
543 }
544
545 if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) {
546 bb_error_msg_and_die("Dump terminated");
547 }
548 }
549
550
551 if (filter.family && filter.family != AF_PACKET) {
552 struct nlmsg_list **lp;
553 lp=&linfo;
554
555 if (filter.oneline)
556 no_link = 1;
557
558 while ((l=*lp)!=NULL) {
559 int ok = 0;
560 struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
561 struct nlmsg_list *a;
562
563 for (a=ainfo; a; a=a->next) {
564 struct nlmsghdr *n = &a->h;
565 struct ifaddrmsg *ifa = NLMSG_DATA(n);
566
567 if (ifa->ifa_index != ifi->ifi_index ||
568 (filter.family && filter.family != ifa->ifa_family))
569 continue;
570 if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
571 continue;
572 if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
573 continue;
574 if (filter.pfx.family || filter.label) {
575 struct rtattr *tb[IFA_MAX+1];
576 memset(tb, 0, sizeof(tb));
577 parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
578 if (!tb[IFA_LOCAL])
579 tb[IFA_LOCAL] = tb[IFA_ADDRESS];
580
581 if (filter.pfx.family && tb[IFA_LOCAL]) {
582 inet_prefix dst;
583 memset(&dst, 0, sizeof(dst));
584 dst.family = ifa->ifa_family;
585 memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
586 if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
587 continue;
588 }
589 if (filter.label) {
590 SPRINT_BUF(b1);
591 const char *label;
592 if (tb[IFA_LABEL])
593 label = RTA_DATA(tb[IFA_LABEL]);
594 else
595 label = ll_idx_n2a(ifa->ifa_index, b1);
596 if (fnmatch(filter.label, label, 0) != 0)
597 continue;
598 }
599 }
600
601 ok = 1;
602 break;
603 }
604 if (!ok)
605 *lp = l->next;
606 else
607 lp = &l->next;
608 }
609 }
610
611 for (l=linfo; l; l = l->next) {
612 if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) {
613 struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
614 if (filter.family != AF_PACKET)
615 print_selected_addrinfo(ifi->ifi_index, ainfo, stdout);
616 }
617 fflush(stdout);
618 }
619
620 exit(0);
621}
622
623static int default_scope(inet_prefix *lcl)
624{
625 if (lcl->family == AF_INET) {
626 if (lcl->bytelen >= 1 && *(__u8*)&lcl->data == 127)
627 return RT_SCOPE_HOST;
628 }
629 return 0;
630}
631
632static int ipaddr_modify(int cmd, int argc, char **argv)
633{
634 const char *option[] = { "peer", "remote", "broadcast", "brd",
635 "anycast", "scope", "dev", "label", "local", 0 };
636 struct rtnl_handle rth;
637 struct {
638 struct nlmsghdr n;
639 struct ifaddrmsg ifa;
640 char buf[256];
641 } req;
642 char *d = NULL;
643 char *l = NULL;
644 inet_prefix lcl;
645 inet_prefix peer;
646 int local_len = 0;
647 int peer_len = 0;
648 int brd_len = 0;
649 int any_len = 0;
650 int scoped = 0;
651
652 memset(&req, 0, sizeof(req));
653
654 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
655 req.n.nlmsg_flags = NLM_F_REQUEST;
656 req.n.nlmsg_type = cmd;
657 req.ifa.ifa_family = preferred_family;
658
659 while (argc > 0) {
660 const unsigned short option_num = compare_string_array(option, *argv);
661 switch (option_num) {
662 case 0: /* peer */
663 case 1: /* remote */
664 NEXT_ARG();
665
666 if (peer_len) {
667 duparg("peer", *argv);
668 }
669 get_prefix(&peer, *argv, req.ifa.ifa_family);
670 peer_len = peer.bytelen;
671 if (req.ifa.ifa_family == AF_UNSPEC) {
672 req.ifa.ifa_family = peer.family;
673 }
674 addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen);
675 req.ifa.ifa_prefixlen = peer.bitlen;
676 break;
677 case 2: /* broadcast */
678 case 3: /* brd */
679 {
680 inet_prefix addr;
681 NEXT_ARG();
682 if (brd_len) {
683 duparg("broadcast", *argv);
684 }
685 if (strcmp(*argv, "+") == 0) {
686 brd_len = -1;
687 }
688 else if (strcmp(*argv, "-") == 0) {
689 brd_len = -2;
690 } else {
691 get_addr(&addr, *argv, req.ifa.ifa_family);
692 if (req.ifa.ifa_family == AF_UNSPEC)
693 req.ifa.ifa_family = addr.family;
694 addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen);
695 brd_len = addr.bytelen;
696 }
697 break;
698 }
699 case 4: /* anycast */
700 {
701 inet_prefix addr;
702 NEXT_ARG();
703 if (any_len) {
704 duparg("anycast", *argv);
705 }
706 get_addr(&addr, *argv, req.ifa.ifa_family);
707 if (req.ifa.ifa_family == AF_UNSPEC) {
708 req.ifa.ifa_family = addr.family;
709 }
710 addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
711 any_len = addr.bytelen;
712 break;
713 }
714 case 5: /* scope */
715 {
716 int scope = 0;
717 NEXT_ARG();
718 if (rtnl_rtscope_a2n(&scope, *argv)) {
719 invarg(*argv, "invalid scope value.");
720 }
721 req.ifa.ifa_scope = scope;
722 scoped = 1;
723 break;
724 }
725 case 6: /* dev */
726 NEXT_ARG();
727 d = *argv;
728 break;
729 case 7: /* label */
730 NEXT_ARG();
731 l = *argv;
732 addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1);
733 break;
734 case 8: /* local */
735 NEXT_ARG();
736 default:
737 if (local_len) {
738 duparg2("local", *argv);
739 }
740 get_prefix(&lcl, *argv, req.ifa.ifa_family);
741 if (req.ifa.ifa_family == AF_UNSPEC) {
742 req.ifa.ifa_family = lcl.family;
743 }
744 addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
745 local_len = lcl.bytelen;
746 }
747 argc--;
748 argv++;
749 }
750
751 if (d == NULL) {
752 bb_error_msg("Not enough information: \"dev\" argument is required.");
753 return -1;
754 }
755 if (l && matches(d, l) != 0) {
756 bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s).", d, l);
757 }
758
759 if (peer_len == 0 && local_len && cmd != RTM_DELADDR) {
760 peer = lcl;
761 addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
762 }
763 if (req.ifa.ifa_prefixlen == 0)
764 req.ifa.ifa_prefixlen = lcl.bitlen;
765
766 if (brd_len < 0 && cmd != RTM_DELADDR) {
767 inet_prefix brd;
768 int i;
769 if (req.ifa.ifa_family != AF_INET) {
770 bb_error_msg("Broadcast can be set only for IPv4 addresses");
771 return -1;
772 }
773 brd = peer;
774 if (brd.bitlen <= 30) {
775 for (i=31; i>=brd.bitlen; i--) {
776 if (brd_len == -1)
777 brd.data[0] |= htonl(1<<(31-i));
778 else
779 brd.data[0] &= ~htonl(1<<(31-i));
780 }
781 addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen);
782 brd_len = brd.bytelen;
783 }
784 }
785 if (!scoped && cmd != RTM_DELADDR)
786 req.ifa.ifa_scope = default_scope(&lcl);
787
788 if (rtnl_open(&rth, 0) < 0)
789 exit(1);
790
791 ll_init_map(&rth);
792
793 if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) {
794 bb_error_msg("Cannot find device \"%s\"", d);
795 return -1;
796 }
797
798 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
799 exit(2);
800
801 exit(0);
802}
803
804extern int do_ipaddr(int argc, char **argv)
805{
806 const char *commands[] = { "add", "delete", "list", "show", "lst", "flush", 0 };
807 unsigned short command_num = 2;
808
809 if (*argv) {
810 command_num = compare_string_array(commands, *argv);
811 }
812 switch (command_num) {
813 case 0: /* add */
814 return ipaddr_modify(RTM_NEWADDR, argc-1, argv+1);
815 case 1: /* delete */
816 return ipaddr_modify(RTM_DELADDR, argc-1, argv+1);
817 case 2: /* list */
818 case 3: /* show */
819 case 4: /* lst */
820 return ipaddr_list_or_flush(argc-1, argv+1, 0);
821 case 5: /* flush */
822 return ipaddr_list_or_flush(argc-1, argv+1, 1);
823 }
824 bb_error_msg_and_die("Unknown command %s", *argv);
825}
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 @@
1/*
2 * iplink.c "ip link".
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 */
12
13#include <sys/ioctl.h>
14#include <sys/socket.h>
15#include <linux/version.h>
16
17#include <errno.h>
18#include <stdlib.h>
19#include <string.h>
20#include <unistd.h>
21
22#include <net/if.h>
23#include <net/if_packet.h>
24#include <netpacket/packet.h>
25
26#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
27#include <net/ethernet.h>
28#else
29#include <linux/if_ether.h>
30#endif
31
32#include "rt_names.h"
33#include "utils.h"
34#include "ip_common.h"
35
36#include "libbb.h"
37
38
39/* take from linux/sockios.h */
40#define SIOCSIFNAME 0x8923 /* set interface name */
41
42static int do_link;
43
44static int on_off(char *msg)
45{
46 bb_error_msg("Error: argument of \"%s\" must be \"on\" or \"off\"", msg);
47 return -1;
48}
49
50static int get_ctl_fd(void)
51{
52 int s_errno;
53 int fd;
54
55 fd = socket(PF_INET, SOCK_DGRAM, 0);
56 if (fd >= 0)
57 return fd;
58 s_errno = errno;
59 fd = socket(PF_PACKET, SOCK_DGRAM, 0);
60 if (fd >= 0)
61 return fd;
62 fd = socket(PF_INET6, SOCK_DGRAM, 0);
63 if (fd >= 0)
64 return fd;
65 errno = s_errno;
66 perror("Cannot create control socket");
67 return -1;
68}
69
70static int do_chflags(char *dev, __u32 flags, __u32 mask)
71{
72 struct ifreq ifr;
73 int fd;
74 int err;
75
76 strcpy(ifr.ifr_name, dev);
77 fd = get_ctl_fd();
78 if (fd < 0)
79 return -1;
80 err = ioctl(fd, SIOCGIFFLAGS, &ifr);
81 if (err) {
82 perror("SIOCGIFFLAGS");
83 close(fd);
84 return -1;
85 }
86 if ((ifr.ifr_flags^flags)&mask) {
87 ifr.ifr_flags &= ~mask;
88 ifr.ifr_flags |= mask&flags;
89 err = ioctl(fd, SIOCSIFFLAGS, &ifr);
90 if (err)
91 perror("SIOCSIFFLAGS");
92 }
93 close(fd);
94 return err;
95}
96
97static int do_changename(char *dev, char *newdev)
98{
99#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
100 struct ifreq ifr;
101 int fd;
102 int err;
103
104 strcpy(ifr.ifr_name, dev);
105 strcpy(ifr.ifr_newname, newdev);
106 fd = get_ctl_fd();
107 if (fd < 0)
108 return -1;
109 err = ioctl(fd, SIOCSIFNAME, &ifr);
110 if (err) {
111 perror("SIOCSIFNAME");
112 close(fd);
113 return -1;
114 }
115 close(fd);
116 return err;
117#endif
118 return 0;
119}
120
121static int set_qlen(char *dev, int qlen)
122{
123 struct ifreq ifr;
124 int s;
125
126 s = get_ctl_fd();
127 if (s < 0)
128 return -1;
129
130 memset(&ifr, 0, sizeof(ifr));
131 strcpy(ifr.ifr_name, dev);
132 ifr.ifr_qlen = qlen;
133 if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
134 perror("SIOCSIFXQLEN");
135 close(s);
136 return -1;
137 }
138 close(s);
139
140 return 0;
141}
142
143static int set_mtu(char *dev, int mtu)
144{
145 struct ifreq ifr;
146 int s;
147
148 s = get_ctl_fd();
149 if (s < 0)
150 return -1;
151
152 memset(&ifr, 0, sizeof(ifr));
153 strcpy(ifr.ifr_name, dev);
154 ifr.ifr_mtu = mtu;
155 if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
156 perror("SIOCSIFMTU");
157 close(s);
158 return -1;
159 }
160 close(s);
161
162 return 0;
163}
164
165static int get_address(char *dev, int *htype)
166{
167 struct ifreq ifr;
168 struct sockaddr_ll me;
169 int alen;
170 int s;
171
172 s = socket(PF_PACKET, SOCK_DGRAM, 0);
173 if (s < 0) {
174 perror("socket(PF_PACKET)");
175 return -1;
176 }
177
178 memset(&ifr, 0, sizeof(ifr));
179 strcpy(ifr.ifr_name, dev);
180 if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
181 perror("SIOCGIFINDEX");
182 close(s);
183 return -1;
184 }
185
186 memset(&me, 0, sizeof(me));
187 me.sll_family = AF_PACKET;
188 me.sll_ifindex = ifr.ifr_ifindex;
189 me.sll_protocol = htons(ETH_P_LOOP);
190 if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) {
191 perror("bind");
192 close(s);
193 return -1;
194 }
195
196 alen = sizeof(me);
197 if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
198 perror("getsockname");
199 close(s);
200 return -1;
201 }
202 close(s);
203 *htype = me.sll_hatype;
204 return me.sll_halen;
205}
206
207static int parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
208{
209 int alen;
210
211 memset(ifr, 0, sizeof(*ifr));
212 strcpy(ifr->ifr_name, dev);
213 ifr->ifr_hwaddr.sa_family = hatype;
214 alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla);
215 if (alen < 0)
216 return -1;
217 if (alen != halen) {
218 bb_error_msg("Wrong address (%s) length: expected %d bytes", lla, halen);
219 return -1;
220 }
221 return 0;
222}
223
224static int set_address(struct ifreq *ifr, int brd)
225{
226 int s;
227
228 s = get_ctl_fd();
229 if (s < 0)
230 return -1;
231 if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) {
232 perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR");
233 close(s);
234 return -1;
235 }
236 close(s);
237 return 0;
238}
239
240
241static int do_set(int argc, char **argv)
242{
243 char *dev = NULL;
244 __u32 mask = 0;
245 __u32 flags = 0;
246 int qlen = -1;
247 int mtu = -1;
248 char *newaddr = NULL;
249 char *newbrd = NULL;
250 struct ifreq ifr0, ifr1;
251 char *newname = NULL;
252 int htype, halen;
253
254 while (argc > 0) {
255 if (strcmp(*argv, "up") == 0) {
256 mask |= IFF_UP;
257 flags |= IFF_UP;
258 } else if (strcmp(*argv, "down") == 0) {
259 mask |= IFF_UP;
260 flags &= ~IFF_UP;
261 } else if (strcmp(*argv, "name") == 0) {
262 NEXT_ARG();
263 newname = *argv;
264 } else if (strcmp(*argv, "mtu") == 0) {
265 NEXT_ARG();
266 if (mtu != -1)
267 duparg("mtu", *argv);
268 if (get_integer(&mtu, *argv, 0))
269 invarg("Invalid \"mtu\" value\n", *argv);
270 } else if (strcmp(*argv, "multicast") == 0) {
271 NEXT_ARG();
272 mask |= IFF_MULTICAST;
273 if (strcmp(*argv, "on") == 0) {
274 flags |= IFF_MULTICAST;
275 } else if (strcmp(*argv, "off") == 0) {
276 flags &= ~IFF_MULTICAST;
277 } else
278 return on_off("multicast");
279 } else if (strcmp(*argv, "arp") == 0) {
280 NEXT_ARG();
281 mask |= IFF_NOARP;
282 if (strcmp(*argv, "on") == 0) {
283 flags &= ~IFF_NOARP;
284 } else if (strcmp(*argv, "off") == 0) {
285 flags |= IFF_NOARP;
286 } else
287 return on_off("noarp");
288 } else {
289 if (strcmp(*argv, "dev") == 0) {
290 NEXT_ARG();
291 }
292 if (dev)
293 duparg2("dev", *argv);
294 dev = *argv;
295 }
296 argc--; argv++;
297 }
298
299 if (!dev) {
300 bb_error_msg("Not enough of information: \"dev\" argument is required.");
301 exit(-1);
302 }
303
304 if (newaddr || newbrd) {
305 halen = get_address(dev, &htype);
306 if (halen < 0)
307 return -1;
308 if (newaddr) {
309 if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0)
310 return -1;
311 }
312 if (newbrd) {
313 if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0)
314 return -1;
315 }
316 }
317
318 if (newname && strcmp(dev, newname)) {
319 if (do_changename(dev, newname) < 0)
320 return -1;
321 dev = newname;
322 }
323 if (qlen != -1) {
324 if (set_qlen(dev, qlen) < 0)
325 return -1;
326 }
327 if (mtu != -1) {
328 if (set_mtu(dev, mtu) < 0)
329 return -1;
330 }
331 if (newaddr || newbrd) {
332 if (newbrd) {
333 if (set_address(&ifr1, 1) < 0)
334 return -1;
335 }
336 if (newaddr) {
337 if (set_address(&ifr0, 0) < 0)
338 return -1;
339 }
340 }
341 if (mask)
342 return do_chflags(dev, flags, mask);
343 return 0;
344}
345
346static int ipaddr_list_link(int argc, char **argv)
347{
348 preferred_family = AF_PACKET;
349 do_link = 1;
350 return ipaddr_list_or_flush(argc, argv, 0);
351}
352
353int do_iplink(int argc, char **argv)
354{
355 if (argc > 0) {
356 if (matches(*argv, "set") == 0)
357 return do_set(argc-1, argv+1);
358 if (matches(*argv, "show") == 0 ||
359 matches(*argv, "lst") == 0 ||
360 matches(*argv, "list") == 0)
361 return ipaddr_list_link(argc-1, argv+1);
362 } else
363 return ipaddr_list_link(0, NULL);
364
365 bb_error_msg("Command \"%s\" is unknown.", *argv);
366 exit(-1);
367}
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 @@
1/*
2 * iproute.c "ip route".
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 *
12 * Changes:
13 *
14 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
15 * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
16 */
17
18#include <sys/socket.h>
19
20#include <stdlib.h>
21#include <string.h>
22#include <fcntl.h>
23#include <unistd.h>
24
25#include "rt_names.h"
26#include "utils.h"
27
28#include "libbb.h"
29
30#ifndef RTAX_RTTVAR
31#define RTAX_RTTVAR RTAX_HOPS
32#endif
33
34
35static struct
36{
37 int tb;
38 int flushed;
39 char *flushb;
40 int flushp;
41 int flushe;
42 struct rtnl_handle *rth;
43 int protocol, protocolmask;
44 int scope, scopemask;
45 int type, typemask;
46 int tos, tosmask;
47 int iif, iifmask;
48 int oif, oifmask;
49 int realm, realmmask;
50 inet_prefix rprefsrc;
51 inet_prefix rvia;
52 inet_prefix rdst;
53 inet_prefix mdst;
54 inet_prefix rsrc;
55 inet_prefix msrc;
56} filter;
57
58static int flush_update(void)
59{
60 if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
61 perror("Failed to send flush request\n");
62 return -1;
63 }
64 filter.flushp = 0;
65 return 0;
66}
67
68static int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
69{
70 FILE *fp = (FILE*)arg;
71 struct rtmsg *r = NLMSG_DATA(n);
72 int len = n->nlmsg_len;
73 struct rtattr * tb[RTA_MAX+1];
74 char abuf[256];
75 inet_prefix dst;
76 inet_prefix src;
77 int host_len = -1;
78 SPRINT_BUF(b1);
79
80
81 if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
82 fprintf(stderr, "Not a route: %08x %08x %08x\n",
83 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
84 return 0;
85 }
86 if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
87 return 0;
88 len -= NLMSG_LENGTH(sizeof(*r));
89 if (len < 0) {
90 bb_error_msg("wrong nlmsg len %d", len);
91 return -1;
92 }
93
94 if (r->rtm_family == AF_INET6)
95 host_len = 128;
96 else if (r->rtm_family == AF_INET)
97 host_len = 32;
98
99 if (r->rtm_family == AF_INET6) {
100 if (filter.tb) {
101 if (filter.tb < 0) {
102 if (!(r->rtm_flags&RTM_F_CLONED)) {
103 return 0;
104 }
105 } else {
106 if (r->rtm_flags&RTM_F_CLONED) {
107 return 0;
108 }
109 if (filter.tb == RT_TABLE_LOCAL) {
110 if (r->rtm_type != RTN_LOCAL) {
111 return 0;
112 }
113 } else if (filter.tb == RT_TABLE_MAIN) {
114 if (r->rtm_type == RTN_LOCAL) {
115 return 0;
116 }
117 } else {
118 return 0;
119 }
120 }
121 }
122 } else {
123 if (filter.tb > 0 && filter.tb != r->rtm_table) {
124 return 0;
125 }
126 }
127 if (filter.rdst.family &&
128 (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) {
129 return 0;
130 }
131 if (filter.mdst.family &&
132 (r->rtm_family != filter.mdst.family ||
133 (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) {
134 return 0;
135 }
136 if (filter.rsrc.family &&
137 (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) {
138 return 0;
139 }
140 if (filter.msrc.family &&
141 (r->rtm_family != filter.msrc.family ||
142 (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) {
143 return 0;
144 }
145
146 memset(tb, 0, sizeof(tb));
147 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
148
149 if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bitlen))
150 return 0;
151 if (filter.mdst.family && filter.mdst.bitlen >= 0 &&
152 inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len))
153 return 0;
154
155 if (filter.rsrc.family && inet_addr_match(&src, &filter.rsrc, filter.rsrc.bitlen))
156 return 0;
157 if (filter.msrc.family && filter.msrc.bitlen >= 0 &&
158 inet_addr_match(&src, &filter.msrc, r->rtm_src_len))
159 return 0;
160
161 if (filter.flushb &&
162 r->rtm_family == AF_INET6 &&
163 r->rtm_dst_len == 0 &&
164 r->rtm_type == RTN_UNREACHABLE &&
165 tb[RTA_PRIORITY] &&
166 *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1)
167 return 0;
168
169 if (filter.flushb) {
170 struct nlmsghdr *fn;
171 if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
172 if (flush_update())
173 return -1;
174 }
175 fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
176 memcpy(fn, n, n->nlmsg_len);
177 fn->nlmsg_type = RTM_DELROUTE;
178 fn->nlmsg_flags = NLM_F_REQUEST;
179 fn->nlmsg_seq = ++filter.rth->seq;
180 filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
181 filter.flushed++;
182 return 0;
183 }
184
185 if (n->nlmsg_type == RTM_DELROUTE) {
186 fprintf(fp, "Deleted ");
187 }
188 if (r->rtm_type != RTN_UNICAST && !filter.type) {
189 fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
190 }
191
192 if (tb[RTA_DST]) {
193 if (r->rtm_dst_len != host_len) {
194 fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family,
195 RTA_PAYLOAD(tb[RTA_DST]),
196 RTA_DATA(tb[RTA_DST]),
197 abuf, sizeof(abuf)),
198 r->rtm_dst_len
199 );
200 } else {
201 fprintf(fp, "%s ", format_host(r->rtm_family,
202 RTA_PAYLOAD(tb[RTA_DST]),
203 RTA_DATA(tb[RTA_DST]),
204 abuf, sizeof(abuf))
205 );
206 }
207 } else if (r->rtm_dst_len) {
208 fprintf(fp, "0/%d ", r->rtm_dst_len);
209 } else {
210 fprintf(fp, "default ");
211 }
212 if (tb[RTA_SRC]) {
213 if (r->rtm_src_len != host_len) {
214 fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family,
215 RTA_PAYLOAD(tb[RTA_SRC]),
216 RTA_DATA(tb[RTA_SRC]),
217 abuf, sizeof(abuf)),
218 r->rtm_src_len
219 );
220 } else {
221 fprintf(fp, "from %s ", format_host(r->rtm_family,
222 RTA_PAYLOAD(tb[RTA_SRC]),
223 RTA_DATA(tb[RTA_SRC]),
224 abuf, sizeof(abuf))
225 );
226 }
227 } else if (r->rtm_src_len) {
228 fprintf(fp, "from 0/%u ", r->rtm_src_len);
229 }
230 if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) {
231 fprintf(fp, "via %s ",
232 format_host(r->rtm_family,
233 RTA_PAYLOAD(tb[RTA_GATEWAY]),
234 RTA_DATA(tb[RTA_GATEWAY]),
235 abuf, sizeof(abuf)));
236 }
237 if (tb[RTA_OIF] && filter.oifmask != -1) {
238 fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
239 }
240
241 if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) {
242 /* Do not use format_host(). It is our local addr
243 and symbolic name will not be useful.
244 */
245 fprintf(fp, " src %s ",
246 rt_addr_n2a(r->rtm_family,
247 RTA_PAYLOAD(tb[RTA_PREFSRC]),
248 RTA_DATA(tb[RTA_PREFSRC]),
249 abuf, sizeof(abuf)));
250 }
251 if (tb[RTA_PRIORITY]) {
252 fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY]));
253 }
254 if (r->rtm_family == AF_INET6) {
255 struct rta_cacheinfo *ci = NULL;
256 if (tb[RTA_CACHEINFO]) {
257 ci = RTA_DATA(tb[RTA_CACHEINFO]);
258 }
259 if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
260 static int hz;
261 if (!hz) {
262 hz = get_hz();
263 }
264 if (r->rtm_flags & RTM_F_CLONED) {
265 fprintf(fp, "%s cache ", _SL_);
266 }
267 if (ci->rta_expires) {
268 fprintf(fp, " expires %dsec", ci->rta_expires/hz);
269 }
270 if (ci->rta_error != 0) {
271 fprintf(fp, " error %d", ci->rta_error);
272 }
273 } else if (ci) {
274 if (ci->rta_error != 0)
275 fprintf(fp, " error %d", ci->rta_error);
276 }
277 }
278 if (tb[RTA_IIF] && filter.iifmask != -1) {
279 fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
280 }
281 fprintf(fp, "\n");
282 fflush(fp);
283 return 0;
284}
285
286static int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
287{
288 struct rtnl_handle rth;
289 struct {
290 struct nlmsghdr n;
291 struct rtmsg r;
292 char buf[1024];
293 } req;
294 char mxbuf[256];
295 struct rtattr * mxrta = (void*)mxbuf;
296 unsigned mxlock = 0;
297 char *d = NULL;
298 int gw_ok = 0;
299 int dst_ok = 0;
300 int proto_ok = 0;
301 int type_ok = 0;
302
303 memset(&req, 0, sizeof(req));
304
305 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
306 req.n.nlmsg_flags = NLM_F_REQUEST|flags;
307 req.n.nlmsg_type = cmd;
308 req.r.rtm_family = preferred_family;
309 req.r.rtm_table = RT_TABLE_MAIN;
310 req.r.rtm_scope = RT_SCOPE_NOWHERE;
311
312 if (cmd != RTM_DELROUTE) {
313 req.r.rtm_protocol = RTPROT_BOOT;
314 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
315 req.r.rtm_type = RTN_UNICAST;
316 }
317
318 mxrta->rta_type = RTA_METRICS;
319 mxrta->rta_len = RTA_LENGTH(0);
320
321 while (argc > 0) {
322 if (strcmp(*argv, "src") == 0) {
323 inet_prefix addr;
324 NEXT_ARG();
325 get_addr(&addr, *argv, req.r.rtm_family);
326 if (req.r.rtm_family == AF_UNSPEC) {
327 req.r.rtm_family = addr.family;
328 }
329 addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
330 } else if (strcmp(*argv, "via") == 0) {
331 inet_prefix addr;
332 gw_ok = 1;
333 NEXT_ARG();
334 get_addr(&addr, *argv, req.r.rtm_family);
335 if (req.r.rtm_family == AF_UNSPEC) {
336 req.r.rtm_family = addr.family;
337 }
338 addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
339 } else if (strcmp(*argv, "mtu") == 0) {
340 unsigned mtu;
341 NEXT_ARG();
342 if (strcmp(*argv, "lock") == 0) {
343 mxlock |= (1<<RTAX_MTU);
344 NEXT_ARG();
345 }
346 if (get_unsigned(&mtu, *argv, 0)) {
347 invarg("\"mtu\" value is invalid\n", *argv);
348 }
349 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
350 } else if (matches(*argv, "protocol") == 0) {
351 int prot;
352 NEXT_ARG();
353 if (rtnl_rtprot_a2n(&prot, *argv))
354 invarg("\"protocol\" value is invalid\n", *argv);
355 req.r.rtm_protocol = prot;
356 proto_ok =1;
357 } else if (strcmp(*argv, "dev") == 0 ||
358 strcmp(*argv, "oif") == 0) {
359 NEXT_ARG();
360 d = *argv;
361 } else {
362 int type;
363 inet_prefix dst;
364
365 if (strcmp(*argv, "to") == 0) {
366 NEXT_ARG();
367 }
368 if ((**argv < '0' || **argv > '9') &&
369 rtnl_rtntype_a2n(&type, *argv) == 0) {
370 NEXT_ARG();
371 req.r.rtm_type = type;
372 type_ok = 1;
373 }
374
375 if (dst_ok) {
376 duparg2("to", *argv);
377 }
378 get_prefix(&dst, *argv, req.r.rtm_family);
379 if (req.r.rtm_family == AF_UNSPEC) {
380 req.r.rtm_family = dst.family;
381 }
382 req.r.rtm_dst_len = dst.bitlen;
383 dst_ok = 1;
384 if (dst.bytelen) {
385 addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
386 }
387 }
388 argc--; argv++;
389 }
390
391 if (rtnl_open(&rth, 0) < 0) {
392 exit(1);
393 }
394
395 if (d) {
396 int idx;
397
398 ll_init_map(&rth);
399
400 if (d) {
401 if ((idx = ll_name_to_index(d)) == 0) {
402 bb_error_msg("Cannot find device \"%s\"", d);
403 return -1;
404 }
405 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
406 }
407 }
408
409 if (mxrta->rta_len > RTA_LENGTH(0)) {
410 if (mxlock) {
411 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
412 }
413 addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
414 }
415
416 if (req.r.rtm_family == AF_UNSPEC) {
417 req.r.rtm_family = AF_INET;
418 }
419
420 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
421 exit(2);
422 }
423
424 return 0;
425}
426
427static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
428{
429 struct {
430 struct nlmsghdr nlh;
431 struct rtmsg rtm;
432 } req;
433 struct sockaddr_nl nladdr;
434
435 memset(&nladdr, 0, sizeof(nladdr));
436 memset(&req, 0, sizeof(req));
437 nladdr.nl_family = AF_NETLINK;
438
439 req.nlh.nlmsg_len = sizeof(req);
440 req.nlh.nlmsg_type = RTM_GETROUTE;
441 req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
442 req.nlh.nlmsg_pid = 0;
443 req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
444 req.rtm.rtm_family = family;
445 req.rtm.rtm_flags |= RTM_F_CLONED;
446
447 return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
448}
449
450static int iproute_flush_cache(void)
451{
452#define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush"
453
454 int len;
455 int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY);
456 char *buffer = "-1";
457
458 if (flush_fd < 0) {
459 fprintf (stderr, "Cannot open \"%s\"\n", ROUTE_FLUSH_PATH);
460 return -1;
461 }
462
463 len = strlen (buffer);
464
465 if ((write (flush_fd, (void *)buffer, len)) < len) {
466 fprintf (stderr, "Cannot flush routing cache\n");
467 return -1;
468 }
469 close(flush_fd);
470 return 0;
471}
472
473static void iproute_reset_filter(void)
474{
475 memset(&filter, 0, sizeof(filter));
476 filter.mdst.bitlen = -1;
477 filter.msrc.bitlen = -1;
478}
479
480static int iproute_list_or_flush(int argc, char **argv, int flush)
481{
482 int do_ipv6 = preferred_family;
483 struct rtnl_handle rth;
484 char *id = NULL;
485 char *od = NULL;
486
487 iproute_reset_filter();
488 filter.tb = RT_TABLE_MAIN;
489
490 if (flush && argc <= 0) {
491 fprintf(stderr, "\"ip route flush\" requires arguments.\n");
492 return -1;
493 }
494
495 while (argc > 0) {
496 if (matches(*argv, "protocol") == 0) {
497 int prot = 0;
498 NEXT_ARG();
499 filter.protocolmask = -1;
500 if (rtnl_rtprot_a2n(&prot, *argv)) {
501 if (strcmp(*argv, "all") != 0) {
502 invarg("invalid \"protocol\"\n", *argv);
503 }
504 prot = 0;
505 filter.protocolmask = 0;
506 }
507 filter.protocol = prot;
508 } else if (strcmp(*argv, "dev") == 0 ||
509 strcmp(*argv, "oif") == 0) {
510 NEXT_ARG();
511 od = *argv;
512 } else if (strcmp(*argv, "iif") == 0) {
513 NEXT_ARG();
514 id = *argv;
515 } else if (matches(*argv, "from") == 0) {
516 NEXT_ARG();
517 if (matches(*argv, "root") == 0) {
518 NEXT_ARG();
519 get_prefix(&filter.rsrc, *argv, do_ipv6);
520 } else if (matches(*argv, "match") == 0) {
521 NEXT_ARG();
522 get_prefix(&filter.msrc, *argv, do_ipv6);
523 } else {
524 if (matches(*argv, "exact") == 0) {
525 NEXT_ARG();
526 }
527 get_prefix(&filter.msrc, *argv, do_ipv6);
528 filter.rsrc = filter.msrc;
529 }
530 } else {
531 if (matches(*argv, "to") == 0) {
532 NEXT_ARG();
533 }
534 if (matches(*argv, "root") == 0) {
535 NEXT_ARG();
536 get_prefix(&filter.rdst, *argv, do_ipv6);
537 } else if (matches(*argv, "match") == 0) {
538 NEXT_ARG();
539 get_prefix(&filter.mdst, *argv, do_ipv6);
540 } else {
541 if (matches(*argv, "exact") == 0) {
542 NEXT_ARG();
543 }
544 get_prefix(&filter.mdst, *argv, do_ipv6);
545 filter.rdst = filter.mdst;
546 }
547 }
548 argc--; argv++;
549 }
550
551 if (do_ipv6 == AF_UNSPEC && filter.tb) {
552 do_ipv6 = AF_INET;
553 }
554
555 if (rtnl_open(&rth, 0) < 0) {
556 exit(1);
557 }
558
559 ll_init_map(&rth);
560
561 if (id || od) {
562 int idx;
563
564 if (id) {
565 if ((idx = ll_name_to_index(id)) == 0) {
566 bb_error_msg("Cannot find device \"%s\"", id);
567 return -1;
568 }
569 filter.iif = idx;
570 filter.iifmask = -1;
571 }
572 if (od) {
573 if ((idx = ll_name_to_index(od)) == 0) {
574 bb_error_msg("Cannot find device \"%s\"", od);
575 }
576 filter.oif = idx;
577 filter.oifmask = -1;
578 }
579 }
580
581 if (flush) {
582 int round = 0;
583 char flushb[4096-512];
584
585 if (filter.tb == -1) {
586 if (do_ipv6 != AF_INET6)
587 iproute_flush_cache();
588 if (do_ipv6 == AF_INET)
589 return 0;
590 }
591
592 filter.flushb = flushb;
593 filter.flushp = 0;
594 filter.flushe = sizeof(flushb);
595 filter.rth = &rth;
596
597 for (;;) {
598 if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
599 perror("Cannot send dump request");
600 return -1;
601 }
602 filter.flushed = 0;
603 if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
604 bb_error_msg("Flush terminated\n");
605 return -1;
606 }
607 if (filter.flushed == 0) {
608 if (round == 0) {
609 if (filter.tb != -1 || do_ipv6 == AF_INET6)
610 fprintf(stderr, "Nothing to flush.\n");
611 }
612 fflush(stdout);
613 return 0;
614 }
615 round++;
616 if (flush_update() < 0)
617 exit(1);
618 }
619 }
620
621 if (filter.tb != -1) {
622 if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
623 bb_perror_msg_and_die("Cannot send dump request");
624 }
625 } else {
626 if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
627 bb_perror_msg_and_die("Cannot send dump request");
628 }
629 }
630
631 if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
632 bb_error_msg_and_die("Dump terminated");
633 }
634
635 exit(0);
636}
637
638
639static int iproute_get(int argc, char **argv)
640{
641 struct rtnl_handle rth;
642 struct {
643 struct nlmsghdr n;
644 struct rtmsg r;
645 char buf[1024];
646 } req;
647 char *idev = NULL;
648 char *odev = NULL;
649 int connected = 0;
650 int from_ok = 0;
651 const char *options[] = { "from", "iif", "oif", "dev", "notify", "connected", "to", 0 };
652
653 memset(&req, 0, sizeof(req));
654
655 iproute_reset_filter();
656
657 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
658 req.n.nlmsg_flags = NLM_F_REQUEST;
659 req.n.nlmsg_type = RTM_GETROUTE;
660 req.r.rtm_family = preferred_family;
661 req.r.rtm_table = 0;
662 req.r.rtm_protocol = 0;
663 req.r.rtm_scope = 0;
664 req.r.rtm_type = 0;
665 req.r.rtm_src_len = 0;
666 req.r.rtm_dst_len = 0;
667 req.r.rtm_tos = 0;
668
669 while (argc > 0) {
670 switch (compare_string_array(options, *argv)) {
671 case 0: /* from */
672 {
673 inet_prefix addr;
674 NEXT_ARG();
675 from_ok = 1;
676 get_prefix(&addr, *argv, req.r.rtm_family);
677 if (req.r.rtm_family == AF_UNSPEC) {
678 req.r.rtm_family = addr.family;
679 }
680 if (addr.bytelen) {
681 addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
682 }
683 req.r.rtm_src_len = addr.bitlen;
684 break;
685 }
686 case 1: /* iif */
687 NEXT_ARG();
688 idev = *argv;
689 break;
690 case 2: /* oif */
691 case 3: /* dev */
692 NEXT_ARG();
693 odev = *argv;
694 break;
695 case 4: /* notify */
696 req.r.rtm_flags |= RTM_F_NOTIFY;
697 break;
698 case 5: /* connected */
699 connected = 1;
700 break;
701 case 6: /* to */
702 NEXT_ARG();
703 default:
704 {
705 inet_prefix addr;
706 get_prefix(&addr, *argv, req.r.rtm_family);
707 if (req.r.rtm_family == AF_UNSPEC) {
708 req.r.rtm_family = addr.family;
709 }
710 if (addr.bytelen) {
711 addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
712 }
713 req.r.rtm_dst_len = addr.bitlen;
714 }
715 argc--; argv++;
716 }
717 }
718
719 if (req.r.rtm_dst_len == 0) {
720 bb_error_msg_and_die("need at least destination address");
721 }
722
723 if (rtnl_open(&rth, 0) < 0)
724 exit(1);
725
726 ll_init_map(&rth);
727
728 if (idev || odev) {
729 int idx;
730
731 if (idev) {
732 if ((idx = ll_name_to_index(idev)) == 0) {
733 bb_error_msg("Cannot find device \"%s\"", idev);
734 return -1;
735 }
736 addattr32(&req.n, sizeof(req), RTA_IIF, idx);
737 }
738 if (odev) {
739 if ((idx = ll_name_to_index(odev)) == 0) {
740 bb_error_msg("Cannot find device \"%s\"", odev);
741 return -1;
742 }
743 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
744 }
745 }
746
747 if (req.r.rtm_family == AF_UNSPEC) {
748 req.r.rtm_family = AF_INET;
749 }
750
751 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
752 exit(2);
753 }
754
755 if (connected && !from_ok) {
756 struct rtmsg *r = NLMSG_DATA(&req.n);
757 int len = req.n.nlmsg_len;
758 struct rtattr * tb[RTA_MAX+1];
759
760 if (print_route(NULL, &req.n, (void*)stdout) < 0) {
761 bb_error_msg_and_die("An error :-)");
762 }
763
764 if (req.n.nlmsg_type != RTM_NEWROUTE) {
765 bb_error_msg("Not a route?");
766 return -1;
767 }
768 len -= NLMSG_LENGTH(sizeof(*r));
769 if (len < 0) {
770 bb_error_msg("Wrong len %d", len);
771 return -1;
772 }
773
774 memset(tb, 0, sizeof(tb));
775 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
776
777 if (tb[RTA_PREFSRC]) {
778 tb[RTA_PREFSRC]->rta_type = RTA_SRC;
779 r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
780 } else if (!tb[RTA_SRC]) {
781 bb_error_msg("Failed to connect the route");
782 return -1;
783 }
784 if (!odev && tb[RTA_OIF]) {
785 tb[RTA_OIF]->rta_type = 0;
786 }
787 if (tb[RTA_GATEWAY]) {
788 tb[RTA_GATEWAY]->rta_type = 0;
789 }
790 if (!idev && tb[RTA_IIF]) {
791 tb[RTA_IIF]->rta_type = 0;
792 }
793 req.n.nlmsg_flags = NLM_F_REQUEST;
794 req.n.nlmsg_type = RTM_GETROUTE;
795
796 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
797 exit(2);
798 }
799 }
800
801 if (print_route(NULL, &req.n, (void*)stdout) < 0) {
802 bb_error_msg_and_die("An error :-)");
803 }
804
805 exit(0);
806}
807
808int do_iproute(int argc, char **argv)
809{
810 const char *ip_route_commands[] = { "add", "append", "change", "chg",
811 "delete", "del", "get", "list", "show", "prepend", "replace", "test", "flush", 0 };
812 unsigned short command_num = 7;
813 unsigned int flags = 0;
814 int cmd = RTM_NEWROUTE;
815
816 if (*argv) {
817 command_num = compare_string_array(ip_route_commands, *argv);
818 }
819 switch(command_num) {
820 case 0: /* add*/
821 flags = NLM_F_CREATE|NLM_F_EXCL;
822 break;
823 case 1: /* append */
824 flags = NLM_F_CREATE|NLM_F_APPEND;
825 break;
826 case 2: /* change */
827 case 3: /* chg */
828 flags = NLM_F_REPLACE;
829 break;
830 case 4: /* delete */
831 case 5: /* del */
832 cmd = RTM_DELROUTE;
833 break;
834 case 6: /* get */
835 return iproute_get(argc-1, argv+1);
836 case 7: /* list */
837 case 8: /* show */
838 return iproute_list_or_flush(argc-1, argv+1, 0);
839 case 9: /* prepend */
840 flags = NLM_F_CREATE;
841 case 10: /* replace */
842 flags = NLM_F_CREATE|NLM_F_REPLACE;
843 case 11: /* test */
844 flags = NLM_F_EXCL;
845 case 12: /* flush */
846 return iproute_list_or_flush(argc-1, argv+1, 1);
847 default:
848 bb_error_msg_and_die("Unknown command %s", *argv);
849 }
850
851 return iproute_modify(cmd, flags, argc-1, argv+1);
852}
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 @@
1/*
2 * iptunnel.c "ip tunnel"
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 *
12 * Changes:
13 *
14 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
15 * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit
16 * Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag
17 */
18
19#include <sys/socket.h>
20#include <sys/ioctl.h>
21
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25
26#include <arpa/inet.h>
27#include <netinet/ip.h>
28#include <netinet/in.h>
29
30#include <net/if.h>
31#include <net/if_arp.h>
32
33#include <asm/types.h>
34#define __constant_htons htons
35#include <linux/if_tunnel.h>
36
37#include "rt_names.h"
38#include "utils.h"
39
40#include "libbb.h"
41
42static int do_ioctl_get_ifindex(char *dev)
43{
44 struct ifreq ifr;
45 int fd;
46
47 strcpy(ifr.ifr_name, dev);
48 fd = socket(AF_INET, SOCK_DGRAM, 0);
49 if (ioctl(fd, SIOCGIFINDEX, &ifr)) {
50 bb_perror_msg("ioctl");
51 return 0;
52 }
53 close(fd);
54 return ifr.ifr_ifindex;
55}
56
57static int do_ioctl_get_iftype(char *dev)
58{
59 struct ifreq ifr;
60 int fd;
61
62 strcpy(ifr.ifr_name, dev);
63 fd = socket(AF_INET, SOCK_DGRAM, 0);
64 if (ioctl(fd, SIOCGIFHWADDR, &ifr)) {
65 bb_perror_msg("ioctl");
66 return -1;
67 }
68 close(fd);
69 return ifr.ifr_addr.sa_family;
70}
71
72
73static char *do_ioctl_get_ifname(int idx)
74{
75 static struct ifreq ifr;
76 int fd;
77
78 ifr.ifr_ifindex = idx;
79 fd = socket(AF_INET, SOCK_DGRAM, 0);
80 if (ioctl(fd, SIOCGIFNAME, &ifr)) {
81 bb_perror_msg("ioctl");
82 return NULL;
83 }
84 close(fd);
85 return ifr.ifr_name;
86}
87
88
89
90static int do_get_ioctl(char *basedev, struct ip_tunnel_parm *p)
91{
92 struct ifreq ifr;
93 int fd;
94 int err;
95
96 strcpy(ifr.ifr_name, basedev);
97 ifr.ifr_ifru.ifru_data = (void*)p;
98 fd = socket(AF_INET, SOCK_DGRAM, 0);
99 err = ioctl(fd, SIOCGETTUNNEL, &ifr);
100 if (err) {
101 bb_perror_msg("ioctl");
102 }
103 close(fd);
104 return err;
105}
106
107static int do_add_ioctl(int cmd, char *basedev, struct ip_tunnel_parm *p)
108{
109 struct ifreq ifr;
110 int fd;
111 int err;
112
113 if (cmd == SIOCCHGTUNNEL && p->name[0]) {
114 strcpy(ifr.ifr_name, p->name);
115 } else {
116 strcpy(ifr.ifr_name, basedev);
117 }
118 ifr.ifr_ifru.ifru_data = (void*)p;
119 fd = socket(AF_INET, SOCK_DGRAM, 0);
120 err = ioctl(fd, cmd, &ifr);
121 if (err) {
122 bb_perror_msg("ioctl");
123 }
124 close(fd);
125 return err;
126}
127
128static int do_del_ioctl(char *basedev, struct ip_tunnel_parm *p)
129{
130 struct ifreq ifr;
131 int fd;
132 int err;
133
134 if (p->name[0]) {
135 strcpy(ifr.ifr_name, p->name);
136 } else {
137 strcpy(ifr.ifr_name, basedev);
138 }
139 ifr.ifr_ifru.ifru_data = (void*)p;
140 fd = socket(AF_INET, SOCK_DGRAM, 0);
141 err = ioctl(fd, SIOCDELTUNNEL, &ifr);
142 if (err) {
143 bb_perror_msg("ioctl");
144 }
145 close(fd);
146 return err;
147}
148
149static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p)
150{
151 int count = 0;
152 char medium[IFNAMSIZ];
153 memset(p, 0, sizeof(*p));
154 memset(&medium, 0, sizeof(medium));
155
156 p->iph.version = 4;
157 p->iph.ihl = 5;
158#ifndef IP_DF
159#define IP_DF 0x4000 /* Flag: "Don't Fragment" */
160#endif
161 p->iph.frag_off = htons(IP_DF);
162
163 while (argc > 0) {
164 if (strcmp(*argv, "mode") == 0) {
165 NEXT_ARG();
166 if (strcmp(*argv, "ipip") == 0 ||
167 strcmp(*argv, "ip/ip") == 0) {
168 if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) {
169 bb_error_msg("You managed to ask for more than one tunnel mode.");
170 exit(-1);
171 }
172 p->iph.protocol = IPPROTO_IPIP;
173 } else if (strcmp(*argv, "gre") == 0 ||
174 strcmp(*argv, "gre/ip") == 0) {
175 if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) {
176 bb_error_msg("You managed to ask for more than one tunnel mode.");
177 exit(-1);
178 }
179 p->iph.protocol = IPPROTO_GRE;
180 } else if (strcmp(*argv, "sit") == 0 ||
181 strcmp(*argv, "ipv6/ip") == 0) {
182 if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
183 bb_error_msg("You managed to ask for more than one tunnel mode.");
184 exit(-1);
185 }
186 p->iph.protocol = IPPROTO_IPV6;
187 } else {
188 bb_error_msg("Cannot guess tunnel mode.");
189 exit(-1);
190 }
191 } else if (strcmp(*argv, "key") == 0) {
192 unsigned uval;
193 NEXT_ARG();
194 p->i_flags |= GRE_KEY;
195 p->o_flags |= GRE_KEY;
196 if (strchr(*argv, '.'))
197 p->i_key = p->o_key = get_addr32(*argv);
198 else {
199 if (get_unsigned(&uval, *argv, 0)<0) {
200 bb_error_msg("invalid value of \"key\"");
201 exit(-1);
202 }
203 p->i_key = p->o_key = htonl(uval);
204 }
205 } else if (strcmp(*argv, "ikey") == 0) {
206 unsigned uval;
207 NEXT_ARG();
208 p->i_flags |= GRE_KEY;
209 if (strchr(*argv, '.'))
210 p->o_key = get_addr32(*argv);
211 else {
212 if (get_unsigned(&uval, *argv, 0)<0) {
213 bb_error_msg("invalid value of \"ikey\"");
214 exit(-1);
215 }
216 p->i_key = htonl(uval);
217 }
218 } else if (strcmp(*argv, "okey") == 0) {
219 unsigned uval;
220 NEXT_ARG();
221 p->o_flags |= GRE_KEY;
222 if (strchr(*argv, '.'))
223 p->o_key = get_addr32(*argv);
224 else {
225 if (get_unsigned(&uval, *argv, 0)<0) {
226 bb_error_msg("invalid value of \"okey\"");
227 exit(-1);
228 }
229 p->o_key = htonl(uval);
230 }
231 } else if (strcmp(*argv, "seq") == 0) {
232 p->i_flags |= GRE_SEQ;
233 p->o_flags |= GRE_SEQ;
234 } else if (strcmp(*argv, "iseq") == 0) {
235 p->i_flags |= GRE_SEQ;
236 } else if (strcmp(*argv, "oseq") == 0) {
237 p->o_flags |= GRE_SEQ;
238 } else if (strcmp(*argv, "csum") == 0) {
239 p->i_flags |= GRE_CSUM;
240 p->o_flags |= GRE_CSUM;
241 } else if (strcmp(*argv, "icsum") == 0) {
242 p->i_flags |= GRE_CSUM;
243 } else if (strcmp(*argv, "ocsum") == 0) {
244 p->o_flags |= GRE_CSUM;
245 } else if (strcmp(*argv, "nopmtudisc") == 0) {
246 p->iph.frag_off = 0;
247 } else if (strcmp(*argv, "pmtudisc") == 0) {
248 p->iph.frag_off = htons(IP_DF);
249 } else if (strcmp(*argv, "remote") == 0) {
250 NEXT_ARG();
251 if (strcmp(*argv, "any"))
252 p->iph.daddr = get_addr32(*argv);
253 } else if (strcmp(*argv, "local") == 0) {
254 NEXT_ARG();
255 if (strcmp(*argv, "any"))
256 p->iph.saddr = get_addr32(*argv);
257 } else if (strcmp(*argv, "dev") == 0) {
258 NEXT_ARG();
259 strncpy(medium, *argv, IFNAMSIZ-1);
260 } else if (strcmp(*argv, "ttl") == 0) {
261 unsigned uval;
262 NEXT_ARG();
263 if (strcmp(*argv, "inherit") != 0) {
264 if (get_unsigned(&uval, *argv, 0))
265 invarg("invalid TTL\n", *argv);
266 if (uval > 255)
267 invarg("TTL must be <=255\n", *argv);
268 p->iph.ttl = uval;
269 }
270 } else if (strcmp(*argv, "tos") == 0 ||
271 matches(*argv, "dsfield") == 0) {
272 __u32 uval;
273 NEXT_ARG();
274 if (strcmp(*argv, "inherit") != 0) {
275 if (rtnl_dsfield_a2n(&uval, *argv))
276 invarg("bad TOS value", *argv);
277 p->iph.tos = uval;
278 } else
279 p->iph.tos = 1;
280 } else {
281 if (strcmp(*argv, "name") == 0) {
282 NEXT_ARG();
283 }
284 if (p->name[0])
285 duparg2("name", *argv);
286 strncpy(p->name, *argv, IFNAMSIZ);
287 if (cmd == SIOCCHGTUNNEL && count == 0) {
288 struct ip_tunnel_parm old_p;
289 memset(&old_p, 0, sizeof(old_p));
290 if (do_get_ioctl(*argv, &old_p))
291 return -1;
292 *p = old_p;
293 }
294 }
295 count++;
296 argc--; argv++;
297 }
298
299
300 if (p->iph.protocol == 0) {
301 if (memcmp(p->name, "gre", 3) == 0)
302 p->iph.protocol = IPPROTO_GRE;
303 else if (memcmp(p->name, "ipip", 4) == 0)
304 p->iph.protocol = IPPROTO_IPIP;
305 else if (memcmp(p->name, "sit", 3) == 0)
306 p->iph.protocol = IPPROTO_IPV6;
307 }
308
309 if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) {
310 if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) {
311 bb_error_msg("Keys are not allowed with ipip and sit.");
312 return -1;
313 }
314 }
315
316 if (medium[0]) {
317 p->link = do_ioctl_get_ifindex(medium);
318 if (p->link == 0)
319 return -1;
320 }
321
322 if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
323 p->i_key = p->iph.daddr;
324 p->i_flags |= GRE_KEY;
325 }
326 if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
327 p->o_key = p->iph.daddr;
328 p->o_flags |= GRE_KEY;
329 }
330 if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) {
331 bb_error_msg("Broadcast tunnel requires a source address.");
332 return -1;
333 }
334 return 0;
335}
336
337
338static int do_add(int cmd, int argc, char **argv)
339{
340 struct ip_tunnel_parm p;
341
342 if (parse_args(argc, argv, cmd, &p) < 0)
343 return -1;
344
345 if (p.iph.ttl && p.iph.frag_off == 0) {
346 bb_error_msg("ttl != 0 and noptmudisc are incompatible");
347 return -1;
348 }
349
350 switch (p.iph.protocol) {
351 case IPPROTO_IPIP:
352 return do_add_ioctl(cmd, "tunl0", &p);
353 case IPPROTO_GRE:
354 return do_add_ioctl(cmd, "gre0", &p);
355 case IPPROTO_IPV6:
356 return do_add_ioctl(cmd, "sit0", &p);
357 default:
358 bb_error_msg("cannot determine tunnel mode (ipip, gre or sit)");
359 return -1;
360 }
361 return -1;
362}
363
364int do_del(int argc, char **argv)
365{
366 struct ip_tunnel_parm p;
367
368 if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0)
369 return -1;
370
371 switch (p.iph.protocol) {
372 case IPPROTO_IPIP:
373 return do_del_ioctl("tunl0", &p);
374 case IPPROTO_GRE:
375 return do_del_ioctl("gre0", &p);
376 case IPPROTO_IPV6:
377 return do_del_ioctl("sit0", &p);
378 default:
379 return do_del_ioctl(p.name, &p);
380 }
381 return -1;
382}
383
384void print_tunnel(struct ip_tunnel_parm *p)
385{
386 char s1[256];
387 char s2[256];
388 char s3[64];
389 char s4[64];
390
391 format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1));
392 format_host(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2));
393 inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
394 inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
395
396 printf("%s: %s/ip remote %s local %s ",
397 p->name,
398 p->iph.protocol == IPPROTO_IPIP ? "ip" :
399 (p->iph.protocol == IPPROTO_GRE ? "gre" :
400 (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")),
401 p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any");
402 if (p->link) {
403 char *n = do_ioctl_get_ifname(p->link);
404 if (n)
405 printf(" dev %s ", n);
406 }
407 if (p->iph.ttl)
408 printf(" ttl %d ", p->iph.ttl);
409 else
410 printf(" ttl inherit ");
411 if (p->iph.tos) {
412 SPRINT_BUF(b1);
413 printf(" tos");
414 if (p->iph.tos&1)
415 printf(" inherit");
416 if (p->iph.tos&~1)
417 printf("%c%s ", p->iph.tos&1 ? '/' : ' ',
418 rtnl_dsfield_n2a(p->iph.tos&~1, b1, sizeof(b1)));
419 }
420 if (!(p->iph.frag_off&htons(IP_DF)))
421 printf(" nopmtudisc");
422
423 if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key)
424 printf(" key %s", s3);
425 else if ((p->i_flags|p->o_flags)&GRE_KEY) {
426 if (p->i_flags&GRE_KEY)
427 printf(" ikey %s ", s3);
428 if (p->o_flags&GRE_KEY)
429 printf(" okey %s ", s4);
430 }
431
432 if (p->i_flags&GRE_SEQ)
433 printf("%s Drop packets out of sequence.\n", _SL_);
434 if (p->i_flags&GRE_CSUM)
435 printf("%s Checksum in received packet is required.", _SL_);
436 if (p->o_flags&GRE_SEQ)
437 printf("%s Sequence packets on output.", _SL_);
438 if (p->o_flags&GRE_CSUM)
439 printf("%s Checksum output packets.", _SL_);
440}
441
442static int do_tunnels_list(struct ip_tunnel_parm *p)
443{
444 char name[IFNAMSIZ];
445 unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
446 rx_fifo, rx_frame,
447 tx_bytes, tx_packets, tx_errs, tx_drops,
448 tx_fifo, tx_colls, tx_carrier, rx_multi;
449 int type;
450 struct ip_tunnel_parm p1;
451
452 char buf[512];
453 FILE *fp = fopen("/proc/net/dev", "r");
454 if (fp == NULL) {
455 perror("fopen");
456 return -1;
457 }
458
459 fgets(buf, sizeof(buf), fp);
460 fgets(buf, sizeof(buf), fp);
461
462 while (fgets(buf, sizeof(buf), fp) != NULL) {
463 char *ptr;
464 buf[sizeof(buf) - 1] = 0;
465 if ((ptr = strchr(buf, ':')) == NULL ||
466 (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
467 bb_error_msg("Wrong format of /proc/net/dev. Sorry.");
468 return -1;
469 }
470 if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld",
471 &rx_bytes, &rx_packets, &rx_errs, &rx_drops,
472 &rx_fifo, &rx_frame, &rx_multi,
473 &tx_bytes, &tx_packets, &tx_errs, &tx_drops,
474 &tx_fifo, &tx_colls, &tx_carrier) != 14)
475 continue;
476 if (p->name[0] && strcmp(p->name, name))
477 continue;
478 type = do_ioctl_get_iftype(name);
479 if (type == -1) {
480 bb_error_msg("Failed to get type of [%s]", name);
481 continue;
482 }
483 if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT)
484 continue;
485 memset(&p1, 0, sizeof(p1));
486 if (do_get_ioctl(name, &p1))
487 continue;
488 if ((p->link && p1.link != p->link) ||
489 (p->name[0] && strcmp(p1.name, p->name)) ||
490 (p->iph.daddr && p1.iph.daddr != p->iph.daddr) ||
491 (p->iph.saddr && p1.iph.saddr != p->iph.saddr) ||
492 (p->i_key && p1.i_key != p->i_key))
493 continue;
494 print_tunnel(&p1);
495 printf("\n");
496 }
497 return 0;
498}
499
500static int do_show(int argc, char **argv)
501{
502 int err;
503 struct ip_tunnel_parm p;
504
505 if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0)
506 return -1;
507
508 switch (p.iph.protocol) {
509 case IPPROTO_IPIP:
510 err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p);
511 break;
512 case IPPROTO_GRE:
513 err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p);
514 break;
515 case IPPROTO_IPV6:
516 err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p);
517 break;
518 default:
519 do_tunnels_list(&p);
520 return 0;
521 }
522 if (err)
523 return -1;
524
525 print_tunnel(&p);
526 printf("\n");
527 return 0;
528}
529
530int do_iptunnel(int argc, char **argv)
531{
532 if (argc > 0) {
533 if (matches(*argv, "add") == 0)
534 return do_add(SIOCADDTUNNEL, argc-1, argv+1);
535 if (matches(*argv, "change") == 0)
536 return do_add(SIOCCHGTUNNEL, argc-1, argv+1);
537 if (matches(*argv, "del") == 0)
538 return do_del(argc-1, argv+1);
539 if (matches(*argv, "show") == 0 ||
540 matches(*argv, "lst") == 0 ||
541 matches(*argv, "list") == 0)
542 return do_show(argc-1, argv+1);
543 } else
544 return do_show(0, NULL);
545
546 bb_error_msg("Command \"%s\" is unknown.", *argv);
547 exit(-1);
548}
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 @@
1/*
2 * libnetlink.c RTnetlink service routines.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 */
12
13#include <sys/socket.h>
14
15#include <errno.h>
16#include <stdio.h>
17#include <string.h>
18#include <time.h>
19#include <unistd.h>
20
21#include <sys/uio.h>
22
23#include "libnetlink.h"
24#include "libbb.h"
25
26void rtnl_close(struct rtnl_handle *rth)
27{
28 close(rth->fd);
29}
30
31int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
32{
33 int addr_len;
34
35 memset(rth, 0, sizeof(rth));
36
37 rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
38 if (rth->fd < 0) {
39 bb_perror_msg("Cannot open netlink socket");
40 return -1;
41 }
42
43 memset(&rth->local, 0, sizeof(rth->local));
44 rth->local.nl_family = AF_NETLINK;
45 rth->local.nl_groups = subscriptions;
46
47 if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
48 bb_perror_msg("Cannot bind netlink socket");
49 return -1;
50 }
51 addr_len = sizeof(rth->local);
52 if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
53 bb_perror_msg("Cannot getsockname");
54 return -1;
55 }
56 if (addr_len != sizeof(rth->local)) {
57 bb_error_msg("Wrong address length %d", addr_len);
58 return -1;
59 }
60 if (rth->local.nl_family != AF_NETLINK) {
61 bb_error_msg("Wrong address family %d", rth->local.nl_family);
62 return -1;
63 }
64 rth->seq = time(NULL);
65 return 0;
66}
67
68int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
69{
70 struct {
71 struct nlmsghdr nlh;
72 struct rtgenmsg g;
73 } req;
74 struct sockaddr_nl nladdr;
75
76 memset(&nladdr, 0, sizeof(nladdr));
77 nladdr.nl_family = AF_NETLINK;
78
79 req.nlh.nlmsg_len = sizeof(req);
80 req.nlh.nlmsg_type = type;
81 req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
82 req.nlh.nlmsg_pid = 0;
83 req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
84 req.g.rtgen_family = family;
85
86 return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
87}
88
89int rtnl_send(struct rtnl_handle *rth, char *buf, int len)
90{
91 struct sockaddr_nl nladdr;
92
93 memset(&nladdr, 0, sizeof(nladdr));
94 nladdr.nl_family = AF_NETLINK;
95
96 return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
97}
98
99int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
100{
101 struct nlmsghdr nlh;
102 struct sockaddr_nl nladdr;
103 struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } };
104 struct msghdr msg = {
105 (void*)&nladdr, sizeof(nladdr),
106 iov, 2,
107 NULL, 0,
108 0
109 };
110
111 memset(&nladdr, 0, sizeof(nladdr));
112 nladdr.nl_family = AF_NETLINK;
113
114 nlh.nlmsg_len = NLMSG_LENGTH(len);
115 nlh.nlmsg_type = type;
116 nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
117 nlh.nlmsg_pid = 0;
118 nlh.nlmsg_seq = rth->dump = ++rth->seq;
119
120 return sendmsg(rth->fd, &msg, 0);
121}
122
123int rtnl_dump_filter(struct rtnl_handle *rth,
124 int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
125 void *arg1,
126 int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
127 void *arg2)
128{
129 char buf[8192];
130 struct sockaddr_nl nladdr;
131 struct iovec iov = { buf, sizeof(buf) };
132
133 while (1) {
134 int status;
135 struct nlmsghdr *h;
136
137 struct msghdr msg = {
138 (void*)&nladdr, sizeof(nladdr),
139 &iov, 1,
140 NULL, 0,
141 0
142 };
143
144 status = recvmsg(rth->fd, &msg, 0);
145
146 if (status < 0) {
147 if (errno == EINTR)
148 continue;
149 bb_perror_msg("OVERRUN");
150 continue;
151 }
152 if (status == 0) {
153 bb_error_msg("EOF on netlink");
154 return -1;
155 }
156 if (msg.msg_namelen != sizeof(nladdr)) {
157 bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
158 }
159
160 h = (struct nlmsghdr*)buf;
161 while (NLMSG_OK(h, status)) {
162 int err;
163
164 if (nladdr.nl_pid != 0 ||
165 h->nlmsg_pid != rth->local.nl_pid ||
166 h->nlmsg_seq != rth->dump) {
167 if (junk) {
168 err = junk(&nladdr, h, arg2);
169 if (err < 0) {
170 return err;
171 }
172 }
173 goto skip_it;
174 }
175
176 if (h->nlmsg_type == NLMSG_DONE) {
177 return 0;
178 }
179 if (h->nlmsg_type == NLMSG_ERROR) {
180 struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h);
181 if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
182 bb_error_msg("ERROR truncated");
183 } else {
184 errno = -l_err->error;
185 bb_perror_msg("RTNETLINK answers");
186 }
187 return -1;
188 }
189 err = filter(&nladdr, h, arg1);
190 if (err < 0) {
191 return err;
192 }
193
194skip_it:
195 h = NLMSG_NEXT(h, status);
196 }
197 if (msg.msg_flags & MSG_TRUNC) {
198 bb_error_msg("Message truncated");
199 continue;
200 }
201 if (status) {
202 bb_error_msg_and_die("!!!Remnant of size %d", status);
203 }
204 }
205}
206
207int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
208 unsigned groups, struct nlmsghdr *answer,
209 int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
210 void *jarg)
211{
212 int status;
213 unsigned seq;
214 struct nlmsghdr *h;
215 struct sockaddr_nl nladdr;
216 struct iovec iov = { (void*)n, n->nlmsg_len };
217 char buf[8192];
218 struct msghdr msg = {
219 (void*)&nladdr, sizeof(nladdr),
220 &iov, 1,
221 NULL, 0,
222 0
223 };
224
225 memset(&nladdr, 0, sizeof(nladdr));
226 nladdr.nl_family = AF_NETLINK;
227 nladdr.nl_pid = peer;
228 nladdr.nl_groups = groups;
229
230 n->nlmsg_seq = seq = ++rtnl->seq;
231 if (answer == NULL) {
232 n->nlmsg_flags |= NLM_F_ACK;
233 }
234 status = sendmsg(rtnl->fd, &msg, 0);
235
236 if (status < 0) {
237 bb_perror_msg("Cannot talk to rtnetlink");
238 return -1;
239 }
240
241 iov.iov_base = buf;
242
243 while (1) {
244 iov.iov_len = sizeof(buf);
245 status = recvmsg(rtnl->fd, &msg, 0);
246
247 if (status < 0) {
248 if (errno == EINTR) {
249 continue;
250 }
251 bb_perror_msg("OVERRUN");
252 continue;
253 }
254 if (status == 0) {
255 bb_error_msg("EOF on netlink");
256 return -1;
257 }
258 if (msg.msg_namelen != sizeof(nladdr)) {
259 bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
260 }
261 for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
262 int l_err;
263 int len = h->nlmsg_len;
264 int l = len - sizeof(*h);
265
266 if (l<0 || len>status) {
267 if (msg.msg_flags & MSG_TRUNC) {
268 bb_error_msg("Truncated message");
269 return -1;
270 }
271 bb_error_msg_and_die("!!!malformed message: len=%d", len);
272 }
273
274 if (nladdr.nl_pid != peer ||
275 h->nlmsg_pid != rtnl->local.nl_pid ||
276 h->nlmsg_seq != seq) {
277 if (junk) {
278 l_err = junk(&nladdr, h, jarg);
279 if (l_err < 0) {
280 return l_err;
281 }
282 }
283 continue;
284 }
285
286 if (h->nlmsg_type == NLMSG_ERROR) {
287 struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
288 if (l < sizeof(struct nlmsgerr)) {
289 bb_error_msg("ERROR truncated");
290 } else {
291 errno = -err->error;
292 if (errno == 0) {
293 if (answer) {
294 memcpy(answer, h, h->nlmsg_len);
295 }
296 return 0;
297 }
298 bb_perror_msg("RTNETLINK answers");
299 }
300 return -1;
301 }
302 if (answer) {
303 memcpy(answer, h, h->nlmsg_len);
304 return 0;
305 }
306
307 bb_error_msg("Unexpected reply!!!");
308
309 status -= NLMSG_ALIGN(len);
310 h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
311 }
312 if (msg.msg_flags & MSG_TRUNC) {
313 bb_error_msg("Message truncated");
314 continue;
315 }
316 if (status) {
317 bb_error_msg_and_die("!!!Remnant of size %d", status);
318 }
319 }
320}
321
322int rtnl_listen(struct rtnl_handle *rtnl,
323 int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
324 void *jarg)
325{
326 int status;
327 struct nlmsghdr *h;
328 struct sockaddr_nl nladdr;
329 struct iovec iov;
330 char buf[8192];
331 struct msghdr msg = {
332 (void*)&nladdr, sizeof(nladdr),
333 &iov, 1,
334 NULL, 0,
335 0
336 };
337
338 memset(&nladdr, 0, sizeof(nladdr));
339 nladdr.nl_family = AF_NETLINK;
340 nladdr.nl_pid = 0;
341 nladdr.nl_groups = 0;
342
343
344 iov.iov_base = buf;
345
346 while (1) {
347 iov.iov_len = sizeof(buf);
348 status = recvmsg(rtnl->fd, &msg, 0);
349
350 if (status < 0) {
351 if (errno == EINTR)
352 continue;
353 bb_perror_msg("OVERRUN");
354 continue;
355 }
356 if (status == 0) {
357 bb_error_msg("EOF on netlink");
358 return -1;
359 }
360 if (msg.msg_namelen != sizeof(nladdr)) {
361 bb_error_msg_and_die("Sender address length == %d", msg.msg_namelen);
362 }
363 for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
364 int err;
365 int len = h->nlmsg_len;
366 int l = len - sizeof(*h);
367
368 if (l<0 || len>status) {
369 if (msg.msg_flags & MSG_TRUNC) {
370 bb_error_msg("Truncated message");
371 return -1;
372 }
373 bb_error_msg_and_die("!!!malformed message: len=%d", len);
374 }
375
376 err = handler(&nladdr, h, jarg);
377 if (err < 0) {
378 return err;
379 }
380
381 status -= NLMSG_ALIGN(len);
382 h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
383 }
384 if (msg.msg_flags & MSG_TRUNC) {
385 bb_error_msg("Message truncated");
386 continue;
387 }
388 if (status) {
389 bb_error_msg_and_die("!!!Remnant of size %d", status);
390 }
391 }
392}
393
394int rtnl_from_file(FILE *rtnl,
395 int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
396 void *jarg)
397{
398 int status;
399 struct sockaddr_nl nladdr;
400 char buf[8192];
401 struct nlmsghdr *h = (void*)buf;
402
403 memset(&nladdr, 0, sizeof(nladdr));
404 nladdr.nl_family = AF_NETLINK;
405 nladdr.nl_pid = 0;
406 nladdr.nl_groups = 0;
407
408 while (1) {
409 int err, len, type;
410 int l;
411
412 status = fread(&buf, 1, sizeof(*h), rtnl);
413
414 if (status < 0) {
415 if (errno == EINTR)
416 continue;
417 bb_perror_msg("rtnl_from_file: fread");
418 return -1;
419 }
420 if (status == 0)
421 return 0;
422
423 len = h->nlmsg_len;
424 type= h->nlmsg_type;
425 l = len - sizeof(*h);
426
427 if (l<0 || len>sizeof(buf)) {
428 bb_error_msg("!!!malformed message: len=%d @%lu",
429 len, ftell(rtnl));
430 return -1;
431 }
432
433 status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
434
435 if (status < 0) {
436 bb_perror_msg("rtnl_from_file: fread");
437 return -1;
438 }
439 if (status < l) {
440 bb_error_msg("rtnl-from_file: truncated message");
441 return -1;
442 }
443
444 err = handler(&nladdr, h, jarg);
445 if (err < 0)
446 return err;
447 }
448}
449
450int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
451{
452 int len = RTA_LENGTH(4);
453 struct rtattr *rta;
454 if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
455 return -1;
456 rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
457 rta->rta_type = type;
458 rta->rta_len = len;
459 memcpy(RTA_DATA(rta), &data, 4);
460 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
461 return 0;
462}
463
464int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
465{
466 int len = RTA_LENGTH(alen);
467 struct rtattr *rta;
468
469 if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
470 return -1;
471 rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
472 rta->rta_type = type;
473 rta->rta_len = len;
474 memcpy(RTA_DATA(rta), data, alen);
475 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
476 return 0;
477}
478
479int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
480{
481 int len = RTA_LENGTH(4);
482 struct rtattr *subrta;
483
484 if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
485 return -1;
486 }
487 subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
488 subrta->rta_type = type;
489 subrta->rta_len = len;
490 memcpy(RTA_DATA(subrta), &data, 4);
491 rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
492 return 0;
493}
494
495int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
496{
497 struct rtattr *subrta;
498 int len = RTA_LENGTH(alen);
499
500 if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
501 return -1;
502 }
503 subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
504 subrta->rta_type = type;
505 subrta->rta_len = len;
506 memcpy(RTA_DATA(subrta), data, alen);
507 rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
508 return 0;
509}
510
511
512int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
513{
514 while (RTA_OK(rta, len)) {
515 if (rta->rta_type <= max) {
516 tb[rta->rta_type] = rta;
517 }
518 rta = RTA_NEXT(rta,len);
519 }
520 if (len) {
521 bb_error_msg("!!!Deficit %d, rta_len=%d", len, rta->rta_len);
522 }
523 return 0;
524}
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 @@
1#ifndef __LIBNETLINK_H__
2#define __LIBNETLINK_H__ 1
3
4#include <asm/types.h>
5#include <linux/netlink.h>
6#include <linux/rtnetlink.h>
7
8struct rtnl_handle
9{
10 int fd;
11 struct sockaddr_nl local;
12 struct sockaddr_nl peer;
13 __u32 seq;
14 __u32 dump;
15};
16
17extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
18extern void rtnl_close(struct rtnl_handle *rth);
19extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
20extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
21extern int rtnl_dump_filter(struct rtnl_handle *rth,
22 int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
23 void *arg1,
24 int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
25 void *arg2);
26extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
27 unsigned groups, struct nlmsghdr *answer,
28 int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
29 void *jarg);
30extern int rtnl_send(struct rtnl_handle *rth, char *buf, int);
31
32
33extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
34extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen);
35extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
36extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen);
37
38extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
39
40extern int rtnl_listen(struct rtnl_handle *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
41 void *jarg);
42extern int rtnl_from_file(FILE *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
43 void *jarg);
44
45#endif /* __LIBNETLINK_H__ */
46
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 @@
1#ifndef __LINUX_PKT_SCHED_H
2#define __LINUX_PKT_SCHED_H
3
4/* Logical priority bands not depending on specific packet scheduler.
5 Every scheduler will map them to real traffic classes, if it has
6 no more precise mechanism to classify packets.
7
8 These numbers have no special meaning, though their coincidence
9 with obsolete IPv6 values is not occasional :-). New IPv6 drafts
10 preferred full anarchy inspired by diffserv group.
11
12 Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy
13 class, actually, as rule it will be handled with more care than
14 filler or even bulk.
15 */
16
17#include <asm/types.h>
18
19#define TC_PRIO_BESTEFFORT 0
20#define TC_PRIO_FILLER 1
21#define TC_PRIO_BULK 2
22#define TC_PRIO_INTERACTIVE_BULK 4
23#define TC_PRIO_INTERACTIVE 6
24#define TC_PRIO_CONTROL 7
25
26#define TC_PRIO_MAX 15
27
28/* Generic queue statistics, available for all the elements.
29 Particular schedulers may have also their private records.
30 */
31
32struct tc_stats
33{
34 __u64 bytes; /* NUmber of enqueues bytes */
35 __u32 packets; /* Number of enqueued packets */
36 __u32 drops; /* Packets dropped because of lack of resources */
37 __u32 overlimits; /* Number of throttle events when this
38 * flow goes out of allocated bandwidth */
39 __u32 bps; /* Current flow byte rate */
40 __u32 pps; /* Current flow packet rate */
41 __u32 qlen;
42 __u32 backlog;
43#ifdef __KERNEL__
44 spinlock_t *lock;
45#endif
46};
47
48struct tc_estimator
49{
50 char interval;
51 unsigned char ewma_log;
52};
53
54/* "Handles"
55 ---------
56
57 All the traffic control objects have 32bit identifiers, or "handles".
58
59 They can be considered as opaque numbers from user API viewpoint,
60 but actually they always consist of two fields: major and
61 minor numbers, which are interpreted by kernel specially,
62 that may be used by applications, though not recommended.
63
64 F.e. qdisc handles always have minor number equal to zero,
65 classes (or flows) have major equal to parent qdisc major, and
66 minor uniquely identifying class inside qdisc.
67
68 Macros to manipulate handles:
69 */
70
71#define TC_H_MAJ_MASK (0xFFFF0000U)
72#define TC_H_MIN_MASK (0x0000FFFFU)
73#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK)
74#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK)
75#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK))
76
77#define TC_H_UNSPEC (0U)
78#define TC_H_ROOT (0xFFFFFFFFU)
79#define TC_H_INGRESS (0xFFFFFFF1U)
80
81struct tc_ratespec
82{
83 unsigned char cell_log;
84 unsigned char __reserved;
85 unsigned short feature;
86 short addend;
87 unsigned short mpu;
88 __u32 rate;
89};
90
91/* FIFO section */
92
93struct tc_fifo_qopt
94{
95 __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */
96};
97
98/* PRIO section */
99
100#define TCQ_PRIO_BANDS 16
101
102struct tc_prio_qopt
103{
104 int bands; /* Number of bands */
105 __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */
106};
107
108/* CSZ section */
109
110struct tc_csz_qopt
111{
112 int flows; /* Maximal number of guaranteed flows */
113 unsigned char R_log; /* Fixed point position for round number */
114 unsigned char delta_log; /* Log of maximal managed time interval */
115 __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> CSZ band */
116};
117
118struct tc_csz_copt
119{
120 struct tc_ratespec slice;
121 struct tc_ratespec rate;
122 struct tc_ratespec peakrate;
123 __u32 limit;
124 __u32 buffer;
125 __u32 mtu;
126};
127
128enum
129{
130 TCA_CSZ_UNSPEC,
131 TCA_CSZ_PARMS,
132 TCA_CSZ_RTAB,
133 TCA_CSZ_PTAB,
134};
135
136/* TBF section */
137
138struct tc_tbf_qopt
139{
140 struct tc_ratespec rate;
141 struct tc_ratespec peakrate;
142 __u32 limit;
143 __u32 buffer;
144 __u32 mtu;
145};
146
147enum
148{
149 TCA_TBF_UNSPEC,
150 TCA_TBF_PARMS,
151 TCA_TBF_RTAB,
152 TCA_TBF_PTAB,
153};
154
155
156/* TEQL section */
157
158/* TEQL does not require any parameters */
159
160/* SFQ section */
161
162struct tc_sfq_qopt
163{
164 unsigned quantum; /* Bytes per round allocated to flow */
165 int perturb_period; /* Period of hash perturbation */
166 __u32 limit; /* Maximal packets in queue */
167 unsigned divisor; /* Hash divisor */
168 unsigned flows; /* Maximal number of flows */
169};
170
171/*
172 * NOTE: limit, divisor and flows are hardwired to code at the moment.
173 *
174 * limit=flows=128, divisor=1024;
175 *
176 * The only reason for this is efficiency, it is possible
177 * to change these parameters in compile time.
178 */
179
180/* RED section */
181
182enum
183{
184 TCA_RED_UNSPEC,
185 TCA_RED_PARMS,
186 TCA_RED_STAB,
187};
188
189struct tc_red_qopt
190{
191 __u32 limit; /* HARD maximal queue length (bytes) */
192 __u32 qth_min; /* Min average length threshold (bytes) */
193 __u32 qth_max; /* Max average length threshold (bytes) */
194 unsigned char Wlog; /* log(W) */
195 unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */
196 unsigned char Scell_log; /* cell size for idle damping */
197 unsigned char flags;
198#define TC_RED_ECN 1
199};
200
201struct tc_red_xstats
202{
203 __u32 early; /* Early drops */
204 __u32 pdrop; /* Drops due to queue limits */
205 __u32 other; /* Drops due to drop() calls */
206 __u32 marked; /* Marked packets */
207};
208
209/* GRED section */
210
211#define MAX_DPs 16
212
213enum
214{
215 TCA_GRED_UNSPEC,
216 TCA_GRED_PARMS,
217 TCA_GRED_STAB,
218 TCA_GRED_DPS,
219};
220
221#define TCA_SET_OFF TCA_GRED_PARMS
222struct tc_gred_qopt
223{
224 __u32 limit; /* HARD maximal queue length (bytes)
225*/
226 __u32 qth_min; /* Min average length threshold (bytes)
227*/
228 __u32 qth_max; /* Max average length threshold (bytes)
229*/
230 __u32 DP; /* upto 2^32 DPs */
231 __u32 backlog;
232 __u32 qave;
233 __u32 forced;
234 __u32 early;
235 __u32 other;
236 __u32 pdrop;
237
238 unsigned char Wlog; /* log(W) */
239 unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */
240 unsigned char Scell_log; /* cell size for idle damping */
241 __u8 prio; /* prio of this VQ */
242 __u32 packets;
243 __u32 bytesin;
244};
245/* gred setup */
246struct tc_gred_sopt
247{
248 __u32 DPs;
249 __u32 def_DP;
250 __u8 grio;
251};
252
253/* HTB section */
254#define TC_HTB_NUMPRIO 4
255#define TC_HTB_MAXDEPTH 4
256
257struct tc_htb_opt
258{
259 struct tc_ratespec rate;
260 struct tc_ratespec ceil;
261 __u32 buffer;
262 __u32 cbuffer;
263 __u32 quantum; /* out only */
264 __u32 level; /* out only */
265 __u8 prio;
266 __u8 injectd; /* inject class distance */
267 __u8 pad[2];
268};
269struct tc_htb_glob
270{
271 __u32 rate2quantum; /* bps->quantum divisor */
272 __u32 defcls; /* default class number */
273 __u32 use_dcache; /* use dequeue cache ? */
274 __u32 debug; /* debug flags */
275
276
277 /* stats */
278 __u32 deq_rate; /* dequeue rate */
279 __u32 utilz; /* dequeue utilization */
280 __u32 trials; /* deq_prio trials per dequeue */
281 __u32 dcache_hits;
282 __u32 direct_pkts; /* count of non shapped packets */
283};
284enum
285{
286 TCA_HTB_UNSPEC,
287 TCA_HTB_PARMS,
288 TCA_HTB_INIT,
289 TCA_HTB_CTAB,
290 TCA_HTB_RTAB,
291};
292struct tc_htb_xstats
293{
294 __u32 lends;
295 __u32 borrows;
296 __u32 giants; /* too big packets (rate will not be accurate) */
297 __u32 injects; /* how many times leaf used injected bw */
298 __u32 tokens;
299 __u32 ctokens;
300};
301
302/* CBQ section */
303
304#define TC_CBQ_MAXPRIO 8
305#define TC_CBQ_MAXLEVEL 8
306#define TC_CBQ_DEF_EWMA 5
307
308struct tc_cbq_lssopt
309{
310 unsigned char change;
311 unsigned char flags;
312#define TCF_CBQ_LSS_BOUNDED 1
313#define TCF_CBQ_LSS_ISOLATED 2
314 unsigned char ewma_log;
315 unsigned char level;
316#define TCF_CBQ_LSS_FLAGS 1
317#define TCF_CBQ_LSS_EWMA 2
318#define TCF_CBQ_LSS_MAXIDLE 4
319#define TCF_CBQ_LSS_MINIDLE 8
320#define TCF_CBQ_LSS_OFFTIME 0x10
321#define TCF_CBQ_LSS_AVPKT 0x20
322 __u32 maxidle;
323 __u32 minidle;
324 __u32 offtime;
325 __u32 avpkt;
326};
327
328struct tc_cbq_wrropt
329{
330 unsigned char flags;
331 unsigned char priority;
332 unsigned char cpriority;
333 unsigned char __reserved;
334 __u32 allot;
335 __u32 weight;
336};
337
338struct tc_cbq_ovl
339{
340 unsigned char strategy;
341#define TC_CBQ_OVL_CLASSIC 0
342#define TC_CBQ_OVL_DELAY 1
343#define TC_CBQ_OVL_LOWPRIO 2
344#define TC_CBQ_OVL_DROP 3
345#define TC_CBQ_OVL_RCLASSIC 4
346 unsigned char priority2;
347 __u32 penalty;
348};
349
350struct tc_cbq_police
351{
352 unsigned char police;
353 unsigned char __res1;
354 unsigned short __res2;
355};
356
357struct tc_cbq_fopt
358{
359 __u32 split;
360 __u32 defmap;
361 __u32 defchange;
362};
363
364struct tc_cbq_xstats
365{
366 __u32 borrows;
367 __u32 overactions;
368 __s32 avgidle;
369 __s32 undertime;
370};
371
372enum
373{
374 TCA_CBQ_UNSPEC,
375 TCA_CBQ_LSSOPT,
376 TCA_CBQ_WRROPT,
377 TCA_CBQ_FOPT,
378 TCA_CBQ_OVL_STRATEGY,
379 TCA_CBQ_RATE,
380 TCA_CBQ_RTAB,
381 TCA_CBQ_POLICE,
382};
383
384#define TCA_CBQ_MAX TCA_CBQ_POLICE
385
386/* dsmark section */
387
388enum {
389 TCA_DSMARK_UNSPEC,
390 TCA_DSMARK_INDICES,
391 TCA_DSMARK_DEFAULT_INDEX,
392 TCA_DSMARK_SET_TC_INDEX,
393 TCA_DSMARK_MASK,
394 TCA_DSMARK_VALUE
395};
396
397#define TCA_DSMARK_MAX TCA_DSMARK_VALUE
398
399/* ATM section */
400
401enum {
402 TCA_ATM_UNSPEC,
403 TCA_ATM_FD, /* file/socket descriptor */
404 TCA_ATM_PTR, /* pointer to descriptor - later */
405 TCA_ATM_HDR, /* LL header */
406 TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */
407 TCA_ATM_ADDR, /* PVC address (for output only) */
408 TCA_ATM_STATE /* VC state (ATM_VS_*; for output only) */
409};
410
411#define TCA_ATM_MAX TCA_ATM_STATE
412
413#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 @@
1/*
2 * ll_addr.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 */
11
12#include <arpa/inet.h>
13#include <string.h>
14#include <net/if_arp.h>
15#include "utils.h"
16#include "libbb.h"
17
18const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen)
19{
20 int i;
21 int l;
22
23 if (alen == 4 &&
24 (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == ARPHRD_IPGRE)) {
25 return inet_ntop(AF_INET, addr, buf, blen);
26 }
27 l = 0;
28 for (i=0; i<alen; i++) {
29 if (i==0) {
30 snprintf(buf+l, blen, "%02x", addr[i]);
31 blen -= 2;
32 l += 2;
33 } else {
34 snprintf(buf+l, blen, ":%02x", addr[i]);
35 blen -= 3;
36 l += 3;
37 }
38 }
39 return buf;
40}
41
42int ll_addr_a2n(unsigned char *lladdr, int len, char *arg)
43{
44 if (strchr(arg, '.')) {
45 inet_prefix pfx;
46 if (get_addr_1(&pfx, arg, AF_INET)) {
47 bb_error_msg("\"%s\" is invalid lladdr.", arg);
48 return -1;
49 }
50 if (len < 4) {
51 return -1;
52 }
53 memcpy(lladdr, pfx.data, 4);
54 return 4;
55 } else {
56 int i;
57
58 for (i=0; i<len; i++) {
59 int temp;
60 char *cp = strchr(arg, ':');
61 if (cp) {
62 *cp = 0;
63 cp++;
64 }
65 if (sscanf(arg, "%x", &temp) != 1) {
66 bb_error_msg("\"%s\" is invalid lladdr.", arg);
67 return -1;
68 }
69 if (temp < 0 || temp > 255) {
70 bb_error_msg("\"%s\" is invalid lladdr.", arg);
71 return -1;
72 }
73 lladdr[i] = temp;
74 if (!cp) {
75 break;
76 }
77 arg = cp;
78 }
79 return i+1;
80 }
81}
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 @@
1/*
2 * ll_map.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 */
12
13#include <stdio.h>
14#include <string.h>
15#include <stdlib.h>
16#include <netinet/in.h>
17
18#include "libnetlink.h"
19
20struct idxmap
21{
22 struct idxmap * next;
23 int index;
24 int type;
25 int alen;
26 unsigned flags;
27 unsigned char addr[8];
28 char name[16];
29};
30
31static struct idxmap *idxmap[16];
32
33int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
34{
35 int h;
36 struct ifinfomsg *ifi = NLMSG_DATA(n);
37 struct idxmap *im, **imp;
38 struct rtattr *tb[IFLA_MAX+1];
39
40 if (n->nlmsg_type != RTM_NEWLINK)
41 return 0;
42
43 if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
44 return -1;
45
46
47 memset(tb, 0, sizeof(tb));
48 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
49 if (tb[IFLA_IFNAME] == NULL)
50 return 0;
51
52 h = ifi->ifi_index&0xF;
53
54 for (imp=&idxmap[h]; (im=*imp)!=NULL; imp = &im->next)
55 if (im->index == ifi->ifi_index)
56 break;
57
58 if (im == NULL) {
59 im = malloc(sizeof(*im));
60 if (im == NULL)
61 return 0;
62 im->next = *imp;
63 im->index = ifi->ifi_index;
64 *imp = im;
65 }
66
67 im->type = ifi->ifi_type;
68 im->flags = ifi->ifi_flags;
69 if (tb[IFLA_ADDRESS]) {
70 int alen;
71 im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
72 if (alen > sizeof(im->addr))
73 alen = sizeof(im->addr);
74 memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen);
75 } else {
76 im->alen = 0;
77 memset(im->addr, 0, sizeof(im->addr));
78 }
79 strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME]));
80 return 0;
81}
82
83const char *ll_idx_n2a(int idx, char *buf)
84{
85 struct idxmap *im;
86
87 if (idx == 0)
88 return "*";
89 for (im = idxmap[idx&0xF]; im; im = im->next)
90 if (im->index == idx)
91 return im->name;
92 snprintf(buf, 16, "if%d", idx);
93 return buf;
94}
95
96
97const char *ll_index_to_name(int idx)
98{
99 static char nbuf[16];
100
101 return ll_idx_n2a(idx, nbuf);
102}
103
104int ll_index_to_type(int idx)
105{
106 struct idxmap *im;
107
108 if (idx == 0)
109 return -1;
110 for (im = idxmap[idx&0xF]; im; im = im->next)
111 if (im->index == idx)
112 return im->type;
113 return -1;
114}
115
116unsigned ll_index_to_flags(int idx)
117{
118 struct idxmap *im;
119
120 if (idx == 0)
121 return 0;
122
123 for (im = idxmap[idx&0xF]; im; im = im->next)
124 if (im->index == idx)
125 return im->flags;
126 return 0;
127}
128
129int ll_name_to_index(char *name)
130{
131 static char ncache[16];
132 static int icache;
133 struct idxmap *im;
134 int i;
135
136 if (name == NULL)
137 return 0;
138 if (icache && strcmp(name, ncache) == 0)
139 return icache;
140 for (i=0; i<16; i++) {
141 for (im = idxmap[i]; im; im = im->next) {
142 if (strcmp(im->name, name) == 0) {
143 icache = im->index;
144 strcpy(ncache, name);
145 return im->index;
146 }
147 }
148 }
149 return 0;
150}
151
152int ll_init_map(struct rtnl_handle *rth)
153{
154 if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) {
155 perror("Cannot send dump request");
156 exit(1);
157 }
158
159 if (rtnl_dump_filter(rth, ll_remember_index, &idxmap, NULL, NULL) < 0) {
160 fprintf(stderr, "Dump terminated\n");
161 exit(1);
162 }
163 return 0;
164}
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 @@
1#ifndef __LL_MAP_H__
2#define __LL_MAP_H__ 1
3
4extern int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
5extern int ll_init_map(struct rtnl_handle *rth);
6extern int ll_name_to_index(char *name);
7extern const char *ll_index_to_name(int idx);
8extern const char *ll_idx_n2a(int idx, char *buf);
9extern int ll_index_to_type(int idx);
10extern unsigned ll_index_to_flags(int idx);
11
12#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 @@
1/*
2 * ll_proto.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 */
11
12#include <stdio.h>
13#include <arpa/inet.h>
14#include <string.h>
15#include "utils.h"
16
17#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
18#include <net/ethernet.h>
19#else
20#include <linux/if_ether.h>
21#endif
22
23#define __PF(f,n) { ETH_P_##f, #n },
24static struct {
25 int id;
26 char *name;
27} llproto_names[] = {
28__PF(LOOP,loop)
29__PF(PUP,pup)
30#ifdef ETH_P_PUPAT
31__PF(PUPAT,pupat)
32#endif
33__PF(IP,ip)
34__PF(X25,x25)
35__PF(ARP,arp)
36__PF(BPQ,bpq)
37#ifdef ETH_P_IEEEPUP
38__PF(IEEEPUP,ieeepup)
39#endif
40#ifdef ETH_P_IEEEPUPAT
41__PF(IEEEPUPAT,ieeepupat)
42#endif
43__PF(DEC,dec)
44__PF(DNA_DL,dna_dl)
45__PF(DNA_RC,dna_rc)
46__PF(DNA_RT,dna_rt)
47__PF(LAT,lat)
48__PF(DIAG,diag)
49__PF(CUST,cust)
50__PF(SCA,sca)
51__PF(RARP,rarp)
52__PF(ATALK,atalk)
53__PF(AARP,aarp)
54__PF(IPX,ipx)
55__PF(IPV6,ipv6)
56#ifdef ETH_P_PPP_DISC
57__PF(PPP_DISC,ppp_disc)
58#endif
59#ifdef ETH_P_PPP_SES
60__PF(PPP_SES,ppp_ses)
61#endif
62#ifdef ETH_P_ATMMPOA
63__PF(ATMMPOA,atmmpoa)
64#endif
65#ifdef ETH_P_ATMFATE
66__PF(ATMFATE,atmfate)
67#endif
68
69__PF(802_3,802_3)
70__PF(AX25,ax25)
71__PF(ALL,all)
72__PF(802_2,802_2)
73__PF(SNAP,snap)
74__PF(DDCMP,ddcmp)
75__PF(WAN_PPP,wan_ppp)
76__PF(PPP_MP,ppp_mp)
77__PF(LOCALTALK,localtalk)
78__PF(PPPTALK,ppptalk)
79__PF(TR_802_2,tr_802_2)
80__PF(MOBITEX,mobitex)
81__PF(CONTROL,control)
82__PF(IRDA,irda)
83#ifdef ETH_P_ECONET
84__PF(ECONET,econet)
85#endif
86
87{ 0x8100, "802.1Q" },
88{ ETH_P_IP, "ipv4" },
89};
90#undef __PF
91
92
93char * ll_proto_n2a(unsigned short id, char *buf, int len)
94{
95 int i;
96
97 id = ntohs(id);
98
99 for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
100 if (llproto_names[i].id == id)
101 return llproto_names[i].name;
102 }
103 snprintf(buf, len, "[%d]", id);
104 return buf;
105}
106
107int ll_proto_a2n(unsigned short *id, char *buf)
108{
109 int i;
110 for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
111 if (strcasecmp(llproto_names[i].name, buf) == 0) {
112 *id = htons(llproto_names[i].id);
113 return 0;
114 }
115 }
116 if (get_u16(id, buf, 0))
117 return -1;
118 *id = htons(*id);
119 return 0;
120}
diff --git a/busybox/networking/libiproute/ll_types.c b/busybox/networking/libiproute/ll_types.c
new file mode 100644
index 000000000..f39f777e1
--- /dev/null
+++ b/busybox/networking/libiproute/ll_types.c
@@ -0,0 +1,115 @@
1/*
2 * ll_types.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 */
11#include <stdio.h>
12#include <arpa/inet.h>
13
14#include <linux/if_arp.h>
15
16char * ll_type_n2a(int type, char *buf, int len)
17{
18#define __PF(f,n) { ARPHRD_##f, #n },
19static struct {
20 int type;
21 char *name;
22} arphrd_names[] = {
23{ 0, "generic" },
24__PF(ETHER,ether)
25__PF(EETHER,eether)
26__PF(AX25,ax25)
27__PF(PRONET,pronet)
28__PF(CHAOS,chaos)
29#ifdef ARPHRD_IEEE802_TR
30__PF(IEEE802,ieee802)
31#else
32__PF(IEEE802,tr)
33#endif
34__PF(ARCNET,arcnet)
35__PF(APPLETLK,atalk)
36__PF(DLCI,dlci)
37#ifdef ARPHRD_ATM
38__PF(ATM,atm)
39#endif
40__PF(METRICOM,metricom)
41#ifdef ARPHRD_IEEE1394
42__PF(IEEE1394,ieee1394)
43#endif
44
45__PF(SLIP,slip)
46__PF(CSLIP,cslip)
47__PF(SLIP6,slip6)
48__PF(CSLIP6,cslip6)
49__PF(RSRVD,rsrvd)
50__PF(ADAPT,adapt)
51__PF(ROSE,rose)
52__PF(X25,x25)
53#ifdef ARPHRD_HWX25
54__PF(HWX25,hwx25)
55#endif
56__PF(PPP,ppp)
57__PF(HDLC,hdlc)
58__PF(LAPB,lapb)
59#ifdef ARPHRD_DDCMP
60__PF(DDCMP,ddcmp)
61__PF(RAWHDLC,rawhdlc)
62#endif
63
64__PF(TUNNEL,ipip)
65__PF(TUNNEL6,tunnel6)
66__PF(FRAD,frad)
67__PF(SKIP,skip)
68__PF(LOOPBACK,loopback)
69__PF(LOCALTLK,ltalk)
70__PF(FDDI,fddi)
71__PF(BIF,bif)
72__PF(SIT,sit)
73__PF(IPDDP,ip/ddp)
74__PF(IPGRE,gre)
75__PF(PIMREG,pimreg)
76__PF(HIPPI,hippi)
77__PF(ASH,ash)
78__PF(ECONET,econet)
79__PF(IRDA,irda)
80__PF(FCPP,fcpp)
81__PF(FCAL,fcal)
82__PF(FCPL,fcpl)
83__PF(FCFABRIC,fcfb0)
84__PF(FCFABRIC+1,fcfb1)
85__PF(FCFABRIC+2,fcfb2)
86__PF(FCFABRIC+3,fcfb3)
87__PF(FCFABRIC+4,fcfb4)
88__PF(FCFABRIC+5,fcfb5)
89__PF(FCFABRIC+6,fcfb6)
90__PF(FCFABRIC+7,fcfb7)
91__PF(FCFABRIC+8,fcfb8)
92__PF(FCFABRIC+9,fcfb9)
93__PF(FCFABRIC+10,fcfb10)
94__PF(FCFABRIC+11,fcfb11)
95__PF(FCFABRIC+12,fcfb12)
96#ifdef ARPHRD_IEEE802_TR
97__PF(IEEE802_TR,tr)
98#endif
99#ifdef ARPHRD_IEEE80211
100__PF(IEEE80211,ieee802.11)
101#endif
102#ifdef ARPHRD_VOID
103__PF(VOID,void)
104#endif
105};
106#undef __PF
107
108 int i;
109 for (i=0; i<sizeof(arphrd_names)/sizeof(arphrd_names[0]); i++) {
110 if (arphrd_names[i].type == type)
111 return arphrd_names[i].name;
112 }
113 snprintf(buf, len, "[%d]", type);
114 return buf;
115}
diff --git a/busybox/networking/libiproute/rt_names.c b/busybox/networking/libiproute/rt_names.c
new file mode 100644
index 000000000..d503645b0
--- /dev/null
+++ b/busybox/networking/libiproute/rt_names.c
@@ -0,0 +1,385 @@
1/*
2 * rt_names.c rtnetlink names DB.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 */
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15#include <stdint.h>
16
17static void rtnl_tab_initialize(char *file, char **tab, int size)
18{
19 char buf[512];
20 FILE *fp;
21
22 fp = fopen(file, "r");
23 if (!fp)
24 return;
25 while (fgets(buf, sizeof(buf), fp)) {
26 char *p = buf;
27 int id;
28 char namebuf[512];
29
30 while (*p == ' ' || *p == '\t')
31 p++;
32 if (*p == '#' || *p == '\n' || *p == 0)
33 continue;
34 if (sscanf(p, "0x%x %s\n", &id, namebuf) != 2 &&
35 sscanf(p, "0x%x %s #", &id, namebuf) != 2 &&
36 sscanf(p, "%d %s\n", &id, namebuf) != 2 &&
37 sscanf(p, "%d %s #", &id, namebuf) != 2) {
38 fprintf(stderr, "Database %s is corrupted at %s\n",
39 file, p);
40 return;
41 }
42
43 if (id<0 || id>size)
44 continue;
45
46 tab[id] = strdup(namebuf);
47 }
48 fclose(fp);
49}
50
51
52static char * rtnl_rtprot_tab[256] = {
53 "none",
54 "redirect",
55 "kernel",
56 "boot",
57 "static",
58 NULL,
59 NULL,
60 NULL,
61 "gated",
62 "ra",
63 "mrt",
64 "zebra",
65 "bird",
66};
67
68
69
70static int rtnl_rtprot_init;
71
72static void rtnl_rtprot_initialize(void)
73{
74 rtnl_rtprot_init = 1;
75 rtnl_tab_initialize("/etc/iproute2/rt_protos",
76 rtnl_rtprot_tab, 256);
77}
78
79char * rtnl_rtprot_n2a(int id, char *buf, int len)
80{
81 if (id<0 || id>=256) {
82 snprintf(buf, len, "%d", id);
83 return buf;
84 }
85 if (!rtnl_rtprot_tab[id]) {
86 if (!rtnl_rtprot_init)
87 rtnl_rtprot_initialize();
88 }
89 if (rtnl_rtprot_tab[id])
90 return rtnl_rtprot_tab[id];
91 snprintf(buf, len, "%d", id);
92 return buf;
93}
94
95int rtnl_rtprot_a2n(uint32_t *id, char *arg)
96{
97 static char *cache = NULL;
98 static unsigned long res;
99 char *end;
100 int i;
101
102 if (cache && strcmp(cache, arg) == 0) {
103 *id = res;
104 return 0;
105 }
106
107 if (!rtnl_rtprot_init)
108 rtnl_rtprot_initialize();
109
110 for (i=0; i<256; i++) {
111 if (rtnl_rtprot_tab[i] &&
112 strcmp(rtnl_rtprot_tab[i], arg) == 0) {
113 cache = rtnl_rtprot_tab[i];
114 res = i;
115 *id = res;
116 return 0;
117 }
118 }
119
120 res = strtoul(arg, &end, 0);
121 if (!end || end == arg || *end || res > 255)
122 return -1;
123 *id = res;
124 return 0;
125}
126
127
128
129static char * rtnl_rtscope_tab[256] = {
130 "global",
131};
132
133static int rtnl_rtscope_init;
134
135static void rtnl_rtscope_initialize(void)
136{
137 rtnl_rtscope_init = 1;
138 rtnl_rtscope_tab[255] = "nowhere";
139 rtnl_rtscope_tab[254] = "host";
140 rtnl_rtscope_tab[253] = "link";
141 rtnl_rtscope_tab[200] = "site";
142 rtnl_tab_initialize("/etc/iproute2/rt_scopes",
143 rtnl_rtscope_tab, 256);
144}
145
146char * rtnl_rtscope_n2a(int id, char *buf, int len)
147{
148 if (id<0 || id>=256) {
149 snprintf(buf, len, "%d", id);
150 return buf;
151 }
152 if (!rtnl_rtscope_tab[id]) {
153 if (!rtnl_rtscope_init)
154 rtnl_rtscope_initialize();
155 }
156 if (rtnl_rtscope_tab[id])
157 return rtnl_rtscope_tab[id];
158 snprintf(buf, len, "%d", id);
159 return buf;
160}
161
162int rtnl_rtscope_a2n(uint32_t *id, char *arg)
163{
164 static char *cache = NULL;
165 static unsigned long res;
166 char *end;
167 int i;
168
169 if (cache && strcmp(cache, arg) == 0) {
170 *id = res;
171 return 0;
172 }
173
174 if (!rtnl_rtscope_init)
175 rtnl_rtscope_initialize();
176
177 for (i=0; i<256; i++) {
178 if (rtnl_rtscope_tab[i] &&
179 strcmp(rtnl_rtscope_tab[i], arg) == 0) {
180 cache = rtnl_rtscope_tab[i];
181 res = i;
182 *id = res;
183 return 0;
184 }
185 }
186
187 res = strtoul(arg, &end, 0);
188 if (!end || end == arg || *end || res > 255)
189 return -1;
190 *id = res;
191 return 0;
192}
193
194
195
196static char * rtnl_rtrealm_tab[256] = {
197 "unknown",
198};
199
200static int rtnl_rtrealm_init;
201
202static void rtnl_rtrealm_initialize(void)
203{
204 rtnl_rtrealm_init = 1;
205 rtnl_tab_initialize("/etc/iproute2/rt_realms",
206 rtnl_rtrealm_tab, 256);
207}
208
209char * rtnl_rtrealm_n2a(int id, char *buf, int len)
210{
211 if (id<0 || id>=256) {
212 snprintf(buf, len, "%d", id);
213 return buf;
214 }
215 if (!rtnl_rtrealm_tab[id]) {
216 if (!rtnl_rtrealm_init)
217 rtnl_rtrealm_initialize();
218 }
219 if (rtnl_rtrealm_tab[id])
220 return rtnl_rtrealm_tab[id];
221 snprintf(buf, len, "%d", id);
222 return buf;
223}
224
225
226int rtnl_rtrealm_a2n(uint32_t *id, char *arg)
227{
228 static char *cache = NULL;
229 static unsigned long res;
230 char *end;
231 int i;
232
233 if (cache && strcmp(cache, arg) == 0) {
234 *id = res;
235 return 0;
236 }
237
238 if (!rtnl_rtrealm_init)
239 rtnl_rtrealm_initialize();
240
241 for (i=0; i<256; i++) {
242 if (rtnl_rtrealm_tab[i] &&
243 strcmp(rtnl_rtrealm_tab[i], arg) == 0) {
244 cache = rtnl_rtrealm_tab[i];
245 res = i;
246 *id = res;
247 return 0;
248 }
249 }
250
251 res = strtoul(arg, &end, 0);
252 if (!end || end == arg || *end || res > 255)
253 return -1;
254 *id = res;
255 return 0;
256}
257
258
259
260static char * rtnl_rttable_tab[256] = {
261 "unspec",
262};
263
264static int rtnl_rttable_init;
265
266static void rtnl_rttable_initialize(void)
267{
268 rtnl_rttable_init = 1;
269 rtnl_rttable_tab[255] = "local";
270 rtnl_rttable_tab[254] = "main";
271 rtnl_tab_initialize("/etc/iproute2/rt_tables",
272 rtnl_rttable_tab, 256);
273}
274
275char * rtnl_rttable_n2a(int id, char *buf, int len)
276{
277 if (id<0 || id>=256) {
278 snprintf(buf, len, "%d", id);
279 return buf;
280 }
281 if (!rtnl_rttable_tab[id]) {
282 if (!rtnl_rttable_init)
283 rtnl_rttable_initialize();
284 }
285 if (rtnl_rttable_tab[id])
286 return rtnl_rttable_tab[id];
287 snprintf(buf, len, "%d", id);
288 return buf;
289}
290
291int rtnl_rttable_a2n(uint32_t *id, char *arg)
292{
293 static char *cache = NULL;
294 static unsigned long res;
295 char *end;
296 int i;
297
298 if (cache && strcmp(cache, arg) == 0) {
299 *id = res;
300 return 0;
301 }
302
303 if (!rtnl_rttable_init)
304 rtnl_rttable_initialize();
305
306 for (i=0; i<256; i++) {
307 if (rtnl_rttable_tab[i] &&
308 strcmp(rtnl_rttable_tab[i], arg) == 0) {
309 cache = rtnl_rttable_tab[i];
310 res = i;
311 *id = res;
312 return 0;
313 }
314 }
315
316 i = strtoul(arg, &end, 0);
317 if (!end || end == arg || *end || i > 255)
318 return -1;
319 *id = i;
320 return 0;
321}
322
323
324static char * rtnl_rtdsfield_tab[256] = {
325 "0",
326};
327
328static int rtnl_rtdsfield_init;
329
330static void rtnl_rtdsfield_initialize(void)
331{
332 rtnl_rtdsfield_init = 1;
333 rtnl_tab_initialize("/etc/iproute2/rt_dsfield",
334 rtnl_rtdsfield_tab, 256);
335}
336
337char * rtnl_dsfield_n2a(int id, char *buf, int len)
338{
339 if (id<0 || id>=256) {
340 snprintf(buf, len, "%d", id);
341 return buf;
342 }
343 if (!rtnl_rtdsfield_tab[id]) {
344 if (!rtnl_rtdsfield_init)
345 rtnl_rtdsfield_initialize();
346 }
347 if (rtnl_rtdsfield_tab[id])
348 return rtnl_rtdsfield_tab[id];
349 snprintf(buf, len, "0x%02x", id);
350 return buf;
351}
352
353
354int rtnl_dsfield_a2n(uint32_t *id, char *arg)
355{
356 static char *cache = NULL;
357 static unsigned long res;
358 char *end;
359 int i;
360
361 if (cache && strcmp(cache, arg) == 0) {
362 *id = res;
363 return 0;
364 }
365
366 if (!rtnl_rtdsfield_init)
367 rtnl_rtdsfield_initialize();
368
369 for (i=0; i<256; i++) {
370 if (rtnl_rtdsfield_tab[i] &&
371 strcmp(rtnl_rtdsfield_tab[i], arg) == 0) {
372 cache = rtnl_rtdsfield_tab[i];
373 res = i;
374 *id = res;
375 return 0;
376 }
377 }
378
379 res = strtoul(arg, &end, 16);
380 if (!end || end == arg || *end || res > 255)
381 return -1;
382 *id = res;
383 return 0;
384}
385
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 @@
1#ifndef RT_NAMES_H_
2#define RT_NAMES_H_ 1
3
4#include <stdint.h>
5
6const char* rtnl_rtprot_n2a(int id, char *buf, int len);
7const char* rtnl_rtscope_n2a(int id, char *buf, int len);
8const char* rtnl_rttable_n2a(int id, char *buf, int len);
9const char* rtnl_rtrealm_n2a(int id, char *buf, int len);
10const char* rtnl_dsfield_n2a(int id, char *buf, int len);
11int rtnl_rtprot_a2n(int *id, char *arg);
12int rtnl_rtscope_a2n(int *id, char *arg);
13int rtnl_rttable_a2n(int *id, char *arg);
14int rtnl_rtrealm_a2n(uint32_t *id, char *arg);
15int rtnl_dsfield_a2n(uint32_t *id, char *arg);
16
17const char *inet_proto_n2a(int proto, char *buf, int len);
18int inet_proto_a2n(char *buf);
19
20
21const char * ll_type_n2a(int type, char *buf, int len);
22
23const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen);
24int ll_addr_a2n(unsigned char *lladdr, int len, char *arg);
25
26const char * ll_proto_n2a(unsigned short id, char *buf, int len);
27int ll_proto_a2n(unsigned short *id, char *buf);
28
29
30#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 @@
1/*
2 * rtm_map.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 */
12
13#include <stdlib.h>
14#include <string.h>
15
16#include "rt_names.h"
17#include "utils.h"
18
19char *rtnl_rtntype_n2a(int id, char *buf, int len)
20{
21 switch (id) {
22 case RTN_UNSPEC:
23 return "none";
24 case RTN_UNICAST:
25 return "unicast";
26 case RTN_LOCAL:
27 return "local";
28 case RTN_BROADCAST:
29 return "broadcast";
30 case RTN_ANYCAST:
31 return "anycast";
32 case RTN_MULTICAST:
33 return "multicast";
34 case RTN_BLACKHOLE:
35 return "blackhole";
36 case RTN_UNREACHABLE:
37 return "unreachable";
38 case RTN_PROHIBIT:
39 return "prohibit";
40 case RTN_THROW:
41 return "throw";
42 case RTN_NAT:
43 return "nat";
44 case RTN_XRESOLVE:
45 return "xresolve";
46 default:
47 snprintf(buf, len, "%d", id);
48 return buf;
49 }
50}
51
52
53int rtnl_rtntype_a2n(int *id, char *arg)
54{
55 char *end;
56 unsigned long res;
57
58 if (strcmp(arg, "local") == 0)
59 res = RTN_LOCAL;
60 else if (strcmp(arg, "nat") == 0)
61 res = RTN_NAT;
62 else if (matches(arg, "broadcast") == 0 ||
63 strcmp(arg, "brd") == 0)
64 res = RTN_BROADCAST;
65 else if (matches(arg, "anycast") == 0)
66 res = RTN_ANYCAST;
67 else if (matches(arg, "multicast") == 0)
68 res = RTN_MULTICAST;
69 else if (matches(arg, "prohibit") == 0)
70 res = RTN_PROHIBIT;
71 else if (matches(arg, "unreachable") == 0)
72 res = RTN_UNREACHABLE;
73 else if (matches(arg, "blackhole") == 0)
74 res = RTN_BLACKHOLE;
75 else if (matches(arg, "xresolve") == 0)
76 res = RTN_XRESOLVE;
77 else if (matches(arg, "unicast") == 0)
78 res = RTN_UNICAST;
79 else if (strcmp(arg, "throw") == 0)
80 res = RTN_THROW;
81 else {
82 res = strtoul(arg, &end, 0);
83 if (!end || end == arg || *end || res > 255)
84 return -1;
85 }
86 *id = res;
87 return 0;
88}
89
90int get_rt_realms(__u32 *realms, char *arg)
91{
92 __u32 realm = 0;
93 char *p = strchr(arg, '/');
94
95 *realms = 0;
96 if (p) {
97 *p = 0;
98 if (rtnl_rtrealm_a2n(realms, arg)) {
99 *p = '/';
100 return -1;
101 }
102 *realms <<= 16;
103 *p = '/';
104 arg = p+1;
105 }
106 if (*arg && rtnl_rtrealm_a2n(&realm, arg))
107 return -1;
108 *realms |= realm;
109 return 0;
110}
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 @@
1#ifndef __RTM_MAP_H__
2#define __RTM_MAP_H__ 1
3
4char *rtnl_rtntype_n2a(int id, char *buf, int len);
5int rtnl_rtntype_a2n(int *id, char *arg);
6
7int get_rt_realms(__u32 *realms, char *arg);
8
9
10#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 @@
1/*
2 * utils.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 *
12 * Changes:
13 *
14 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
15 */
16
17#include <stdlib.h>
18#include <string.h>
19#include <unistd.h>
20#include <arpa/inet.h>
21
22#include "utils.h"
23#include "libbb.h"
24
25int get_integer(int *val, char *arg, int base)
26{
27 long res;
28 char *ptr;
29
30 if (!arg || !*arg)
31 return -1;
32 res = strtol(arg, &ptr, base);
33 if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN)
34 return -1;
35 *val = res;
36 return 0;
37}
38
39int get_unsigned(unsigned *val, char *arg, int base)
40{
41 unsigned long res;
42 char *ptr;
43
44 if (!arg || !*arg)
45 return -1;
46 res = strtoul(arg, &ptr, base);
47 if (!ptr || ptr == arg || *ptr || res > UINT_MAX)
48 return -1;
49 *val = res;
50 return 0;
51}
52
53int get_u32(__u32 * val, char *arg, int base)
54{
55 unsigned long res;
56 char *ptr;
57
58 if (!arg || !*arg)
59 return -1;
60 res = strtoul(arg, &ptr, base);
61 if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL)
62 return -1;
63 *val = res;
64 return 0;
65}
66
67int get_u16(__u16 * val, char *arg, int base)
68{
69 unsigned long res;
70 char *ptr;
71
72 if (!arg || !*arg)
73 return -1;
74 res = strtoul(arg, &ptr, base);
75 if (!ptr || ptr == arg || *ptr || res > 0xFFFF)
76 return -1;
77 *val = res;
78 return 0;
79}
80
81int get_u8(__u8 * val, char *arg, int base)
82{
83 unsigned long res;
84 char *ptr;
85
86 if (!arg || !*arg)
87 return -1;
88 res = strtoul(arg, &ptr, base);
89 if (!ptr || ptr == arg || *ptr || res > 0xFF)
90 return -1;
91 *val = res;
92 return 0;
93}
94
95int get_s16(__s16 * val, char *arg, int base)
96{
97 long res;
98 char *ptr;
99
100 if (!arg || !*arg)
101 return -1;
102 res = strtol(arg, &ptr, base);
103 if (!ptr || ptr == arg || *ptr || res > 0x7FFF || res < -0x8000)
104 return -1;
105 *val = res;
106 return 0;
107}
108
109int get_s8(__s8 * val, char *arg, int base)
110{
111 long res;
112 char *ptr;
113
114 if (!arg || !*arg)
115 return -1;
116 res = strtol(arg, &ptr, base);
117 if (!ptr || ptr == arg || *ptr || res > 0x7F || res < -0x80)
118 return -1;
119 *val = res;
120 return 0;
121}
122
123int get_addr_1(inet_prefix * addr, char *name, int family)
124{
125 char *cp;
126 unsigned char *ap = (unsigned char *) addr->data;
127 int i;
128
129 memset(addr, 0, sizeof(*addr));
130
131 if (strcmp(name, "default") == 0 ||
132 strcmp(name, "all") == 0 || strcmp(name, "any") == 0) {
133 addr->family = family;
134 addr->bytelen = (family == AF_INET6 ? 16 : 4);
135 addr->bitlen = -1;
136 return 0;
137 }
138
139 if (strchr(name, ':')) {
140 addr->family = AF_INET6;
141 if (family != AF_UNSPEC && family != AF_INET6)
142 return -1;
143 if (inet_pton(AF_INET6, name, addr->data) <= 0)
144 return -1;
145 addr->bytelen = 16;
146 addr->bitlen = -1;
147 return 0;
148 }
149
150 addr->family = AF_INET;
151 if (family != AF_UNSPEC && family != AF_INET)
152 return -1;
153 addr->bytelen = 4;
154 addr->bitlen = -1;
155 for (cp = name, i = 0; *cp; cp++) {
156 if (*cp <= '9' && *cp >= '0') {
157 ap[i] = 10 * ap[i] + (*cp - '0');
158 continue;
159 }
160 if (*cp == '.' && ++i <= 3)
161 continue;
162 return -1;
163 }
164 return 0;
165}
166
167int get_prefix_1(inet_prefix * dst, char *arg, int family)
168{
169 int err;
170 unsigned plen;
171 char *slash;
172
173 memset(dst, 0, sizeof(*dst));
174
175 if (strcmp(arg, "default") == 0 || strcmp(arg, "any") == 0) {
176 dst->family = family;
177 dst->bytelen = 0;
178 dst->bitlen = 0;
179 return 0;
180 }
181
182 slash = strchr(arg, '/');
183 if (slash)
184 *slash = 0;
185 err = get_addr_1(dst, arg, family);
186 if (err == 0) {
187 switch (dst->family) {
188 case AF_INET6:
189 dst->bitlen = 128;
190 break;
191 default:
192 case AF_INET:
193 dst->bitlen = 32;
194 }
195 if (slash) {
196 if (get_integer(&plen, slash + 1, 0) || plen > dst->bitlen) {
197 err = -1;
198 goto done;
199 }
200 dst->bitlen = plen;
201 }
202 }
203 done:
204 if (slash)
205 *slash = '/';
206 return err;
207}
208
209int get_addr(inet_prefix * dst, char *arg, int family)
210{
211 if (family == AF_PACKET) {
212 bb_error_msg_and_die("\"%s\" may be inet address, but it is not allowed in this context.", arg);
213 }
214 if (get_addr_1(dst, arg, family)) {
215 bb_error_msg_and_die("an inet address is expected rather than \"%s\".", arg);
216 }
217 return 0;
218}
219
220int get_prefix(inet_prefix * dst, char *arg, int family)
221{
222 if (family == AF_PACKET) {
223 bb_error_msg_and_die("\"%s\" may be inet address, but it is not allowed in this context.", arg);
224 }
225 if (get_prefix_1(dst, arg, family)) {
226 bb_error_msg_and_die("an inet address is expected rather than \"%s\".", arg);
227 }
228 return 0;
229}
230
231__u32 get_addr32(char *name)
232{
233 inet_prefix addr;
234
235 if (get_addr_1(&addr, name, AF_INET)) {
236 bb_error_msg_and_die("an IP address is expected rather than \"%s\"", name);
237 }
238 return addr.data[0];
239}
240
241void incomplete_command()
242{
243 bb_error_msg("Command line is not complete. Try option \"help\"");
244 exit(-1);
245}
246
247void invarg(char *msg, char *arg)
248{
249 bb_error_msg("argument \"%s\" is wrong: %s", arg, msg);
250 exit(-1);
251}
252
253void duparg(char *key, char *arg)
254{
255 bb_error_msg("duplicate \"%s\": \"%s\" is the second value.", key, arg);
256 exit(-1);
257}
258
259void duparg2(char *key, char *arg)
260{
261 bb_error_msg("either \"%s\" is duplicate, or \"%s\" is a garbage.", key, arg);
262 exit(-1);
263}
264
265int matches(char *cmd, char *pattern)
266{
267 int len = strlen(cmd);
268
269 if (len > strlen(pattern)) {
270 return -1;
271 }
272 return memcmp(pattern, cmd, len);
273}
274
275int inet_addr_match(inet_prefix * a, inet_prefix * b, int bits)
276{
277 __u32 *a1 = a->data;
278 __u32 *a2 = b->data;
279 int words = bits >> 0x05;
280
281 bits &= 0x1f;
282
283 if (words)
284 if (memcmp(a1, a2, words << 2))
285 return -1;
286
287 if (bits) {
288 __u32 w1, w2;
289 __u32 mask;
290
291 w1 = a1[words];
292 w2 = a2[words];
293
294 mask = htonl((0xffffffff) << (0x20 - bits));
295
296 if ((w1 ^ w2) & mask)
297 return 1;
298 }
299
300 return 0;
301}
302
303int __iproute2_hz_internal;
304
305int __get_hz(void)
306{
307 int hz = 0;
308 FILE *fp = fopen("/proc/net/psched", "r");
309
310 if (fp) {
311 unsigned nom, denom;
312
313 if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2)
314 if (nom == 1000000)
315 hz = denom;
316 fclose(fp);
317 }
318 if (hz)
319 return hz;
320 return sysconf(_SC_CLK_TCK);
321}
322
323const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen)
324{
325 switch (af) {
326 case AF_INET:
327 case AF_INET6:
328 return inet_ntop(af, addr, buf, buflen);
329 default:
330 return "???";
331 }
332}
333
334
335const char *format_host(int af, int len, void *addr, char *buf, int buflen)
336{
337#ifdef RESOLVE_HOSTNAMES
338 if (resolve_hosts) {
339 struct hostent *h_ent;
340
341 if (len <= 0) {
342 switch (af) {
343 case AF_INET:
344 len = 4;
345 break;
346 case AF_INET6:
347 len = 16;
348 break;
349 default:;
350 }
351 }
352 if (len > 0 && (h_ent = gethostbyaddr(addr, len, af)) != NULL) {
353 snprintf(buf, buflen - 1, "%s", h_ent->h_name);
354 return buf;
355 }
356 }
357#endif
358 return rt_addr_n2a(af, len, addr, buf, buflen);
359}
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 @@
1#ifndef __UTILS_H__
2#define __UTILS_H__ 1
3
4#include <asm/types.h>
5#include <resolv.h>
6
7#include "libnetlink.h"
8#include "ll_map.h"
9#include "rtm_map.h"
10
11extern int preferred_family;
12extern int show_stats;
13extern int show_details;
14extern int show_raw;
15extern int resolve_hosts;
16extern int oneline;
17extern char * _SL_;
18
19#ifndef IPPROTO_ESP
20#define IPPROTO_ESP 50
21#endif
22#ifndef IPPROTO_AH
23#define IPPROTO_AH 51
24#endif
25
26#define SPRINT_BSIZE 64
27#define SPRINT_BUF(x) char x[SPRINT_BSIZE]
28
29extern void incomplete_command(void) __attribute__((noreturn));
30
31#define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0)
32
33typedef struct
34{
35 __u8 family;
36 __u8 bytelen;
37 __s16 bitlen;
38 __u32 data[4];
39} inet_prefix;
40
41#define DN_MAXADDL 20
42#ifndef AF_DECnet
43#define AF_DECnet 12
44#endif
45
46struct dn_naddr
47{
48 unsigned short a_len;
49 unsigned char a_addr[DN_MAXADDL];
50};
51
52#define IPX_NODE_LEN 6
53
54struct ipx_addr {
55 uint32_t ipx_net;
56 uint8_t ipx_node[IPX_NODE_LEN];
57};
58
59extern __u32 get_addr32(char *name);
60extern int get_addr_1(inet_prefix *dst, char *arg, int family);
61extern int get_prefix_1(inet_prefix *dst, char *arg, int family);
62extern int get_addr(inet_prefix *dst, char *arg, int family);
63extern int get_prefix(inet_prefix *dst, char *arg, int family);
64
65extern int get_integer(int *val, char *arg, int base);
66extern int get_unsigned(unsigned *val, char *arg, int base);
67#define get_byte get_u8
68#define get_ushort get_u16
69#define get_short get_s16
70extern int get_u32(__u32 *val, char *arg, int base);
71extern int get_u16(__u16 *val, char *arg, int base);
72extern int get_s16(__s16 *val, char *arg, int base);
73extern int get_u8(__u8 *val, char *arg, int base);
74extern int get_s8(__s8 *val, char *arg, int base);
75
76extern const char *format_host(int af, int len, void *addr, char *buf, int buflen);
77extern const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen);
78
79void invarg(char *, char *) __attribute__((noreturn));
80void duparg(char *, char *) __attribute__((noreturn));
81void duparg2(char *, char *) __attribute__((noreturn));
82int matches(char *arg, char *pattern);
83extern int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits);
84
85const char *dnet_ntop(int af, const void *addr, char *str, size_t len);
86int dnet_pton(int af, const char *src, void *addr);
87
88const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
89int ipx_pton(int af, const char *src, void *addr);
90
91extern int __iproute2_hz_internal;
92extern int __get_hz(void);
93
94static __inline__ int get_hz(void)
95{
96 if (__iproute2_hz_internal == 0)
97 __iproute2_hz_internal = __get_hz();
98 return __iproute2_hz_internal;
99}
100
101#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 @@
1/*
2 * nameif.c - Naming Interfaces based on MAC address for busybox.
3 *
4 * Written 2000 by Andi Kleen.
5 * Busybox port 2002 by Nick Fedchik <nick@fedchik.org.ua>
6 * Glenn McGrath <bug1@iinet.net.au>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 * 02111-1307 USA
22 *
23 */
24
25
26#include <sys/syslog.h>
27#include <sys/socket.h>
28#include <sys/ioctl.h>
29#include <errno.h>
30#include <getopt.h>
31#include <stdlib.h>
32#include <string.h>
33#include <net/if.h>
34#include <netinet/ether.h>
35
36#include "busybox.h"
37
38/* Older versions of net/if.h do not appear to define IF_NAMESIZE. */
39#ifndef IF_NAMESIZE
40# ifdef IFNAMSIZ
41# define IF_NAMESIZE IFNAMSIZ
42# else
43# define IF_NAMESIZE 16
44# endif
45#endif
46
47/* take from linux/sockios.h */
48#define SIOCSIFNAME 0x8923 /* set interface name */
49
50/* Octets in one Ethernet addr, from <linux/if_ether.h> */
51#define ETH_ALEN 6
52
53#ifndef ifr_newname
54#define ifr_newname ifr_ifru.ifru_slave
55#endif
56
57typedef struct mactable_s {
58 struct mactable_s *next;
59 struct mactable_s *prev;
60 char *ifname;
61 struct ether_addr *mac;
62} mactable_t;
63
64static unsigned char use_syslog;
65
66static void serror(const char *s, ...) __attribute__ ((noreturn));
67
68static void serror(const char *s, ...)
69{
70 va_list ap;
71
72 va_start(ap, s);
73
74 if (use_syslog) {
75 openlog(bb_applet_name, 0, LOG_LOCAL0);
76 vsyslog(LOG_ERR, s, ap);
77 closelog();
78 } else {
79 bb_verror_msg(s, ap);
80 putc('\n', stderr);
81 }
82
83 va_end(ap);
84
85 exit(EXIT_FAILURE);
86}
87
88/* Check ascii str_macaddr, convert and copy to *mac */
89struct ether_addr *cc_macaddr(char *str_macaddr)
90{
91 struct ether_addr *lmac, *mac;
92
93 lmac = ether_aton(str_macaddr);
94 if (lmac == NULL)
95 serror("cannot parse MAC %s", str_macaddr);
96 mac = xmalloc(ETH_ALEN);
97 memcpy(mac, lmac, ETH_ALEN);
98
99 return mac;
100}
101
102int nameif_main(int argc, char **argv)
103{
104 mactable_t *clist = NULL;
105 FILE *ifh;
106 const char *fname = "/etc/mactab";
107 char *line;
108 int ctl_sk;
109 int opt;
110 int if_index = 1;
111 mactable_t *ch;
112
113
114 while ((opt = getopt(argc, argv, "c:s")) != -1) {
115 switch (opt) {
116 case 'c':
117 fname = optarg;
118 break;
119 case 's':
120 use_syslog = 1;
121 break;
122 default:
123 bb_show_usage();
124 }
125 }
126
127 if ((argc - optind) & 1)
128 bb_show_usage();
129
130 if (optind < argc) {
131 char **a = argv + optind;
132
133 while (*a) {
134
135 if (strlen(*a) > IF_NAMESIZE)
136 serror("interface name `%s' too long", *a);
137 ch = xcalloc(1, sizeof(mactable_t));
138 ch->ifname = bb_xstrdup(*a++);
139 ch->mac = cc_macaddr(*a++);
140 if (clist)
141 clist->prev = ch;
142 ch->next = clist;
143 clist = ch;
144 }
145 } else {
146 ifh = bb_xfopen(fname, "r");
147
148 while ((line = bb_get_line_from_file(ifh)) != NULL) {
149 char *line_ptr;
150 size_t name_length;
151
152 line_ptr = line + strspn(line, " \t");
153 if ((line_ptr[0] == '#') || (line_ptr[0] == '\n'))
154 continue;
155 name_length = strcspn(line_ptr, " \t");
156 ch = xcalloc(1, sizeof(mactable_t));
157 ch->ifname = bb_xstrndup(line_ptr, name_length);
158 if (name_length > IF_NAMESIZE)
159 serror("interface name `%s' too long", ch->ifname);
160 line_ptr += name_length;
161 line_ptr += strspn(line_ptr, " \t");
162 name_length = strspn(line_ptr, "0123456789ABCDEFabcdef:");
163 line_ptr[name_length] = '\0';
164 ch->mac = cc_macaddr(line_ptr);
165 if (clist)
166 clist->prev = ch;
167 ch->next = clist;
168 clist = ch;
169 free(line);
170 }
171 fclose(ifh);
172 }
173
174 if ((ctl_sk = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
175 serror("socket: %m");
176
177 while (clist) {
178 struct ifreq ifr;
179
180 bzero(&ifr, sizeof(struct ifreq));
181 if_index++;
182 ifr.ifr_ifindex = if_index;
183
184 /* Get ifname by index or die */
185 if (ioctl(ctl_sk, SIOCGIFNAME, &ifr))
186 break;
187
188 /* Has this device hwaddr? */
189 if (ioctl(ctl_sk, SIOCGIFHWADDR, &ifr))
190 continue;
191
192 /* Search for mac like in ifr.ifr_hwaddr.sa_data */
193 for (ch = clist; ch; ch = ch->next)
194 if (!memcmp(ch->mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN))
195 break;
196
197 /* Nothing found for current ifr.ifr_hwaddr.sa_data */
198 if (ch == NULL)
199 continue;
200
201 strcpy(ifr.ifr_newname, ch->ifname);
202 if (ioctl(ctl_sk, SIOCSIFNAME, &ifr) < 0)
203 serror("cannot change ifname %s to %s: %m",
204 ifr.ifr_name, ch->ifname);
205
206 /* Remove list entry of renamed interface */
207 if (ch->prev != NULL) {
208 (ch->prev)->next = ch->next;
209 } else {
210 clist = ch->next;
211 }
212 if (ch->next != NULL)
213 (ch->next)->prev = ch->prev;
214#ifdef CONFIG_FEATURE_CLEAN_UP
215 free(ch->ifname);
216 free(ch->mac);
217 free(ch);
218#endif
219 }
220
221 return 0;
222}
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 @@
1/* vi: set sw=4 ts=4: */
2/* nc: mini-netcat - built from the ground up for LRP
3 Copyright (C) 1998 Charles P. Wright
4
5 0.0.1 6K It works.
6 0.0.2 5K Smaller and you can also check the exit condition if you wish.
7 0.0.3 Uses select()
8
9 19980918 Busy Boxed! Dave Cinege
10 19990512 Uses Select. Charles P. Wright
11 19990513 Fixes stdin stupidity and uses buffers. Charles P. Wright
12
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26
27*/
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33
34#include <sys/types.h>
35#include <sys/socket.h>
36#include <netinet/in.h>
37#include <arpa/inet.h>
38#include <netdb.h>
39#include <sys/time.h>
40#include <sys/ioctl.h>
41#include "busybox.h"
42
43#define GAPING_SECURITY_HOLE
44
45int nc_main(int argc, char **argv)
46{
47 int do_listen = 0, lport = 0, delay = 0, tmpfd, opt, sfd, x;
48 char buf[BUFSIZ];
49#ifdef GAPING_SECURITY_HOLE
50 char * pr00gie = NULL;
51#endif
52
53 struct sockaddr_in address;
54 struct hostent *hostinfo;
55
56 fd_set readfds, testfds;
57
58 while ((opt = getopt(argc, argv, "lp:i:e:")) > 0) {
59 switch (opt) {
60 case 'l':
61 do_listen++;
62 break;
63 case 'p':
64 lport = bb_lookup_port(optarg, "tcp", 0);
65 break;
66 case 'i':
67 delay = atoi(optarg);
68 break;
69#ifdef GAPING_SECURITY_HOLE
70 case 'e':
71 pr00gie = optarg;
72 break;
73#endif
74 default:
75 bb_show_usage();
76 }
77 }
78
79#ifdef GAPING_SECURITY_HOLE
80 if (pr00gie) {
81 /* won't need stdin */
82 close (STDIN_FILENO);
83 }
84#endif /* GAPING_SECURITY_HOLE */
85
86
87 if ((do_listen && optind != argc) || (!do_listen && optind + 2 != argc))
88 bb_show_usage();
89
90 if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
91 bb_perror_msg_and_die("socket");
92 x = 1;
93 if (setsockopt (sfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x)) == -1)
94 bb_perror_msg_and_die ("reuseaddr failed");
95 address.sin_family = AF_INET;
96
97 if (lport != 0) {
98 memset(&address.sin_addr, 0, sizeof(address.sin_addr));
99 address.sin_port = lport;
100
101 if (bind(sfd, (struct sockaddr *) &address, sizeof(address)) < 0)
102 bb_perror_msg_and_die("bind");
103 }
104
105 if (do_listen) {
106 socklen_t addrlen = sizeof(address);
107
108 if (listen(sfd, 1) < 0)
109 bb_perror_msg_and_die("listen");
110
111 if ((tmpfd = accept(sfd, (struct sockaddr *) &address, &addrlen)) < 0)
112 bb_perror_msg_and_die("accept");
113
114 close(sfd);
115 sfd = tmpfd;
116 } else {
117 hostinfo = xgethostbyname(argv[optind]);
118
119 address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
120 address.sin_port = bb_lookup_port(argv[optind+1], "tcp", 0);
121
122 if (connect(sfd, (struct sockaddr *) &address, sizeof(address)) < 0)
123 bb_perror_msg_and_die("connect");
124 }
125
126#ifdef GAPING_SECURITY_HOLE
127 /* -e given? */
128 if (pr00gie) {
129 dup2(sfd, 0);
130 close(sfd);
131 dup2 (0, 1);
132 dup2 (0, 2);
133 execl (pr00gie, pr00gie, NULL);
134 /* Don't print stuff or it will go over the wire.... */
135 _exit(-1);
136 }
137#endif /* GAPING_SECURITY_HOLE */
138
139
140 FD_ZERO(&readfds);
141 FD_SET(sfd, &readfds);
142 FD_SET(STDIN_FILENO, &readfds);
143
144 while (1) {
145 int fd;
146 int ofd;
147 int nread;
148
149 testfds = readfds;
150
151 if (select(FD_SETSIZE, &testfds, NULL, NULL, NULL) < 0)
152 bb_perror_msg_and_die("select");
153
154 for (fd = 0; fd < FD_SETSIZE; fd++) {
155 if (FD_ISSET(fd, &testfds)) {
156 if ((nread = safe_read(fd, buf, sizeof(buf))) < 0)
157 bb_perror_msg_and_die("read");
158
159 if (fd == sfd) {
160 if (nread == 0)
161 exit(0);
162 ofd = STDOUT_FILENO;
163 } else {
164 if (nread == 0)
165 shutdown(sfd, 1);
166 ofd = sfd;
167 }
168
169 if (bb_full_write(ofd, buf, nread) < 0)
170 bb_perror_msg_and_die("write");
171 if (delay > 0) {
172 sleep(delay);
173 }
174 }
175 }
176 }
177}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini netstat implementation(s) for busybox
4 * based in part on the netstat implementation from net-tools.
5 *
6 * Copyright (C) 2002 by Bart Visscher <magick@linux-fan.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * 2002-04-20
23 * IPV6 support added by Bart Visscher <magick@linux-fan.com>
24 */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <stdarg.h>
30#include <signal.h>
31#include <errno.h>
32#include <sys/stat.h>
33#include <dirent.h>
34#include <unistd.h>
35#include "inet_common.h"
36#include "busybox.h"
37#include "pwd_.h"
38
39#ifdef CONFIG_ROUTE
40extern void displayroutes(int noresolve, int netstatfmt);
41#endif
42
43#define NETSTAT_CONNECTED 0x01
44#define NETSTAT_LISTENING 0x02
45#define NETSTAT_NUMERIC 0x04
46#define NETSTAT_TCP 0x10
47#define NETSTAT_UDP 0x20
48#define NETSTAT_RAW 0x40
49#define NETSTAT_UNIX 0x80
50
51static int flags = NETSTAT_CONNECTED |
52 NETSTAT_TCP | NETSTAT_UDP | NETSTAT_RAW | NETSTAT_UNIX;
53
54#define PROGNAME_WIDTHs PROGNAME_WIDTH1(PROGNAME_WIDTH)
55#define PROGNAME_WIDTH1(s) PROGNAME_WIDTH2(s)
56#define PROGNAME_WIDTH2(s) #s
57
58#define PRG_HASH_SIZE 211
59
60enum {
61 TCP_ESTABLISHED = 1,
62 TCP_SYN_SENT,
63 TCP_SYN_RECV,
64 TCP_FIN_WAIT1,
65 TCP_FIN_WAIT2,
66 TCP_TIME_WAIT,
67 TCP_CLOSE,
68 TCP_CLOSE_WAIT,
69 TCP_LAST_ACK,
70 TCP_LISTEN,
71 TCP_CLOSING /* now a valid state */
72};
73
74static const char * const tcp_state[] =
75{
76 "",
77 "ESTABLISHED",
78 "SYN_SENT",
79 "SYN_RECV",
80 "FIN_WAIT1",
81 "FIN_WAIT2",
82 "TIME_WAIT",
83 "CLOSE",
84 "CLOSE_WAIT",
85 "LAST_ACK",
86 "LISTEN",
87 "CLOSING"
88};
89
90typedef enum {
91 SS_FREE = 0, /* not allocated */
92 SS_UNCONNECTED, /* unconnected to any socket */
93 SS_CONNECTING, /* in process of connecting */
94 SS_CONNECTED, /* connected to socket */
95 SS_DISCONNECTING /* in process of disconnecting */
96} socket_state;
97
98#define SO_ACCEPTCON (1<<16) /* performed a listen */
99#define SO_WAITDATA (1<<17) /* wait data to read */
100#define SO_NOSPACE (1<<18) /* no space to write */
101
102static char *itoa(unsigned int i)
103{
104 /* 21 digits plus null terminator, good for 64-bit or smaller ints */
105 static char local[22];
106 char *p = &local[21];
107 *p-- = '\0';
108 do {
109 *p-- = '0' + i % 10;
110 i /= 10;
111 } while (i > 0);
112 return p + 1;
113}
114
115static char *get_sname(int port, const char *proto, int num)
116{
117 char *str=itoa(ntohs(port));
118 if (num) {
119 } else {
120 struct servent *se=getservbyport(port,proto);
121 if (se)
122 str=se->s_name;
123 }
124 if (!port) {
125 str="*";
126 }
127 return str;
128}
129
130static void snprint_ip_port(char *ip_port, int size, struct sockaddr *addr, int port, char *proto, int numeric)
131{
132 char *port_name;
133
134#ifdef CONFIG_FEATURE_IPV6
135 if (addr->sa_family == AF_INET6) {
136 INET6_rresolve(ip_port, size, (struct sockaddr_in6 *)addr,
137 (numeric&NETSTAT_NUMERIC) ? 0x0fff : 0);
138 } else
139#endif
140 {
141 INET_rresolve(ip_port, size, (struct sockaddr_in *)addr,
142 0x4000 | ((numeric&NETSTAT_NUMERIC) ? 0x0fff : 0),
143 0xffffffff);
144 }
145 port_name=get_sname(htons(port), proto, numeric);
146 if ((strlen(ip_port) + strlen(port_name)) > 22)
147 ip_port[22 - strlen(port_name)] = '\0';
148 ip_port+=strlen(ip_port);
149 strcat(ip_port, ":");
150 strcat(ip_port, port_name);
151}
152
153static void tcp_do_one(int lnr, const char *line)
154{
155 char local_addr[64], rem_addr[64];
156 const char *state_str;
157 char more[512];
158 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
159#ifdef CONFIG_FEATURE_IPV6
160 struct sockaddr_in6 localaddr, remaddr;
161 char addr6[INET6_ADDRSTRLEN];
162 struct in6_addr in6;
163#else
164 struct sockaddr_in localaddr, remaddr;
165#endif
166 unsigned long rxq, txq, time_len, retr, inode;
167
168 if (lnr == 0)
169 return;
170
171 more[0] = '\0';
172 num = sscanf(line,
173 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
174 &d, local_addr, &local_port,
175 rem_addr, &rem_port, &state,
176 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
177
178 if (strlen(local_addr) > 8) {
179#ifdef CONFIG_FEATURE_IPV6
180 sscanf(local_addr, "%08X%08X%08X%08X",
181 &in6.s6_addr32[0], &in6.s6_addr32[1],
182 &in6.s6_addr32[2], &in6.s6_addr32[3]);
183 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
184 inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
185 sscanf(rem_addr, "%08X%08X%08X%08X",
186 &in6.s6_addr32[0], &in6.s6_addr32[1],
187 &in6.s6_addr32[2], &in6.s6_addr32[3]);
188 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
189 inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
190 localaddr.sin6_family = AF_INET6;
191 remaddr.sin6_family = AF_INET6;
192#endif
193 } else {
194 sscanf(local_addr, "%X",
195 &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
196 sscanf(rem_addr, "%X",
197 &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
198 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
199 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
200 }
201
202 if (num < 10) {
203 bb_error_msg("warning, got bogus tcp line.");
204 return;
205 }
206 state_str = tcp_state[state];
207 if ((rem_port && (flags&NETSTAT_CONNECTED)) ||
208 (!rem_port && (flags&NETSTAT_LISTENING)))
209 {
210 snprint_ip_port(local_addr, sizeof(local_addr),
211 (struct sockaddr *) &localaddr, local_port,
212 "tcp", flags&NETSTAT_NUMERIC);
213
214 snprint_ip_port(rem_addr, sizeof(rem_addr),
215 (struct sockaddr *) &remaddr, rem_port,
216 "tcp", flags&NETSTAT_NUMERIC);
217
218 printf("tcp %6ld %6ld %-23s %-23s %-12s\n",
219 rxq, txq, local_addr, rem_addr, state_str);
220
221 }
222}
223
224static void udp_do_one(int lnr, const char *line)
225{
226 char local_addr[64], rem_addr[64];
227 char *state_str, more[512];
228 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
229#ifdef CONFIG_FEATURE_IPV6
230 struct sockaddr_in6 localaddr, remaddr;
231 char addr6[INET6_ADDRSTRLEN];
232 struct in6_addr in6;
233#else
234 struct sockaddr_in localaddr, remaddr;
235#endif
236 unsigned long rxq, txq, time_len, retr, inode;
237
238 if (lnr == 0)
239 return;
240
241 more[0] = '\0';
242 num = sscanf(line,
243 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
244 &d, local_addr, &local_port,
245 rem_addr, &rem_port, &state,
246 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
247
248 if (strlen(local_addr) > 8) {
249#ifdef CONFIG_FEATURE_IPV6
250 /* Demangle what the kernel gives us */
251 sscanf(local_addr, "%08X%08X%08X%08X",
252 &in6.s6_addr32[0], &in6.s6_addr32[1],
253 &in6.s6_addr32[2], &in6.s6_addr32[3]);
254 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
255 inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
256 sscanf(rem_addr, "%08X%08X%08X%08X",
257 &in6.s6_addr32[0], &in6.s6_addr32[1],
258 &in6.s6_addr32[2], &in6.s6_addr32[3]);
259 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
260 inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
261 localaddr.sin6_family = AF_INET6;
262 remaddr.sin6_family = AF_INET6;
263#endif
264 } else {
265 sscanf(local_addr, "%X",
266 &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
267 sscanf(rem_addr, "%X",
268 &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
269 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
270 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
271 }
272
273 if (num < 10) {
274 bb_error_msg("warning, got bogus udp line.");
275 return;
276 }
277 switch (state) {
278 case TCP_ESTABLISHED:
279 state_str = "ESTABLISHED";
280 break;
281
282 case TCP_CLOSE:
283 state_str = "";
284 break;
285
286 default:
287 state_str = "UNKNOWN";
288 break;
289 }
290
291#ifdef CONFIG_FEATURE_IPV6
292# define notnull(A) (((A.sin6_family == AF_INET6) && \
293 ((A.sin6_addr.s6_addr32[0]) || \
294 (A.sin6_addr.s6_addr32[1]) || \
295 (A.sin6_addr.s6_addr32[2]) || \
296 (A.sin6_addr.s6_addr32[3]))) || \
297 ((A.sin6_family == AF_INET) && \
298 ((struct sockaddr_in *) &A)->sin_addr.s_addr))
299#else
300# define notnull(A) (A.sin_addr.s_addr)
301#endif
302 if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
303 (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
304 {
305 snprint_ip_port(local_addr, sizeof(local_addr),
306 (struct sockaddr *) &localaddr, local_port,
307 "udp", flags&NETSTAT_NUMERIC);
308
309 snprint_ip_port(rem_addr, sizeof(rem_addr),
310 (struct sockaddr *) &remaddr, rem_port,
311 "udp", flags&NETSTAT_NUMERIC);
312
313 printf("udp %6ld %6ld %-23s %-23s %-12s\n",
314 rxq, txq, local_addr, rem_addr, state_str);
315
316 }
317}
318
319static void raw_do_one(int lnr, const char *line)
320{
321 char local_addr[64], rem_addr[64];
322 char *state_str, more[512];
323 int num, local_port, rem_port, d, state, timer_run, uid, timeout;
324#ifdef CONFIG_FEATURE_IPV6
325 struct sockaddr_in6 localaddr, remaddr;
326 char addr6[INET6_ADDRSTRLEN];
327 struct in6_addr in6;
328#else
329 struct sockaddr_in localaddr, remaddr;
330#endif
331 unsigned long rxq, txq, time_len, retr, inode;
332
333 if (lnr == 0)
334 return;
335
336 more[0] = '\0';
337 num = sscanf(line,
338 "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %ld %512s\n",
339 &d, local_addr, &local_port,
340 rem_addr, &rem_port, &state,
341 &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode, more);
342
343 if (strlen(local_addr) > 8) {
344#ifdef CONFIG_FEATURE_IPV6
345 sscanf(local_addr, "%08X%08X%08X%08X",
346 &in6.s6_addr32[0], &in6.s6_addr32[1],
347 &in6.s6_addr32[2], &in6.s6_addr32[3]);
348 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
349 inet_pton(AF_INET6, addr6, (struct sockaddr *) &localaddr.sin6_addr);
350 sscanf(rem_addr, "%08X%08X%08X%08X",
351 &in6.s6_addr32[0], &in6.s6_addr32[1],
352 &in6.s6_addr32[2], &in6.s6_addr32[3]);
353 inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
354 inet_pton(AF_INET6, addr6, (struct sockaddr *) &remaddr.sin6_addr);
355 localaddr.sin6_family = AF_INET6;
356 remaddr.sin6_family = AF_INET6;
357#endif
358 } else {
359 sscanf(local_addr, "%X",
360 &((struct sockaddr_in *) &localaddr)->sin_addr.s_addr);
361 sscanf(rem_addr, "%X",
362 &((struct sockaddr_in *) &remaddr)->sin_addr.s_addr);
363 ((struct sockaddr *) &localaddr)->sa_family = AF_INET;
364 ((struct sockaddr *) &remaddr)->sa_family = AF_INET;
365 }
366
367 if (num < 10) {
368 bb_error_msg("warning, got bogus raw line.");
369 return;
370 }
371 state_str=itoa(state);
372
373#ifdef CONFIG_FEATURE_IPV6
374# define notnull(A) (((A.sin6_family == AF_INET6) && \
375 ((A.sin6_addr.s6_addr32[0]) || \
376 (A.sin6_addr.s6_addr32[1]) || \
377 (A.sin6_addr.s6_addr32[2]) || \
378 (A.sin6_addr.s6_addr32[3]))) || \
379 ((A.sin6_family == AF_INET) && \
380 ((struct sockaddr_in *) &A)->sin_addr.s_addr))
381#else
382# define notnull(A) (A.sin_addr.s_addr)
383#endif
384 if ((notnull(remaddr) && (flags&NETSTAT_CONNECTED)) ||
385 (!notnull(remaddr) && (flags&NETSTAT_LISTENING)))
386 {
387 snprint_ip_port(local_addr, sizeof(local_addr),
388 (struct sockaddr *) &localaddr, local_port,
389 "raw", flags&NETSTAT_NUMERIC);
390
391 snprint_ip_port(rem_addr, sizeof(rem_addr),
392 (struct sockaddr *) &remaddr, rem_port,
393 "raw", flags&NETSTAT_NUMERIC);
394
395 printf("raw %6ld %6ld %-23s %-23s %-12s\n",
396 rxq, txq, local_addr, rem_addr, state_str);
397
398 }
399}
400
401#define HAS_INODE 1
402
403static void unix_do_one(int nr, const char *line)
404{
405 static int has = 0;
406 char path[PATH_MAX], ss_flags[32];
407 char *ss_proto, *ss_state, *ss_type;
408 int num, state, type, inode;
409 void *d;
410 unsigned long refcnt, proto, unix_flags;
411
412 if (nr == 0) {
413 if (strstr(line, "Inode"))
414 has |= HAS_INODE;
415 return;
416 }
417 path[0] = '\0';
418 num = sscanf(line, "%p: %lX %lX %lX %X %X %d %s",
419 &d, &refcnt, &proto, &unix_flags, &type, &state, &inode, path);
420 if (num < 6) {
421 bb_error_msg("warning, got bogus unix line.");
422 return;
423 }
424 if (!(has & HAS_INODE))
425 snprintf(path,sizeof(path),"%d",inode);
426
427 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))!=(NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
428 if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) {
429 if (!(flags&NETSTAT_LISTENING))
430 return;
431 } else {
432 if (!(flags&NETSTAT_CONNECTED))
433 return;
434 }
435 }
436
437 switch (proto) {
438 case 0:
439 ss_proto = "unix";
440 break;
441
442 default:
443 ss_proto = "??";
444 }
445
446 switch (type) {
447 case SOCK_STREAM:
448 ss_type = "STREAM";
449 break;
450
451 case SOCK_DGRAM:
452 ss_type = "DGRAM";
453 break;
454
455 case SOCK_RAW:
456 ss_type = "RAW";
457 break;
458
459 case SOCK_RDM:
460 ss_type = "RDM";
461 break;
462
463 case SOCK_SEQPACKET:
464 ss_type = "SEQPACKET";
465 break;
466
467 default:
468 ss_type = "UNKNOWN";
469 }
470
471 switch (state) {
472 case SS_FREE:
473 ss_state = "FREE";
474 break;
475
476 case SS_UNCONNECTED:
477 /*
478 * Unconnected sockets may be listening
479 * for something.
480 */
481 if (unix_flags & SO_ACCEPTCON) {
482 ss_state = "LISTENING";
483 } else {
484 ss_state = "";
485 }
486 break;
487
488 case SS_CONNECTING:
489 ss_state = "CONNECTING";
490 break;
491
492 case SS_CONNECTED:
493 ss_state = "CONNECTED";
494 break;
495
496 case SS_DISCONNECTING:
497 ss_state = "DISCONNECTING";
498 break;
499
500 default:
501 ss_state = "UNKNOWN";
502 }
503
504 strcpy(ss_flags, "[ ");
505 if (unix_flags & SO_ACCEPTCON)
506 strcat(ss_flags, "ACC ");
507 if (unix_flags & SO_WAITDATA)
508 strcat(ss_flags, "W ");
509 if (unix_flags & SO_NOSPACE)
510 strcat(ss_flags, "N ");
511
512 strcat(ss_flags, "]");
513
514 printf("%-5s %-6ld %-11s %-10s %-13s ",
515 ss_proto, refcnt, ss_flags, ss_type, ss_state);
516 if (has & HAS_INODE)
517 printf("%-6d ",inode);
518 else
519 printf("- ");
520 puts(path);
521}
522
523#define _PATH_PROCNET_UDP "/proc/net/udp"
524#define _PATH_PROCNET_UDP6 "/proc/net/udp6"
525#define _PATH_PROCNET_TCP "/proc/net/tcp"
526#define _PATH_PROCNET_TCP6 "/proc/net/tcp6"
527#define _PATH_PROCNET_RAW "/proc/net/raw"
528#define _PATH_PROCNET_RAW6 "/proc/net/raw6"
529#define _PATH_PROCNET_UNIX "/proc/net/unix"
530
531static void do_info(const char *file, const char *name, void (*proc)(int, const char *))
532{
533 char buffer[8192];
534 int lnr = 0;
535 FILE *procinfo;
536
537 procinfo = fopen(file, "r");
538 if (procinfo == NULL) {
539 if (errno != ENOENT) {
540 perror(file);
541 } else {
542 bb_error_msg("no support for `%s' on this system.", name);
543 }
544 } else {
545 do {
546 if (fgets(buffer, sizeof(buffer), procinfo))
547 (proc)(lnr++, buffer);
548 } while (!feof(procinfo));
549 fclose(procinfo);
550 }
551}
552
553/*
554 * Our main function.
555 */
556
557int netstat_main(int argc, char **argv)
558{
559 int opt;
560 int new_flags=0;
561 int showroute = 0, extended = 0;
562#ifdef CONFIG_FEATURE_IPV6
563 int inet=1;
564 int inet6=1;
565#else
566# define inet 1
567# define inet6 0
568#endif
569 while ((opt = getopt(argc, argv, "laenrtuwx")) != -1)
570 switch (opt) {
571 case 'l':
572 flags &= ~NETSTAT_CONNECTED;
573 flags |= NETSTAT_LISTENING;
574 break;
575 case 'a':
576 flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED;
577 break;
578 case 'n':
579 flags |= NETSTAT_NUMERIC;
580 break;
581 case 'r':
582 showroute = 1;
583 break;
584 case 'e':
585 extended = 1;
586 break;
587 case 't':
588 new_flags |= NETSTAT_TCP;
589 break;
590 case 'u':
591 new_flags |= NETSTAT_UDP;
592 break;
593 case 'w':
594 new_flags |= NETSTAT_RAW;
595 break;
596 case 'x':
597 new_flags |= NETSTAT_UNIX;
598 break;
599 default:
600 bb_show_usage();
601 }
602 if ( showroute ) {
603#ifdef CONFIG_ROUTE
604 displayroutes ( flags & NETSTAT_NUMERIC, !extended );
605 return 0;
606#else
607 bb_error_msg_and_die( "-r (display routing table) is not compiled in." );
608#endif
609 }
610
611 if (new_flags) {
612 flags &= ~(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX);
613 flags |= new_flags;
614 }
615 if (flags&(NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
616 printf("Active Internet connections "); /* xxx */
617
618 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
619 printf("(servers and established)");
620 else {
621 if (flags&NETSTAT_LISTENING)
622 printf("(only servers)");
623 else
624 printf("(w/o servers)");
625 }
626 printf("\nProto Recv-Q Send-Q Local Address Foreign Address State \n");
627 }
628 if (inet && flags&NETSTAT_TCP)
629 do_info(_PATH_PROCNET_TCP,"AF INET (tcp)",tcp_do_one);
630#ifdef CONFIG_FEATURE_IPV6
631 if (inet6 && flags&NETSTAT_TCP)
632 do_info(_PATH_PROCNET_TCP6,"AF INET6 (tcp)",tcp_do_one);
633#endif
634 if (inet && flags&NETSTAT_UDP)
635 do_info(_PATH_PROCNET_UDP,"AF INET (udp)",udp_do_one);
636#ifdef CONFIG_FEATURE_IPV6
637 if (inet6 && flags&NETSTAT_UDP)
638 do_info(_PATH_PROCNET_UDP6,"AF INET6 (udp)",udp_do_one);
639#endif
640 if (inet && flags&NETSTAT_RAW)
641 do_info(_PATH_PROCNET_RAW,"AF INET (raw)",raw_do_one);
642#ifdef CONFIG_FEATURE_IPV6
643 if (inet6 && flags&NETSTAT_RAW)
644 do_info(_PATH_PROCNET_RAW6,"AF INET6 (raw)",raw_do_one);
645#endif
646 if (flags&NETSTAT_UNIX) {
647 printf("Active UNIX domain sockets ");
648 if ((flags&(NETSTAT_LISTENING|NETSTAT_CONNECTED))==(NETSTAT_LISTENING|NETSTAT_CONNECTED))
649 printf("(servers and established)");
650 else {
651 if (flags&NETSTAT_LISTENING)
652 printf("(only servers)");
653 else
654 printf("(w/o servers)");
655 }
656
657 printf("\nProto RefCnt Flags Type State I-Node Path\n");
658 do_info(_PATH_PROCNET_UNIX,"AF UNIX",unix_do_one);
659 }
660 return 0;
661}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini nslookup implementation for busybox
4 *
5 * Copyright (C) 1999,2000 by Lineo, inc. and John Beppu
6 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7 *
8 * Correct default name server display and explicit name server option
9 * added by Ben Zeckel <bzeckel@hmc.edu> June 2001
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27#include <ctype.h>
28#include <errno.h>
29#include <stdio.h>
30#include <string.h>
31#include <stdlib.h>
32
33#include <stdint.h>
34#include <netdb.h>
35#include <sys/socket.h>
36#include <sys/types.h>
37#include <netinet/in.h>
38#include <resolv.h>
39#include <arpa/inet.h>
40#include "busybox.h"
41
42/*
43 | I'm only implementing non-interactive mode;
44 | I totally forgot nslookup even had an interactive mode.
45 */
46
47/* only works for IPv4 */
48static int addr_fprint(char *addr)
49{
50 uint8_t split[4];
51 uint32_t ip;
52 uint32_t *x = (uint32_t *) addr;
53
54 ip = ntohl(*x);
55 split[0] = (ip & 0xff000000) >> 24;
56 split[1] = (ip & 0x00ff0000) >> 16;
57 split[2] = (ip & 0x0000ff00) >> 8;
58 split[3] = (ip & 0x000000ff);
59 printf("%d.%d.%d.%d", split[0], split[1], split[2], split[3]);
60 return 0;
61}
62
63/* takes the NULL-terminated array h_addr_list, and
64 * prints its contents appropriately
65 */
66static int addr_list_fprint(char **h_addr_list)
67{
68 int i, j;
69 char *addr_string = (h_addr_list[1])
70 ? "Addresses: " : "Address: ";
71
72 printf("%s ", addr_string);
73 for (i = 0, j = 0; h_addr_list[i]; i++, j++) {
74 addr_fprint(h_addr_list[i]);
75
76 /* real nslookup does this */
77 if (j == 4) {
78 if (h_addr_list[i + 1]) {
79 printf("\n ");
80 }
81 j = 0;
82 } else {
83 if (h_addr_list[i + 1]) {
84 printf(", ");
85 }
86 }
87
88 }
89 printf("\n");
90 return 0;
91}
92
93/* print the results as nslookup would */
94static struct hostent *hostent_fprint(struct hostent *host, const char *server_host)
95{
96 if (host) {
97 printf("%s %s\n", server_host, host->h_name);
98 addr_list_fprint(host->h_addr_list);
99 } else {
100 printf("*** Unknown host\n");
101 }
102 return host;
103}
104
105/* changes a c-string matching the perl regex \d+\.\d+\.\d+\.\d+
106 * into a uint32_t
107 */
108static uint32_t str_to_addr(const char *addr)
109{
110 uint32_t split[4];
111 uint32_t ip;
112
113 sscanf(addr, "%d.%d.%d.%d",
114 &split[0], &split[1], &split[2], &split[3]);
115
116 /* assuming sscanf worked */
117 ip = (split[0] << 24) |
118 (split[1] << 16) | (split[2] << 8) | (split[3]);
119
120 return htonl(ip);
121}
122
123/* gethostbyaddr wrapper */
124static struct hostent *gethostbyaddr_wrapper(const char *address)
125{
126 struct in_addr addr;
127
128 addr.s_addr = str_to_addr(address);
129 return gethostbyaddr((char *) &addr, 4, AF_INET); /* IPv4 only for now */
130}
131
132/* lookup the default nameserver and display it */
133static inline void server_print(void)
134{
135 struct sockaddr_in def = _res.nsaddr_list[0];
136 char *ip = inet_ntoa(def.sin_addr);
137
138 hostent_fprint(gethostbyaddr_wrapper(ip), "Server:");
139 printf("\n");
140}
141
142/* alter the global _res nameserver structure to use
143 an explicit dns server instead of what is in /etc/resolv.h */
144static inline void set_default_dns(char *server)
145{
146 struct in_addr server_in_addr;
147
148 if(inet_aton(server,&server_in_addr))
149 {
150 _res.nscount = 1;
151 _res.nsaddr_list[0].sin_addr = server_in_addr;
152 }
153}
154
155/* naive function to check whether char *s is an ip address */
156static int is_ip_address(const char *s)
157{
158 while (*s) {
159 if ((isdigit(*s)) || (*s == '.')) {
160 s++;
161 continue;
162 }
163 return 0;
164 }
165 return 1;
166}
167
168/* ________________________________________________________________________ */
169int nslookup_main(int argc, char **argv)
170{
171 struct hostent *host;
172
173 /*
174 * initialize DNS structure _res used in printing the default
175 * name server and in the explicit name server option feature.
176 */
177
178 res_init();
179
180 /*
181 * We allow 1 or 2 arguments.
182 * The first is the name to be looked up and the second is an
183 * optional DNS server with which to do the lookup.
184 * More than 3 arguments is an error to follow the pattern of the
185 * standard nslookup
186 */
187
188 if (argc < 2 || *argv[1]=='-' || argc > 3)
189 bb_show_usage();
190 else if(argc == 3)
191 set_default_dns(argv[2]);
192
193 server_print();
194 if (is_ip_address(argv[1])) {
195 host = gethostbyaddr_wrapper(argv[1]);
196 } else {
197 host = xgethostbyname(argv[1]);
198 }
199 hostent_fprint(host, "Name: ");
200 if (host) {
201 return EXIT_SUCCESS;
202 }
203 return EXIT_FAILURE;
204}
205
206/* $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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * $Id: ping.c,v 1.56 2004/03/15 08:28:48 andersen Exp $
4 * Mini ping implementation for busybox
5 *
6 * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * This version of ping is adapted from the ping in netkit-base 0.10,
23 * which is:
24 *
25 * Copyright (c) 1989 The Regents of the University of California.
26 * All rights reserved.
27 *
28 * This code is derived from software contributed to Berkeley by
29 * Mike Muuss.
30 *
31 * Original copyright notice is retained at the end of this file.
32 */
33
34#include <sys/param.h>
35#include <sys/socket.h>
36#include <sys/file.h>
37#include <sys/time.h>
38#include <sys/times.h>
39#include <sys/signal.h>
40
41#include <netinet/in.h>
42#include <netinet/ip.h>
43#include <netinet/ip_icmp.h>
44#include <arpa/inet.h>
45#include <netdb.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <errno.h>
49#include <unistd.h>
50#include <string.h>
51#include <stdlib.h>
52#include "busybox.h"
53
54
55static const int DEFDATALEN = 56;
56static const int MAXIPLEN = 60;
57static const int MAXICMPLEN = 76;
58static const int MAXPACKET = 65468;
59#define MAX_DUP_CHK (8 * 128)
60static const int MAXWAIT = 10;
61static const int PINGINTERVAL = 1; /* second */
62
63#define O_QUIET (1 << 0)
64
65#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
66#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
67#define SET(bit) (A(bit) |= B(bit))
68#define CLR(bit) (A(bit) &= (~B(bit)))
69#define TST(bit) (A(bit) & B(bit))
70
71static void ping(const char *host);
72
73/* common routines */
74static int in_cksum(unsigned short *buf, int sz)
75{
76 int nleft = sz;
77 int sum = 0;
78 unsigned short *w = buf;
79 unsigned short ans = 0;
80
81 while (nleft > 1) {
82 sum += *w++;
83 nleft -= 2;
84 }
85
86 if (nleft == 1) {
87 *(unsigned char *) (&ans) = *(unsigned char *) w;
88 sum += ans;
89 }
90
91 sum = (sum >> 16) + (sum & 0xFFFF);
92 sum += (sum >> 16);
93 ans = ~sum;
94 return (ans);
95}
96
97/* simple version */
98#ifndef CONFIG_FEATURE_FANCY_PING
99static char *hostname = NULL;
100static void noresp(int ign)
101{
102 printf("No response from %s\n", hostname);
103 exit(EXIT_FAILURE);
104}
105
106static void ping(const char *host)
107{
108 struct hostent *h;
109 struct sockaddr_in pingaddr;
110 struct icmp *pkt;
111 int pingsock, c;
112 char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
113
114 pingsock = create_icmp_socket();
115
116 memset(&pingaddr, 0, sizeof(struct sockaddr_in));
117
118 pingaddr.sin_family = AF_INET;
119 h = xgethostbyname(host);
120 memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr));
121 hostname = h->h_name;
122
123 pkt = (struct icmp *) packet;
124 memset(pkt, 0, sizeof(packet));
125 pkt->icmp_type = ICMP_ECHO;
126 pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
127
128 c = sendto(pingsock, packet, sizeof(packet), 0,
129 (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
130
131 if (c < 0 || c != sizeof(packet))
132 bb_perror_msg_and_die("sendto");
133
134 signal(SIGALRM, noresp);
135 alarm(5); /* give the host 5000ms to respond */
136 /* listen for replies */
137 while (1) {
138 struct sockaddr_in from;
139 size_t fromlen = sizeof(from);
140
141 if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
142 (struct sockaddr *) &from, &fromlen)) < 0) {
143 if (errno == EINTR)
144 continue;
145 bb_perror_msg("recvfrom");
146 continue;
147 }
148 if (c >= 76) { /* ip + icmp */
149 struct iphdr *iphdr = (struct iphdr *) packet;
150
151 pkt = (struct icmp *) (packet + (iphdr->ihl << 2)); /* skip ip hdr */
152 if (pkt->icmp_type == ICMP_ECHOREPLY)
153 break;
154 }
155 }
156 printf("%s is alive!\n", hostname);
157 return;
158}
159
160extern int ping_main(int argc, char **argv)
161{
162 argc--;
163 argv++;
164 if (argc < 1)
165 bb_show_usage();
166 ping(*argv);
167 return EXIT_SUCCESS;
168}
169
170#else /* ! CONFIG_FEATURE_FANCY_PING */
171/* full(er) version */
172static struct sockaddr_in pingaddr;
173static int pingsock = -1;
174static int datalen; /* intentionally uninitialized to work around gcc bug */
175
176static long ntransmitted, nreceived, nrepeats, pingcount;
177static int myid, options;
178static unsigned long tmin = ULONG_MAX, tmax, tsum;
179static char rcvd_tbl[MAX_DUP_CHK / 8];
180
181struct hostent *hostent;
182
183static void sendping(int);
184static void pingstats(int);
185static void unpack(char *, int, struct sockaddr_in *);
186
187/**************************************************************************/
188
189static void pingstats(int junk)
190{
191 int status;
192
193 signal(SIGINT, SIG_IGN);
194
195 printf("\n--- %s ping statistics ---\n", hostent->h_name);
196 printf("%ld packets transmitted, ", ntransmitted);
197 printf("%ld packets received, ", nreceived);
198 if (nrepeats)
199 printf("%ld duplicates, ", nrepeats);
200 if (ntransmitted)
201 printf("%ld%% packet loss\n",
202 (ntransmitted - nreceived) * 100 / ntransmitted);
203 if (nreceived)
204 printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
205 tmin / 10, tmin % 10,
206 (tsum / (nreceived + nrepeats)) / 10,
207 (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
208 if (nreceived != 0)
209 status = EXIT_SUCCESS;
210 else
211 status = EXIT_FAILURE;
212 exit(status);
213}
214
215static void sendping(int junk)
216{
217 struct icmp *pkt;
218 int i;
219 char packet[datalen + 8];
220
221 pkt = (struct icmp *) packet;
222
223 pkt->icmp_type = ICMP_ECHO;
224 pkt->icmp_code = 0;
225 pkt->icmp_cksum = 0;
226 pkt->icmp_seq = ntransmitted++;
227 pkt->icmp_id = myid;
228 CLR(pkt->icmp_seq % MAX_DUP_CHK);
229
230 gettimeofday((struct timeval *) &packet[8], NULL);
231 pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(packet));
232
233 i = sendto(pingsock, packet, sizeof(packet), 0,
234 (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
235
236 if (i < 0)
237 bb_perror_msg_and_die("sendto");
238 else if ((size_t)i != sizeof(packet))
239 bb_error_msg_and_die("ping wrote %d chars; %d expected", i,
240 (int)sizeof(packet));
241
242 signal(SIGALRM, sendping);
243 if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */
244 alarm(PINGINTERVAL);
245 } else { /* done, wait for the last ping to come back */
246 /* todo, don't necessarily need to wait so long... */
247 signal(SIGALRM, pingstats);
248 alarm(MAXWAIT);
249 }
250}
251
252static char *icmp_type_name (int id)
253{
254 switch (id) {
255 case ICMP_ECHOREPLY: return "Echo Reply";
256 case ICMP_DEST_UNREACH: return "Destination Unreachable";
257 case ICMP_SOURCE_QUENCH: return "Source Quench";
258 case ICMP_REDIRECT: return "Redirect (change route)";
259 case ICMP_ECHO: return "Echo Request";
260 case ICMP_TIME_EXCEEDED: return "Time Exceeded";
261 case ICMP_PARAMETERPROB: return "Parameter Problem";
262 case ICMP_TIMESTAMP: return "Timestamp Request";
263 case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
264 case ICMP_INFO_REQUEST: return "Information Request";
265 case ICMP_INFO_REPLY: return "Information Reply";
266 case ICMP_ADDRESS: return "Address Mask Request";
267 case ICMP_ADDRESSREPLY: return "Address Mask Reply";
268 default: return "unknown ICMP type";
269 }
270}
271
272static void unpack(char *buf, int sz, struct sockaddr_in *from)
273{
274 struct icmp *icmppkt;
275 struct iphdr *iphdr;
276 struct timeval tv, *tp;
277 int hlen, dupflag;
278 unsigned long triptime;
279
280 gettimeofday(&tv, NULL);
281
282 /* check IP header */
283 iphdr = (struct iphdr *) buf;
284 hlen = iphdr->ihl << 2;
285 /* discard if too short */
286 if (sz < (datalen + ICMP_MINLEN))
287 return;
288
289 sz -= hlen;
290 icmppkt = (struct icmp *) (buf + hlen);
291
292 if (icmppkt->icmp_id != myid)
293 return; /* not our ping */
294
295 if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
296 ++nreceived;
297 tp = (struct timeval *) icmppkt->icmp_data;
298
299 if ((tv.tv_usec -= tp->tv_usec) < 0) {
300 --tv.tv_sec;
301 tv.tv_usec += 1000000;
302 }
303 tv.tv_sec -= tp->tv_sec;
304
305 triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
306 tsum += triptime;
307 if (triptime < tmin)
308 tmin = triptime;
309 if (triptime > tmax)
310 tmax = triptime;
311
312 if (TST(icmppkt->icmp_seq % MAX_DUP_CHK)) {
313 ++nrepeats;
314 --nreceived;
315 dupflag = 1;
316 } else {
317 SET(icmppkt->icmp_seq % MAX_DUP_CHK);
318 dupflag = 0;
319 }
320
321 if (options & O_QUIET)
322 return;
323
324 printf("%d bytes from %s: icmp_seq=%u", sz,
325 inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
326 icmppkt->icmp_seq);
327 printf(" ttl=%d", iphdr->ttl);
328 printf(" time=%lu.%lu ms", triptime / 10, triptime % 10);
329 if (dupflag)
330 printf(" (DUP!)");
331 printf("\n");
332 } else
333 if (icmppkt->icmp_type != ICMP_ECHO)
334 bb_error_msg("Warning: Got ICMP %d (%s)",
335 icmppkt->icmp_type, icmp_type_name (icmppkt->icmp_type));
336}
337
338static void ping(const char *host)
339{
340 char packet[datalen + MAXIPLEN + MAXICMPLEN];
341 int sockopt;
342
343 pingsock = create_icmp_socket();
344
345 memset(&pingaddr, 0, sizeof(struct sockaddr_in));
346
347 pingaddr.sin_family = AF_INET;
348 hostent = xgethostbyname(host);
349 if (hostent->h_addrtype != AF_INET)
350 bb_error_msg_and_die("unknown address type; only AF_INET is currently supported.");
351
352 memcpy(&pingaddr.sin_addr, hostent->h_addr, sizeof(pingaddr.sin_addr));
353
354 /* enable broadcast pings */
355 sockopt = 1;
356 setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt,
357 sizeof(sockopt));
358
359 /* set recv buf for broadcast pings */
360 sockopt = 48 * 1024;
361 setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt,
362 sizeof(sockopt));
363
364 printf("PING %s (%s): %d data bytes\n",
365 hostent->h_name,
366 inet_ntoa(*(struct in_addr *) &pingaddr.sin_addr.s_addr),
367 datalen);
368
369 signal(SIGINT, pingstats);
370
371 /* start the ping's going ... */
372 sendping(0);
373
374 /* listen for replies */
375 while (1) {
376 struct sockaddr_in from;
377 socklen_t fromlen = (socklen_t) sizeof(from);
378 int c;
379
380 if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
381 (struct sockaddr *) &from, &fromlen)) < 0) {
382 if (errno == EINTR)
383 continue;
384 bb_perror_msg("recvfrom");
385 continue;
386 }
387 unpack(packet, c, &from);
388 if (pingcount > 0 && nreceived >= pingcount)
389 break;
390 }
391 pingstats(0);
392}
393
394extern int ping_main(int argc, char **argv)
395{
396 char *thisarg;
397
398 datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
399
400 argc--;
401 argv++;
402 options = 0;
403 /* Parse any options */
404 while (argc >= 1 && **argv == '-') {
405 thisarg = *argv;
406 thisarg++;
407 switch (*thisarg) {
408 case 'q':
409 options |= O_QUIET;
410 break;
411 case 'c':
412 if (--argc <= 0)
413 bb_show_usage();
414 argv++;
415 pingcount = atoi(*argv);
416 break;
417 case 's':
418 if (--argc <= 0)
419 bb_show_usage();
420 argv++;
421 datalen = atoi(*argv);
422 break;
423 default:
424 bb_show_usage();
425 }
426 argc--;
427 argv++;
428 }
429 if (argc < 1)
430 bb_show_usage();
431
432 myid = getpid() & 0xFFFF;
433 ping(*argv);
434 return EXIT_SUCCESS;
435}
436#endif /* ! CONFIG_FEATURE_FANCY_PING */
437
438/*
439 * Copyright (c) 1989 The Regents of the University of California.
440 * All rights reserved.
441 *
442 * This code is derived from software contributed to Berkeley by
443 * Mike Muuss.
444 *
445 * Redistribution and use in source and binary forms, with or without
446 * modification, are permitted provided that the following conditions
447 * are met:
448 * 1. Redistributions of source code must retain the above copyright
449 * notice, this list of conditions and the following disclaimer.
450 * 2. Redistributions in binary form must reproduce the above copyright
451 * notice, this list of conditions and the following disclaimer in the
452 * documentation and/or other materials provided with the distribution.
453 *
454 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
455 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
456 *
457 * 4. Neither the name of the University nor the names of its contributors
458 * may be used to endorse or promote products derived from this software
459 * without specific prior written permission.
460 *
461 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
462 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
463 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
464 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
465 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
466 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
467 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
468 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
469 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
470 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
471 * SUCH DAMAGE.
472 */
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * $Id: ping6.c,v 1.6 2004/03/15 08:28:48 andersen Exp $
4 * Mini ping implementation for busybox
5 *
6 * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * This version of ping is adapted from the ping in netkit-base 0.10,
23 * which is:
24 *
25 * Copyright (c) 1989 The Regents of the University of California.
26 * All rights reserved.
27 *
28 * This code is derived from software contributed to Berkeley by
29 * Mike Muuss.
30 *
31 * Original copyright notice is retained at the end of this file.
32 *
33 * This version is an adaptation of ping.c from busybox.
34 * The code was modified by Bart Visscher <magick@linux-fan.com>
35 */
36
37#include <sys/param.h>
38#include <sys/socket.h>
39#include <sys/file.h>
40#include <sys/time.h>
41#include <sys/times.h>
42#include <sys/signal.h>
43
44#include <netinet/in.h>
45#include <netinet/ip6.h>
46#include <netinet/icmp6.h>
47#include <arpa/inet.h>
48#include <net/if.h>
49#include <netdb.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <errno.h>
53#include <unistd.h>
54#include <string.h>
55#include <stdlib.h>
56#include <stddef.h> /* offsetof */
57#include "busybox.h"
58
59static const int DEFDATALEN = 56;
60static const int MAXIPLEN = 60;
61static const int MAXICMPLEN = 76;
62static const int MAXPACKET = 65468;
63#define MAX_DUP_CHK (8 * 128)
64static const int MAXWAIT = 10;
65static const int PINGINTERVAL = 1; /* second */
66
67#define O_QUIET (1 << 0)
68#define O_VERBOSE (1 << 1)
69
70#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */
71#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */
72#define SET(bit) (A(bit) |= B(bit))
73#define CLR(bit) (A(bit) &= (~B(bit)))
74#define TST(bit) (A(bit) & B(bit))
75
76static void ping(const char *host);
77
78/* simple version */
79#ifndef CONFIG_FEATURE_FANCY_PING6
80static struct hostent *h;
81
82void noresp(int ign)
83{
84 printf("No response from %s\n", h->h_name);
85 exit(EXIT_FAILURE);
86}
87
88static void ping(const char *host)
89{
90 struct sockaddr_in6 pingaddr;
91 struct icmp6_hdr *pkt;
92 int pingsock, c;
93 int sockopt;
94 char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
95
96 pingsock = create_icmp6_socket();
97
98 memset(&pingaddr, 0, sizeof(struct sockaddr_in));
99
100 pingaddr.sin6_family = AF_INET6;
101 h = xgethostbyname2(host, AF_INET6);
102 memcpy(&pingaddr.sin6_addr, h->h_addr, sizeof(pingaddr.sin6_addr));
103
104 pkt = (struct icmp6_hdr *) packet;
105 memset(pkt, 0, sizeof(packet));
106 pkt->icmp6_type = ICMP6_ECHO_REQUEST;
107
108 sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
109 setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, (char *) &sockopt,
110 sizeof(sockopt));
111
112 c = sendto(pingsock, packet, sizeof(packet), 0,
113 (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in6));
114
115 if (c < 0 || c != sizeof(packet))
116 bb_perror_msg_and_die("sendto");
117
118 signal(SIGALRM, noresp);
119 alarm(5); /* give the host 5000ms to respond */
120 /* listen for replies */
121 while (1) {
122 struct sockaddr_in6 from;
123 size_t fromlen = sizeof(from);
124
125 if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
126 (struct sockaddr *) &from, &fromlen)) < 0) {
127 if (errno == EINTR)
128 continue;
129 bb_perror_msg("recvfrom");
130 continue;
131 }
132 if (c >= 8) { /* icmp6_hdr */
133 pkt = (struct icmp6_hdr *) packet;
134 if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
135 break;
136 }
137 }
138 printf("%s is alive!\n", h->h_name);
139 return;
140}
141
142extern int ping6_main(int argc, char **argv)
143{
144 argc--;
145 argv++;
146 if (argc < 1)
147 bb_show_usage();
148 ping(*argv);
149 return EXIT_SUCCESS;
150}
151
152#else /* ! CONFIG_FEATURE_FANCY_PING6 */
153/* full(er) version */
154static struct sockaddr_in6 pingaddr;
155static int pingsock = -1;
156static int datalen; /* intentionally uninitialized to work around gcc bug */
157static char* ifname;
158
159static long ntransmitted, nreceived, nrepeats, pingcount;
160static int myid, options;
161static unsigned long tmin = ULONG_MAX, tmax, tsum;
162static char rcvd_tbl[MAX_DUP_CHK / 8];
163
164# ifdef CONFIG_FEATURE_FANCY_PING
165extern
166# endif
167 struct hostent *hostent;
168
169static void sendping(int);
170static void pingstats(int);
171static void unpack(char *, int, struct sockaddr_in6 *, int);
172
173/**************************************************************************/
174
175static void pingstats(int junk)
176{
177 int status;
178
179 signal(SIGINT, SIG_IGN);
180
181 printf("\n--- %s ping statistics ---\n", hostent->h_name);
182 printf("%ld packets transmitted, ", ntransmitted);
183 printf("%ld packets received, ", nreceived);
184 if (nrepeats)
185 printf("%ld duplicates, ", nrepeats);
186 if (ntransmitted)
187 printf("%ld%% packet loss\n",
188 (ntransmitted - nreceived) * 100 / ntransmitted);
189 if (nreceived)
190 printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
191 tmin / 10, tmin % 10,
192 (tsum / (nreceived + nrepeats)) / 10,
193 (tsum / (nreceived + nrepeats)) % 10, tmax / 10, tmax % 10);
194 if (nreceived != 0)
195 status = EXIT_SUCCESS;
196 else
197 status = EXIT_FAILURE;
198 exit(status);
199}
200
201static void sendping(int junk)
202{
203 struct icmp6_hdr *pkt;
204 int i;
205 char packet[datalen + 8];
206
207 pkt = (struct icmp6_hdr *) packet;
208
209 pkt->icmp6_type = ICMP6_ECHO_REQUEST;
210 pkt->icmp6_code = 0;
211 pkt->icmp6_cksum = 0;
212 pkt->icmp6_seq = ntransmitted++;
213 pkt->icmp6_id = myid;
214 CLR(pkt->icmp6_seq % MAX_DUP_CHK);
215
216 gettimeofday((struct timeval *) &pkt->icmp6_data8[4], NULL);
217
218 i = sendto(pingsock, packet, sizeof(packet), 0,
219 (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in6));
220
221 if (i < 0)
222 bb_perror_msg_and_die("sendto");
223 else if ((size_t)i != sizeof(packet))
224 bb_error_msg_and_die("ping wrote %d chars; %d expected", i,
225 (int)sizeof(packet));
226
227 signal(SIGALRM, sendping);
228 if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */
229 alarm(PINGINTERVAL);
230 } else { /* done, wait for the last ping to come back */
231 /* todo, don't necessarily need to wait so long... */
232 signal(SIGALRM, pingstats);
233 alarm(MAXWAIT);
234 }
235}
236
237static char *icmp6_type_name (int id)
238{
239 switch (id) {
240 case ICMP6_DST_UNREACH: return "Destination Unreachable";
241 case ICMP6_PACKET_TOO_BIG: return "Packet too big";
242 case ICMP6_TIME_EXCEEDED: return "Time Exceeded";
243 case ICMP6_PARAM_PROB: return "Parameter Problem";
244 case ICMP6_ECHO_REPLY: return "Echo Reply";
245 case ICMP6_ECHO_REQUEST: return "Echo Request";
246 case ICMP6_MEMBERSHIP_QUERY: return "Membership Query";
247 case ICMP6_MEMBERSHIP_REPORT: return "Membership Report";
248 case ICMP6_MEMBERSHIP_REDUCTION: return "Membership Reduction";
249 default: return "unknown ICMP type";
250 }
251}
252
253static void unpack(char *packet, int sz, struct sockaddr_in6 *from, int hoplimit)
254{
255 struct icmp6_hdr *icmppkt;
256 struct timeval tv, *tp;
257 int dupflag;
258 unsigned long triptime;
259 char buf[INET6_ADDRSTRLEN];
260
261 gettimeofday(&tv, NULL);
262
263 /* discard if too short */
264 if (sz < (datalen + sizeof(struct icmp6_hdr)))
265 return;
266
267 icmppkt = (struct icmp6_hdr *) packet;
268
269 if (icmppkt->icmp6_id != myid)
270 return; /* not our ping */
271
272 if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
273 ++nreceived;
274 tp = (struct timeval *) &icmppkt->icmp6_data8[4];
275
276 if ((tv.tv_usec -= tp->tv_usec) < 0) {
277 --tv.tv_sec;
278 tv.tv_usec += 1000000;
279 }
280 tv.tv_sec -= tp->tv_sec;
281
282 triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
283 tsum += triptime;
284 if (triptime < tmin)
285 tmin = triptime;
286 if (triptime > tmax)
287 tmax = triptime;
288
289 if (TST(icmppkt->icmp6_seq % MAX_DUP_CHK)) {
290 ++nrepeats;
291 --nreceived;
292 dupflag = 1;
293 } else {
294 SET(icmppkt->icmp6_seq % MAX_DUP_CHK);
295 dupflag = 0;
296 }
297
298 if (options & O_QUIET)
299 return;
300
301 printf("%d bytes from %s: icmp6_seq=%u", sz,
302 inet_ntop(AF_INET6, (struct in_addr6 *) &pingaddr.sin6_addr,
303 buf, sizeof(buf)),
304 icmppkt->icmp6_seq);
305 printf(" ttl=%d time=%lu.%lu ms", hoplimit,
306 triptime / 10, triptime % 10);
307 if (dupflag)
308 printf(" (DUP!)");
309 printf("\n");
310 } else
311 if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST)
312 bb_error_msg("Warning: Got ICMP %d (%s)",
313 icmppkt->icmp6_type, icmp6_type_name (icmppkt->icmp6_type));
314}
315
316static void ping(const char *host)
317{
318 char packet[datalen + MAXIPLEN + MAXICMPLEN];
319 char buf[INET6_ADDRSTRLEN];
320 int sockopt;
321 struct msghdr msg;
322 struct sockaddr_in6 from;
323 struct iovec iov;
324 char control_buf[CMSG_SPACE(36)];
325
326 pingsock = create_icmp6_socket();
327
328 memset(&pingaddr, 0, sizeof(struct sockaddr_in));
329
330 pingaddr.sin6_family = AF_INET6;
331 hostent = xgethostbyname2(host, AF_INET6);
332 if (hostent->h_addrtype != AF_INET6)
333 bb_error_msg_and_die("unknown address type; only AF_INET6 is currently supported.");
334
335 memcpy(&pingaddr.sin6_addr, hostent->h_addr, sizeof(pingaddr.sin6_addr));
336
337#ifdef ICMP6_FILTER
338 {
339 struct icmp6_filter filt;
340 if (!(options & O_VERBOSE)) {
341 ICMP6_FILTER_SETBLOCKALL(&filt);
342#if 0
343 if ((options & F_FQDN) || (options & F_FQDNOLD) ||
344 (options & F_NODEADDR) || (options & F_SUPTYPES))
345 ICMP6_FILTER_SETPASS(ICMP6_NI_REPLY, &filt);
346 else
347#endif
348 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
349 } else {
350 ICMP6_FILTER_SETPASSALL(&filt);
351 }
352 if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
353 sizeof(filt)) < 0)
354 bb_error_msg_and_die("setsockopt(ICMP6_FILTER)");
355 }
356#endif /*ICMP6_FILTER*/
357
358 /* enable broadcast pings */
359 sockopt = 1;
360 setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *) &sockopt,
361 sizeof(sockopt));
362
363 /* set recv buf for broadcast pings */
364 sockopt = 48 * 1024;
365 setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *) &sockopt,
366 sizeof(sockopt));
367
368 sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
369 setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, (char *) &sockopt,
370 sizeof(sockopt));
371
372 sockopt = 1;
373 setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, (char *) &sockopt,
374 sizeof(sockopt));
375
376 if (ifname) {
377 if ((pingaddr.sin6_scope_id = if_nametoindex(ifname)) == 0)
378 bb_error_msg_and_die("%s: invalid interface name", ifname);
379 }
380
381 printf("PING %s (%s): %d data bytes\n",
382 hostent->h_name,
383 inet_ntop(AF_INET6, (struct in_addr6 *) &pingaddr.sin6_addr,
384 buf, sizeof(buf)),
385 datalen);
386
387 signal(SIGINT, pingstats);
388
389 /* start the ping's going ... */
390 sendping(0);
391
392 /* listen for replies */
393 msg.msg_name=&from;
394 msg.msg_namelen=sizeof(from);
395 msg.msg_iov=&iov;
396 msg.msg_iovlen=1;
397 msg.msg_control=control_buf;
398 iov.iov_base=packet;
399 iov.iov_len=sizeof(packet);
400 while (1) {
401 int c;
402 struct cmsghdr *cmsgptr = NULL;
403 int hoplimit=-1;
404 msg.msg_controllen=sizeof(control_buf);
405
406 if ((c = recvmsg(pingsock, &msg, 0)) < 0) {
407 if (errno == EINTR)
408 continue;
409 bb_perror_msg("recvfrom");
410 continue;
411 }
412 for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
413 cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
414 if (cmsgptr->cmsg_level == SOL_IPV6 &&
415 cmsgptr->cmsg_type == IPV6_HOPLIMIT ) {
416 hoplimit=*(int*)CMSG_DATA(cmsgptr);
417 }
418 }
419 unpack(packet, c, &from, hoplimit);
420 if (pingcount > 0 && nreceived >= pingcount)
421 break;
422 }
423 pingstats(0);
424}
425
426extern int ping6_main(int argc, char **argv)
427{
428 char *thisarg;
429
430 datalen = DEFDATALEN; /* initialized here rather than in global scope to work around gcc bug */
431
432 argc--;
433 argv++;
434 options = 0;
435 /* Parse any options */
436 while (argc >= 1 && **argv == '-') {
437 thisarg = *argv;
438 thisarg++;
439 switch (*thisarg) {
440 case 'v':
441 options &= ~O_QUIET;
442 options |= O_VERBOSE;
443 break;
444 case 'q':
445 options &= ~O_VERBOSE;
446 options |= O_QUIET;
447 break;
448 case 'c':
449 if (--argc <= 0)
450 bb_show_usage();
451 argv++;
452 pingcount = atoi(*argv);
453 break;
454 case 's':
455 if (--argc <= 0)
456 bb_show_usage();
457 argv++;
458 datalen = atoi(*argv);
459 break;
460 case 'I':
461 if (--argc <= 0)
462 bb_show_usage();
463 argv++;
464 ifname = *argv;
465 break;
466 default:
467 bb_show_usage();
468 }
469 argc--;
470 argv++;
471 }
472 if (argc < 1)
473 bb_show_usage();
474
475 myid = getpid() & 0xFFFF;
476 ping(*argv);
477 return EXIT_SUCCESS;
478}
479#endif /* ! CONFIG_FEATURE_FANCY_PING6 */
480
481/*
482 * Copyright (c) 1989 The Regents of the University of California.
483 * All rights reserved.
484 *
485 * This code is derived from software contributed to Berkeley by
486 * Mike Muuss.
487 *
488 * Redistribution and use in source and binary forms, with or without
489 * modification, are permitted provided that the following conditions
490 * are met:
491 * 1. Redistributions of source code must retain the above copyright
492 * notice, this list of conditions and the following disclaimer.
493 * 2. Redistributions in binary form must reproduce the above copyright
494 * notice, this list of conditions and the following disclaimer in the
495 * documentation and/or other materials provided with the distribution.
496 *
497 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
498 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
499 *
500 * 4. Neither the name of the University nor the names of its contributors
501 * may be used to endorse or promote products derived from this software
502 * without specific prior written permission.
503 *
504 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
505 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
506 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
507 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
508 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
509 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
510 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
511 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
512 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
513 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
514 * SUCH DAMAGE.
515 */
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 @@
1/* route
2 *
3 * Similar to the standard Unix route, but with only the necessary
4 * parts for AF_INET and AF_INET6
5 *
6 * Bjorn Wesen, Axis Communications AB
7 *
8 * Author of the original route:
9 * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
10 * (derived from FvK's 'route.c 1.70 01/04/94')
11 *
12 * This program is free software; you can redistribute it
13 * and/or modify it under the terms of the GNU General
14 * Public License as published by the Free Software
15 * Foundation; either version 2 of the License, or (at
16 * your option) any later version.
17 *
18 * $Id: route.c,v 1.26 2004/03/19 23:27:08 mjn3 Exp $
19 *
20 * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
21 * adjustments by Larry Doolittle <LRDoolittle@lbl.gov>
22 *
23 * IPV6 support added by Bart Visscher <magick@linux-fan.com>
24 */
25
26/* 2004/03/09 Manuel Novoa III <mjn3@codepoet.org>
27 *
28 * Rewritten to fix several bugs, add additional error checking, and
29 * remove ridiculous amounts of bloat.
30 */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <errno.h>
36#include <assert.h>
37#include <unistd.h>
38#include <fcntl.h>
39#include <getopt.h>
40#include <sys/types.h>
41#include <sys/ioctl.h>
42#include <net/route.h>
43#include <net/if.h>
44#include "busybox.h"
45#include "inet_common.h"
46
47#ifndef RTF_UP
48/* Keep this in sync with /usr/src/linux/include/linux/route.h */
49#define RTF_UP 0x0001 /* route usable */
50#define RTF_GATEWAY 0x0002 /* destination is a gateway */
51#define RTF_HOST 0x0004 /* host entry (net otherwise) */
52#define RTF_REINSTATE 0x0008 /* reinstate route after tmout */
53#define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */
54#define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */
55#define RTF_MTU 0x0040 /* specific MTU for this route */
56#ifndef RTF_MSS
57#define RTF_MSS RTF_MTU /* Compatibility :-( */
58#endif
59#define RTF_WINDOW 0x0080 /* per route window clamping */
60#define RTF_IRTT 0x0100 /* Initial round trip time */
61#define RTF_REJECT 0x0200 /* Reject route */
62#endif
63
64#if defined (SIOCADDRTOLD) || defined (RTF_IRTT) /* route */
65#define HAVE_NEW_ADDRT 1
66#endif
67
68#if HAVE_NEW_ADDRT
69#define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr)
70#define full_mask(x) (x)
71#else
72#define mask_in_addr(x) ((x).rt_genmask)
73#define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr)
74#endif
75
76/* The RTACTION entries must agree with tbl_verb[] below! */
77#define RTACTION_ADD 1
78#define RTACTION_DEL 2
79
80/* For the various tbl_*[] arrays, the 1st byte is the offset to
81 * the next entry and the 2nd byte is return value. */
82
83#define NET_FLAG 1
84#define HOST_FLAG 2
85
86/* We remap '-' to '#' to avoid problems with getopt. */
87static const char tbl_hash_net_host[] =
88 "\007\001#net\0"
89/* "\010\002#host\0" */
90 "\007\002#host" /* Since last, we can save a byte. */
91;
92
93#define KW_TAKES_ARG 020
94#define KW_SETS_FLAG 040
95
96#define KW_IPVx_METRIC 020
97#define KW_IPVx_NETMASK 021
98#define KW_IPVx_GATEWAY 022
99#define KW_IPVx_MSS 023
100#define KW_IPVx_WINDOW 024
101#define KW_IPVx_IRTT 025
102#define KW_IPVx_DEVICE 026
103
104#define KW_IPVx_FLAG_ONLY 040
105#define KW_IPVx_REJECT 040
106#define KW_IPVx_MOD 041
107#define KW_IPVx_DYN 042
108#define KW_IPVx_REINSTATE 043
109
110static const char tbl_ipvx[] =
111 /* 020 is the "takes an arg" bit */
112#if HAVE_NEW_ADDRT
113 "\011\020metric\0"
114#endif
115 "\012\021netmask\0"
116 "\005\022gw\0"
117 "\012\022gateway\0"
118 "\006\023mss\0"
119 "\011\024window\0"
120#ifdef RTF_IRTT
121 "\007\025irtt\0"
122#endif
123 "\006\026dev\0"
124 "\011\026device\0"
125 /* 040 is the "sets a flag" bit - MUST match flags_ipvx[] values below. */
126#ifdef RTF_REJECT
127 "\011\040reject\0"
128#endif
129 "\006\041mod\0"
130 "\006\042dyn\0"
131/* "\014\043reinstate\0" */
132 "\013\043reinstate" /* Since last, we can save a byte. */
133;
134
135static const int flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */
136#ifdef RTF_REJECT
137 RTF_REJECT,
138#endif
139 RTF_MODIFIED,
140 RTF_DYNAMIC,
141 RTF_REINSTATE
142};
143
144static int kw_lookup(const char *kwtbl, char ***pargs)
145{
146 if (**pargs) {
147 do {
148 if (strcmp(kwtbl+2, **pargs) == 0) { /* Found a match. */
149 *pargs += 1;
150 if (kwtbl[1] & KW_TAKES_ARG) {
151 if (!**pargs) { /* No more args! */
152 bb_show_usage();
153 }
154 *pargs += 1; /* Calling routine will use args[-1]. */
155 }
156 return kwtbl[1];
157 }
158 kwtbl += *kwtbl;
159 } while (*kwtbl);
160 }
161 return 0;
162}
163
164/* Add or delete a route, depending on action. */
165
166static void INET_setroute(int action, char **args)
167{
168 struct rtentry rt;
169 const char *netmask;
170 int skfd, isnet, xflag;
171
172 assert((action == RTACTION_ADD) || (action == RTACTION_DEL));
173
174 /* Grab the -net or -host options. Remember they were transformed. */
175 xflag = kw_lookup(tbl_hash_net_host, &args);
176
177 /* If we did grab -net or -host, make sure we still have an arg left. */
178 if (*args == NULL) {
179 bb_show_usage();
180 }
181
182 /* Clean out the RTREQ structure. */
183 memset((char *) &rt, 0, sizeof(struct rtentry));
184
185 {
186 const char *target = *args++;
187
188 /* Prefer hostname lookup is -host flag (xflag==1) was given. */
189 isnet = INET_resolve(target, (struct sockaddr_in *) &rt.rt_dst,
190 (xflag & HOST_FLAG));
191 if (isnet < 0) {
192 bb_error_msg_and_die("resolving %s", target);
193 }
194
195 }
196
197 if (xflag) { /* Reinit isnet if -net or -host was specified. */
198 isnet = (xflag & NET_FLAG);
199 }
200
201 /* Fill in the other fields. */
202 rt.rt_flags = ((isnet) ? RTF_UP : (RTF_UP | RTF_HOST));
203
204 netmask = bb_INET_default;
205
206 while (*args) {
207 int k = kw_lookup(tbl_ipvx, &args);
208 const char *args_m1 = args[-1];
209
210 if (k & KW_IPVx_FLAG_ONLY) {
211 rt.rt_flags |= flags_ipvx[k & 3];
212 continue;
213 }
214
215#if HAVE_NEW_ADDRT
216 if (k == KW_IPVx_METRIC) {
217 rt.rt_metric = bb_xgetularg10(args_m1) + 1;
218 continue;
219 }
220#endif
221
222 if (k == KW_IPVx_NETMASK) {
223 struct sockaddr mask;
224
225 if (mask_in_addr(rt)) {
226 bb_show_usage();
227 }
228
229 netmask = args_m1;
230 isnet = INET_resolve(netmask, (struct sockaddr_in *) &mask, 0);
231 if (isnet < 0) {
232 bb_error_msg_and_die("resolving %s", netmask);
233 }
234 rt.rt_genmask = full_mask(mask);
235 continue;
236 }
237
238 if (k == KW_IPVx_GATEWAY) {
239 if (rt.rt_flags & RTF_GATEWAY) {
240 bb_show_usage();
241 }
242
243 isnet = INET_resolve(args_m1,
244 (struct sockaddr_in *) &rt.rt_gateway, 1);
245 rt.rt_flags |= RTF_GATEWAY;
246
247 if (isnet) {
248 if (isnet < 0) {
249 bb_error_msg_and_die("resolving %s", args_m1);
250 }
251 bb_error_msg_and_die("gateway %s is a NETWORK", args_m1);
252 }
253 continue;
254 }
255
256 if (k == KW_IPVx_MSS) { /* Check valid MSS bounds. */
257 rt.rt_flags |= RTF_MSS;
258 rt.rt_mss = bb_xgetularg10_bnd(args_m1, 64, 32768);
259 continue;
260 }
261
262 if (k == KW_IPVx_WINDOW) { /* Check valid window bounds. */
263 rt.rt_flags |= RTF_WINDOW;
264 rt.rt_window = bb_xgetularg10_bnd(args_m1, 128, INT_MAX);
265 continue;
266 }
267
268#ifdef RTF_IRTT
269 if (k == KW_IPVx_IRTT) {
270 rt.rt_flags |= RTF_IRTT;
271 rt.rt_irtt = bb_xgetularg10(args_m1);
272 rt.rt_irtt *= (sysconf(_SC_CLK_TCK) / 100); /* FIXME */
273#if 0 /* FIXME: do we need to check anything of this? */
274 if (rt.rt_irtt < 1 || rt.rt_irtt > (120 * HZ)) {
275 bb_error_msg_and_die("bad irtt");
276 }
277#endif
278 continue;
279 }
280#endif
281
282 /* Device is special in that it can be the last arg specified
283 * and doesn't requre the dev/device keyword in that case. */
284 if (!rt.rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
285 /* Don't use args_m1 here since args may have changed! */
286 rt.rt_dev = args[-1];
287 continue;
288 }
289
290 /* Nothing matched. */
291 bb_show_usage();
292 }
293
294#ifdef RTF_REJECT
295 if ((rt.rt_flags & RTF_REJECT) && !rt.rt_dev) {
296 rt.rt_dev = "lo";
297 }
298#endif
299
300 /* sanity checks.. */
301 if (mask_in_addr(rt)) {
302 unsigned long mask = mask_in_addr(rt);
303
304 mask = ~ntohl(mask);
305 if ((rt.rt_flags & RTF_HOST) && mask != 0xffffffff) {
306 bb_error_msg_and_die("netmask %.8x and host route conflict",
307 (unsigned int) mask);
308 }
309 if (mask & (mask + 1)) {
310 bb_error_msg_and_die("bogus netmask %s", netmask);
311 }
312 mask = ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr;
313 if (mask & ~mask_in_addr(rt)) {
314 bb_error_msg_and_die("netmask and route address conflict");
315 }
316 }
317
318 /* Fill out netmask if still unset */
319 if ((action == RTACTION_ADD) && (rt.rt_flags & RTF_HOST)) {
320 mask_in_addr(rt) = 0xffffffff;
321 }
322
323 /* Create a socket to the INET kernel. */
324 if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
325 bb_perror_msg_and_die("socket");
326 }
327
328 if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) {
329 bb_perror_msg_and_die("SIOC[ADD|DEL]RT");
330 }
331
332 /* Don't bother closing, as we're exiting after we return anyway. */
333 /* close(skfd); */
334}
335
336#ifdef CONFIG_FEATURE_IPV6
337
338static void INET6_setroute(int action, char **args)
339{
340 struct sockaddr_in6 sa6;
341 struct in6_rtmsg rt;
342 int prefix_len, skfd;
343 const char *devname;
344
345 assert((action == RTACTION_ADD) || (action == RTACTION_DEL));
346
347 {
348 /* We know args isn't NULL from the check in route_main. */
349 const char *target = *args++;
350
351 if (strcmp(target, "default") == 0) {
352 prefix_len = 0;
353 memset(&sa6, 0, sizeof(sa6));
354 } else {
355 char *cp;
356 if ((cp = strchr(target, '/'))) { /* Yes... const to non is ok. */
357 *cp = 0;
358 prefix_len = bb_xgetularg10_bnd(cp+1, 0, 128);
359 } else {
360 prefix_len = 128;
361 }
362 if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {
363 bb_error_msg_and_die("resolving %s", target);
364 }
365 }
366 }
367
368 /* Clean out the RTREQ structure. */
369 memset((char *) &rt, 0, sizeof(struct in6_rtmsg));
370
371 memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));
372
373 /* Fill in the other fields. */
374 rt.rtmsg_dst_len = prefix_len;
375 rt.rtmsg_flags = ((prefix_len == 128) ? (RTF_UP|RTF_HOST) : RTF_UP);
376 rt.rtmsg_metric = 1;
377
378 devname = NULL;
379
380 while (*args) {
381 int k = kw_lookup(tbl_ipvx, &args);
382 const char *args_m1 = args[-1];
383
384 if ((k == KW_IPVx_MOD) || (k == KW_IPVx_DYN)) {
385 rt.rtmsg_flags |= flags_ipvx[k & 3];
386 continue;
387 }
388
389 if (k == KW_IPVx_METRIC) {
390 rt.rtmsg_metric = bb_xgetularg10(args_m1);
391 continue;
392 }
393
394 if (k == KW_IPVx_GATEWAY) {
395 if (rt.rtmsg_flags & RTF_GATEWAY) {
396 bb_show_usage();
397 }
398
399 if (INET6_resolve(args_m1, (struct sockaddr_in6 *) &sa6) < 0) {
400 bb_error_msg_and_die("resolving %s", args_m1);
401 }
402 memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
403 sizeof(struct in6_addr));
404 rt.rtmsg_flags |= RTF_GATEWAY;
405 continue;
406 }
407
408 /* Device is special in that it can be the last arg specified
409 * and doesn't requre the dev/device keyword in that case. */
410 if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
411 /* Don't use args_m1 here since args may have changed! */
412 devname = args[-1];
413 continue;
414 }
415
416 /* Nothing matched. */
417 bb_show_usage();
418 }
419
420 /* Create a socket to the INET6 kernel. */
421 if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
422 bb_perror_msg_and_die("socket");
423 }
424
425 rt.rtmsg_ifindex = 0;
426
427 if (devname) {
428 struct ifreq ifr;
429 memset(&ifr, 0, sizeof(ifr));
430 strcpy(ifr.ifr_name, devname);
431
432 if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) {
433 bb_perror_msg_and_die("SIOGIFINDEX");
434 }
435 rt.rtmsg_ifindex = ifr.ifr_ifindex;
436 }
437
438 /* Tell the kernel to accept this route. */
439 if (ioctl(skfd, ((action==RTACTION_ADD) ? SIOCADDRT : SIOCDELRT), &rt)<0) {
440 bb_perror_msg_and_die("SIOC[ADD|DEL]RT");
441 }
442
443 /* Don't bother closing, as we're exiting after we return anyway. */
444 /* close(skfd); */
445}
446#endif
447
448static const unsigned int flagvals[] = { /* Must agree with flagchars[]. */
449 RTF_GATEWAY,
450 RTF_HOST,
451 RTF_REINSTATE,
452 RTF_DYNAMIC,
453 RTF_MODIFIED,
454#ifdef CONFIG_FEATURE_IPV6
455 RTF_DEFAULT,
456 RTF_ADDRCONF,
457 RTF_CACHE
458#endif
459};
460
461#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
462#define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE)
463
464static const char flagchars[] = /* Must agree with flagvals[]. */
465 "GHRDM"
466#ifdef CONFIG_FEATURE_IPV6
467 "DAC"
468#endif
469;
470
471static
472#ifndef CONFIG_FEATURE_IPV6
473__inline
474#endif
475void set_flags(char *flagstr, int flags)
476{
477 int i;
478
479 *flagstr++ = 'U';
480
481 for (i=0 ; (*flagstr = flagchars[i]) != 0 ; i++) {
482 if (flags & flagvals[i]) {
483 ++flagstr;
484 }
485 }
486}
487
488void displayroutes(int noresolve, int netstatfmt)
489{
490 char devname[64], flags[16], sdest[16], sgw[16];
491 unsigned long int d, g, m;
492 int flgs, ref, use, metric, mtu, win, ir;
493 struct sockaddr_in s_addr;
494 struct in_addr mask;
495
496 FILE *fp = bb_xfopen("/proc/net/route", "r");
497
498 bb_printf("Kernel IP routing table\n"
499 "Destination Gateway Genmask"
500 " Flags %s Iface\n",
501 netstatfmt ? " MSS Window irtt" : "Metric Ref Use");
502
503 if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */
504 goto ERROR; /* Empty or missing line, or read error. */
505 }
506 while (1) {
507 int r;
508 r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
509 devname, &d, &g, &flgs, &ref, &use, &metric, &m,
510 &mtu, &win, &ir);
511 if (r != 11) {
512 if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
513 break;
514 }
515 ERROR:
516 bb_error_msg_and_die("fscanf");
517 }
518
519 if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */
520 continue;
521 }
522
523 set_flags(flags, (flgs & IPV4_MASK));
524#ifdef RTF_REJECT
525 if (flgs & RTF_REJECT) {
526 flags[0] = '!';
527 }
528#endif
529
530 memset(&s_addr, 0, sizeof(struct sockaddr_in));
531 s_addr.sin_family = AF_INET;
532 s_addr.sin_addr.s_addr = d;
533 INET_rresolve(sdest, sizeof(sdest), &s_addr,
534 (noresolve | 0x8000), m); /* Default instead of *. */
535
536 s_addr.sin_addr.s_addr = g;
537 INET_rresolve(sgw, sizeof(sgw), &s_addr,
538 (noresolve | 0x4000), m); /* Host instead of net. */
539
540 mask.s_addr = m;
541 bb_printf("%-16s%-16s%-16s%-6s", sdest, sgw, inet_ntoa(mask), flags);
542 if (netstatfmt) {
543 bb_printf("%5d %-5d %6d %s\n", mtu, win, ir, devname);
544 } else {
545 bb_printf("%-6d %-2d %7d %s\n", metric, ref, use, devname);
546 }
547 }
548}
549
550#ifdef CONFIG_FEATURE_IPV6
551
552static void INET6_displayroutes(int noresolve)
553{
554 char addr6[128], naddr6[128];
555 /* In addr6x, we store both 40-byte ':'-delimited ipv6 addresses.
556 * We read the non-delimited strings into the tail of the buffer
557 * using fscanf and then modify the buffer by shifting forward
558 * while inserting ':'s and the nul terminator for the first string.
559 * Hence the strings are at addr6x and addr6x+40. This generates
560 * _much_ less code than the previous (upstream) approach. */
561 char addr6x[80];
562 char iface[16], flags[16];
563 int iflags, metric, refcnt, use, prefix_len, slen;
564 struct sockaddr_in6 snaddr6;
565
566 FILE *fp = bb_xfopen("/proc/net/ipv6_route", "r");
567
568 bb_printf("Kernel IPv6 routing table\n%-44s%-40s"
569 "Flags Metric Ref Use Iface\n",
570 "Destination", "Next Hop");
571
572 while (1) {
573 int r;
574 r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n",
575 addr6x+14, &prefix_len, &slen, addr6x+40+7,
576 &metric, &use, &refcnt, &iflags, iface);
577 if (r != 9) {
578 if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
579 break;
580 }
581 ERROR:
582 bb_error_msg_and_die("fscanf");
583 }
584
585 /* Do the addr6x shift-and-insert changes to ':'-delimit addresses.
586 * For now, always do this to validate the proc route format, even
587 * if the interface is down. */
588 {
589 int i = 0;
590 char *p = addr6x+14;
591
592 do {
593 if (!*p) {
594 if (i==40) { /* nul terminator for 1st address? */
595 addr6x[39] = 0; /* Fixup... need 0 instead of ':'. */
596 ++p; /* Skip and continue. */
597 continue;
598 }
599 goto ERROR;
600 }
601 addr6x[i++] = *p++;
602 if (!((i+1)%5)) {
603 addr6x[i++] = ':';
604 }
605 } while (i < 40+28+7);
606 }
607
608 if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */
609 continue;
610 }
611
612 set_flags(flags, (iflags & IPV6_MASK));
613
614 r = 0;
615 do {
616 inet_pton(AF_INET6, addr6x + r,
617 (struct sockaddr *) &snaddr6.sin6_addr);
618 snaddr6.sin6_family = AF_INET6;
619 INET6_rresolve(naddr6, sizeof(naddr6),
620 (struct sockaddr_in6 *) &snaddr6,
621#if 0
622 (noresolve | 0x8000) /* Default instead of *. */
623#else
624 0x0fff /* Apparently, upstream never resolves. */
625#endif
626 );
627
628 if (!r) { /* 1st pass */
629 snprintf(addr6, sizeof(addr6), "%s/%d", naddr6, prefix_len);
630 r += 40;
631 } else { /* 2nd pass */
632 /* Print the info. */
633 bb_printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
634 addr6, naddr6, flags, metric, refcnt, use, iface);
635 break;
636 }
637 } while (1);
638 }
639}
640
641#endif
642
643#define ROUTE_OPT_A 0x01
644#define ROUTE_OPT_n 0x02
645#define ROUTE_OPT_e 0x04
646#define ROUTE_OPT_INET6 0x08 /* Not an actual option. See below. */
647
648/* 1st byte is offset to next entry offset. 2nd byte is return value. */
649static const char tbl_verb[] = /* 2nd byte matches RTACTION_* code */
650 "\006\001add\0"
651 "\006\002del\0"
652/* "\011\002delete\0" */
653 "\010\002delete" /* Since last, we can save a byte. */
654;
655
656int route_main(int argc, char **argv)
657{
658 unsigned long opt;
659 int what;
660 char *family;
661
662 /* First, remap '-net' and '-host' to avoid getopt problems. */
663 {
664 char **p = argv;
665
666 while (*++p) {
667 if ((strcmp(*p, "-net") == 0) || (strcmp(*p, "-host") == 0)) {
668 p[0][0] = '#';
669 }
670 }
671 }
672
673 opt = bb_getopt_ulflags(argc, argv, "A:ne", &family);
674
675 if ((opt & ROUTE_OPT_A) && strcmp(family, "inet")) {
676#ifdef CONFIG_FEATURE_IPV6
677 if (strcmp(family, "inet6") == 0) {
678 opt |= ROUTE_OPT_INET6; /* Set flag for ipv6. */
679 } else
680#endif
681 bb_show_usage();
682 }
683
684 argv += optind;
685
686 /* No more args means display the routing table. */
687 if (!*argv) {
688 int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0;
689#ifdef CONFIG_FEATURE_IPV6
690 if (opt & ROUTE_OPT_INET6)
691 INET6_displayroutes(noresolve);
692 else
693#endif
694 displayroutes(noresolve, opt & ROUTE_OPT_e);
695
696 bb_xferror_stdout();
697 bb_fflush_stdout_and_exit(EXIT_SUCCESS);
698 }
699
700 /* Check verb. At the moment, must be add, del, or delete. */
701 what = kw_lookup(tbl_verb, &argv);
702 if (!what || !*argv) { /* Unknown verb or no more args. */
703 bb_show_usage();
704 }
705
706#ifdef CONFIG_FEATURE_IPV6
707 if (opt & ROUTE_OPT_INET6)
708 INET6_setroute(what, argv);
709 else
710#endif
711 INET_setroute(what, argv);
712
713 return EXIT_SUCCESS;
714}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * telnet implementation for busybox
4 *
5 * Author: Tomi Ollila <too@iki.fi>
6 * Copyright (C) 1994-2000 by Tomi Ollila
7 *
8 * Created: Thu Apr 7 13:29:41 1994 too
9 * Last modified: Fri Jun 9 14:34:24 2000 too
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 * HISTORY
26 * Revision 3.1 1994/04/17 11:31:54 too
27 * initial revision
28 * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen <andersen@codepoet.org>
29 * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan
30 * <jam@ltsp.org>
31 * Modified 2004/02/11 to add ability to pass the USER variable to remote host
32 * by Fernando Silveira <swrh@gmx.net>
33 *
34 */
35
36#include <termios.h>
37#include <unistd.h>
38#include <errno.h>
39#include <stdlib.h>
40#include <stdarg.h>
41#include <string.h>
42#include <signal.h>
43#include <arpa/telnet.h>
44#include <sys/types.h>
45#include <sys/socket.h>
46#include <netinet/in.h>
47#include "busybox.h"
48
49#if 0
50static const int DOTRACE = 1;
51#endif
52
53#ifdef DOTRACE
54#include <arpa/inet.h> /* for inet_ntoa()... */
55#define TRACE(x, y) do { if (x) printf y; } while (0)
56#else
57#define TRACE(x, y)
58#endif
59
60#if 0
61#define USE_POLL
62#include <sys/poll.h>
63#else
64#include <sys/time.h>
65#endif
66
67#define DATABUFSIZE 128
68#define IACBUFSIZE 128
69
70static const int CHM_TRY = 0;
71static const int CHM_ON = 1;
72static const int CHM_OFF = 2;
73
74static const int UF_ECHO = 0x01;
75static const int UF_SGA = 0x02;
76
77enum {
78 TS_0 = 1,
79 TS_IAC = 2,
80 TS_OPT = 3,
81 TS_SUB1 = 4,
82 TS_SUB2 = 5,
83};
84
85#define WriteCS(fd, str) write(fd, str, sizeof str -1)
86
87typedef unsigned char byte;
88
89/* use globals to reduce size ??? */ /* test this hypothesis later */
90static struct Globalvars {
91 int netfd; /* console fd:s are 0 and 1 (and 2) */
92 /* same buffer used both for network and console read/write */
93 char buf[DATABUFSIZE]; /* allocating so static size is smaller */
94 byte telstate; /* telnet negotiation state from network input */
95 byte telwish; /* DO, DONT, WILL, WONT */
96 byte charmode;
97 byte telflags;
98 byte gotsig;
99 /* buffer to handle telnet negotiations */
100 char iacbuf[IACBUFSIZE];
101 short iaclen; /* could even use byte */
102 struct termios termios_def;
103 struct termios termios_raw;
104} G;
105
106#define xUSE_GLOBALVAR_PTR /* xUSE... -> don't use :D (makes smaller code) */
107
108#ifdef USE_GLOBALVAR_PTR
109struct Globalvars * Gptr;
110#define G (*Gptr)
111#endif
112
113static inline void iacflush(void)
114{
115 write(G.netfd, G.iacbuf, G.iaclen);
116 G.iaclen = 0;
117}
118
119/* Function prototypes */
120static void rawmode(void);
121static void cookmode(void);
122static void do_linemode(void);
123static void will_charmode(void);
124static void telopt(byte c);
125static int subneg(byte c);
126
127/* Some globals */
128static int one = 1;
129
130#ifdef CONFIG_FEATURE_TELNET_TTYPE
131static char *ttype;
132#endif
133
134#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
135static const char *autologin;
136#endif
137
138#ifdef CONFIG_FEATURE_AUTOWIDTH
139static int win_width, win_height;
140#endif
141
142static void doexit(int ev)
143{
144 cookmode();
145 exit(ev);
146}
147
148static void conescape(void)
149{
150 char b;
151
152 if (G.gotsig) /* came from line mode... go raw */
153 rawmode();
154
155 WriteCS(1, "\r\nConsole escape. Commands are:\r\n\n"
156 " l go to line mode\r\n"
157 " c go to character mode\r\n"
158 " z suspend telnet\r\n"
159 " e exit telnet\r\n");
160
161 if (read(0, &b, 1) <= 0)
162 doexit(1);
163
164 switch (b)
165 {
166 case 'l':
167 if (!G.gotsig)
168 {
169 do_linemode();
170 goto rrturn;
171 }
172 break;
173 case 'c':
174 if (G.gotsig)
175 {
176 will_charmode();
177 goto rrturn;
178 }
179 break;
180 case 'z':
181 cookmode();
182 kill(0, SIGTSTP);
183 rawmode();
184 break;
185 case 'e':
186 doexit(0);
187 }
188
189 WriteCS(1, "continuing...\r\n");
190
191 if (G.gotsig)
192 cookmode();
193
194 rrturn:
195 G.gotsig = 0;
196
197}
198static void handlenetoutput(int len)
199{
200 /* here we could do smart tricks how to handle 0xFF:s in output
201 * stream like writing twice every sequence of FF:s (thus doing
202 * many write()s. But I think interactive telnet application does
203 * not need to be 100% 8-bit clean, so changing every 0xff:s to
204 * 0x7f:s
205 *
206 * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com)
207 * I don't agree.
208 * first - I cannot use programs like sz/rz
209 * second - the 0x0D is sent as one character and if the next
210 * char is 0x0A then it's eaten by a server side.
211 * third - whay doy you have to make 'many write()s'?
212 * I don't understand.
213 * So I implemented it. It's realy useful for me. I hope that
214 * others people will find it interesting to.
215 */
216
217 int i, j;
218 byte * p = G.buf;
219 byte outbuf[4*DATABUFSIZE];
220
221 for (i = len, j = 0; i > 0; i--, p++)
222 {
223 if (*p == 0x1d)
224 {
225 conescape();
226 return;
227 }
228 outbuf[j++] = *p;
229 if (*p == 0xff)
230 outbuf[j++] = 0xff;
231 else if (*p == 0x0d)
232 outbuf[j++] = 0x00;
233 }
234 if (j > 0 )
235 write(G.netfd, outbuf, j);
236}
237
238
239static void handlenetinput(int len)
240{
241 int i;
242 int cstart = 0;
243
244 for (i = 0; i < len; i++)
245 {
246 byte c = G.buf[i];
247
248 if (G.telstate == 0) /* most of the time state == 0 */
249 {
250 if (c == IAC)
251 {
252 cstart = i;
253 G.telstate = TS_IAC;
254 }
255 }
256 else
257 switch (G.telstate)
258 {
259 case TS_0:
260 if (c == IAC)
261 G.telstate = TS_IAC;
262 else
263 G.buf[cstart++] = c;
264 break;
265
266 case TS_IAC:
267 if (c == IAC) /* IAC IAC -> 0xFF */
268 {
269 G.buf[cstart++] = c;
270 G.telstate = TS_0;
271 break;
272 }
273 /* else */
274 switch (c)
275 {
276 case SB:
277 G.telstate = TS_SUB1;
278 break;
279 case DO:
280 case DONT:
281 case WILL:
282 case WONT:
283 G.telwish = c;
284 G.telstate = TS_OPT;
285 break;
286 default:
287 G.telstate = TS_0; /* DATA MARK must be added later */
288 }
289 break;
290 case TS_OPT: /* WILL, WONT, DO, DONT */
291 telopt(c);
292 G.telstate = TS_0;
293 break;
294 case TS_SUB1: /* Subnegotiation */
295 case TS_SUB2: /* Subnegotiation */
296 if (subneg(c))
297 G.telstate = TS_0;
298 break;
299 }
300 }
301 if (G.telstate)
302 {
303 if (G.iaclen) iacflush();
304 if (G.telstate == TS_0) G.telstate = 0;
305
306 len = cstart;
307 }
308
309 if (len)
310 write(1, G.buf, len);
311}
312
313
314/* ******************************* */
315
316static inline void putiac(int c)
317{
318 G.iacbuf[G.iaclen++] = c;
319}
320
321
322static void putiac2(byte wwdd, byte c)
323{
324 if (G.iaclen + 3 > IACBUFSIZE)
325 iacflush();
326
327 putiac(IAC);
328 putiac(wwdd);
329 putiac(c);
330}
331
332#if 0
333static void putiac1(byte c)
334{
335 if (G.iaclen + 2 > IACBUFSIZE)
336 iacflush();
337
338 putiac(IAC);
339 putiac(c);
340}
341#endif
342
343#ifdef CONFIG_FEATURE_TELNET_TTYPE
344static void putiac_subopt(byte c, char *str)
345{
346 int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 )
347
348 if (G.iaclen + len > IACBUFSIZE)
349 iacflush();
350
351 putiac(IAC);
352 putiac(SB);
353 putiac(c);
354 putiac(0);
355
356 while(*str)
357 putiac(*str++);
358
359 putiac(IAC);
360 putiac(SE);
361}
362#endif
363
364#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
365static void putiac_subopt_autologin(void)
366{
367 int len = strlen(autologin) + 6; // (2 + 1 + 1 + strlen + 2)
368 char *user = "USER";
369
370 if (G.iaclen + len > IACBUFSIZE)
371 iacflush();
372
373 putiac(IAC);
374 putiac(SB);
375 putiac(TELOPT_NEW_ENVIRON);
376 putiac(TELQUAL_IS);
377 putiac(NEW_ENV_VAR);
378
379 while(*user)
380 putiac(*user++);
381
382 putiac(NEW_ENV_VALUE);
383
384 while(*autologin)
385 putiac(*autologin++);
386
387 putiac(IAC);
388 putiac(SE);
389}
390#endif
391
392#ifdef CONFIG_FEATURE_AUTOWIDTH
393static void putiac_naws(byte c, int x, int y)
394{
395 if (G.iaclen + 9 > IACBUFSIZE)
396 iacflush();
397
398 putiac(IAC);
399 putiac(SB);
400 putiac(c);
401
402 putiac((x >> 8) & 0xff);
403 putiac(x & 0xff);
404 putiac((y >> 8) & 0xff);
405 putiac(y & 0xff);
406
407 putiac(IAC);
408 putiac(SE);
409}
410#endif
411
412/* void putiacstring (subneg strings) */
413
414/* ******************************* */
415
416static char const escapecharis[] = "\r\nEscape character is ";
417
418static void setConMode(void)
419{
420 if (G.telflags & UF_ECHO)
421 {
422 if (G.charmode == CHM_TRY) {
423 G.charmode = CHM_ON;
424 printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis);
425 rawmode();
426 }
427 }
428 else
429 {
430 if (G.charmode != CHM_OFF) {
431 G.charmode = CHM_OFF;
432 printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis);
433 cookmode();
434 }
435 }
436}
437
438/* ******************************* */
439
440static void will_charmode(void)
441{
442 G.charmode = CHM_TRY;
443 G.telflags |= (UF_ECHO | UF_SGA);
444 setConMode();
445
446 putiac2(DO, TELOPT_ECHO);
447 putiac2(DO, TELOPT_SGA);
448 iacflush();
449}
450
451static void do_linemode(void)
452{
453 G.charmode = CHM_TRY;
454 G.telflags &= ~(UF_ECHO | UF_SGA);
455 setConMode();
456
457 putiac2(DONT, TELOPT_ECHO);
458 putiac2(DONT, TELOPT_SGA);
459 iacflush();
460}
461
462/* ******************************* */
463
464static inline void to_notsup(char c)
465{
466 if (G.telwish == WILL) putiac2(DONT, c);
467 else if (G.telwish == DO) putiac2(WONT, c);
468}
469
470static inline void to_echo(void)
471{
472 /* if server requests ECHO, don't agree */
473 if (G.telwish == DO) { putiac2(WONT, TELOPT_ECHO); return; }
474 else if (G.telwish == DONT) return;
475
476 if (G.telflags & UF_ECHO)
477 {
478 if (G.telwish == WILL)
479 return;
480 }
481 else
482 if (G.telwish == WONT)
483 return;
484
485 if (G.charmode != CHM_OFF)
486 G.telflags ^= UF_ECHO;
487
488 if (G.telflags & UF_ECHO)
489 putiac2(DO, TELOPT_ECHO);
490 else
491 putiac2(DONT, TELOPT_ECHO);
492
493 setConMode();
494 WriteCS(1, "\r\n"); /* sudden modec */
495}
496
497static inline void to_sga(void)
498{
499 /* daemon always sends will/wont, client do/dont */
500
501 if (G.telflags & UF_SGA)
502 {
503 if (G.telwish == WILL)
504 return;
505 }
506 else
507 if (G.telwish == WONT)
508 return;
509
510 if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */
511 putiac2(DO, TELOPT_SGA);
512 else
513 putiac2(DONT, TELOPT_SGA);
514
515 return;
516}
517
518#ifdef CONFIG_FEATURE_TELNET_TTYPE
519static inline void to_ttype(void)
520{
521 /* Tell server we will (or won't) do TTYPE */
522
523 if(ttype)
524 putiac2(WILL, TELOPT_TTYPE);
525 else
526 putiac2(WONT, TELOPT_TTYPE);
527
528 return;
529}
530#endif
531
532#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
533static inline void to_new_environ(void)
534{
535 /* Tell server we will (or will not) do AUTOLOGIN */
536
537 if (autologin)
538 putiac2(WILL, TELOPT_NEW_ENVIRON);
539 else
540 putiac2(WONT, TELOPT_NEW_ENVIRON);
541
542 return;
543}
544#endif
545
546#ifdef CONFIG_FEATURE_AUTOWIDTH
547static inline void to_naws(void)
548{
549 /* Tell server we will do NAWS */
550 putiac2(WILL, TELOPT_NAWS);
551 return;
552}
553#endif
554
555static void telopt(byte c)
556{
557 switch (c)
558 {
559 case TELOPT_ECHO: to_echo(); break;
560 case TELOPT_SGA: to_sga(); break;
561#ifdef CONFIG_FEATURE_TELNET_TTYPE
562 case TELOPT_TTYPE: to_ttype();break;
563#endif
564#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
565 case TELOPT_NEW_ENVIRON: to_new_environ(); break;
566#endif
567#ifdef CONFIG_FEATURE_AUTOWIDTH
568 case TELOPT_NAWS: to_naws();
569 putiac_naws(c, win_width, win_height);
570 break;
571#endif
572 default: to_notsup(c);
573 break;
574 }
575}
576
577
578/* ******************************* */
579
580/* subnegotiation -- ignore all (except TTYPE,NAWS) */
581
582static int subneg(byte c)
583{
584 switch (G.telstate)
585 {
586 case TS_SUB1:
587 if (c == IAC)
588 G.telstate = TS_SUB2;
589#ifdef CONFIG_FEATURE_TELNET_TTYPE
590 else
591 if (c == TELOPT_TTYPE)
592 putiac_subopt(TELOPT_TTYPE,ttype);
593#endif
594#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
595 else
596 if (c == TELOPT_NEW_ENVIRON)
597 putiac_subopt_autologin();
598#endif
599 break;
600 case TS_SUB2:
601 if (c == SE)
602 return TRUE;
603 G.telstate = TS_SUB1;
604 /* break; */
605 }
606 return FALSE;
607}
608
609/* ******************************* */
610
611static void fgotsig(int sig)
612{
613 G.gotsig = sig;
614}
615
616
617static void rawmode(void)
618{
619 tcsetattr(0, TCSADRAIN, &G.termios_raw);
620}
621
622static void cookmode(void)
623{
624 tcsetattr(0, TCSADRAIN, &G.termios_def);
625}
626
627extern int telnet_main(int argc, char** argv)
628{
629 int len;
630 struct sockaddr_in s_in;
631#ifdef USE_POLL
632 struct pollfd ufds[2];
633#else
634 fd_set readfds;
635 int maxfd;
636#endif
637
638#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
639 int opt;
640#endif
641
642#ifdef CONFIG_FEATURE_AUTOWIDTH
643 get_terminal_width_height(0, &win_width, &win_height);
644#endif
645
646#ifdef CONFIG_FEATURE_TELNET_TTYPE
647 ttype = getenv("TERM");
648#endif
649
650 memset(&G, 0, sizeof G);
651
652 if (tcgetattr(0, &G.termios_def) < 0)
653 exit(1);
654
655 G.termios_raw = G.termios_def;
656 cfmakeraw(&G.termios_raw);
657
658 if (argc < 2)
659 bb_show_usage();
660
661#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
662 autologin = NULL;
663 while ((opt = getopt(argc, argv, "al:")) != EOF) {
664 switch (opt) {
665 case 'l':
666 autologin = optarg;
667 break;
668 case 'a':
669 autologin = getenv("USER");
670 break;
671 case '?':
672 bb_show_usage();
673 break;
674 }
675 }
676 if (optind < argc) {
677 bb_lookup_host(&s_in, argv[optind++]);
678 s_in.sin_port = bb_lookup_port((optind < argc) ? argv[optind++] :
679 "telnet", "tcp", 23);
680 if (optind < argc)
681 bb_show_usage();
682 } else
683 bb_show_usage();
684#else
685 bb_lookup_host(&s_in, argv[1]);
686 s_in.sin_port = bb_lookup_port((argc == 3) ? argv[2] : "telnet", "tcp", 23);
687#endif
688
689 G.netfd = xconnect(&s_in);
690
691 setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
692
693 signal(SIGINT, fgotsig);
694
695#ifdef USE_POLL
696 ufds[0].fd = 0; ufds[1].fd = G.netfd;
697 ufds[0].events = ufds[1].events = POLLIN;
698#else
699 FD_ZERO(&readfds);
700 FD_SET(0, &readfds);
701 FD_SET(G.netfd, &readfds);
702 maxfd = G.netfd + 1;
703#endif
704
705 while (1)
706 {
707#ifndef USE_POLL
708 fd_set rfds = readfds;
709
710 switch (select(maxfd, &rfds, NULL, NULL, NULL))
711#else
712 switch (poll(ufds, 2, -1))
713#endif
714 {
715 case 0:
716 /* timeout */
717 case -1:
718 /* error, ignore and/or log something, bay go to loop */
719 if (G.gotsig)
720 conescape();
721 else
722 sleep(1);
723 break;
724 default:
725
726#ifdef USE_POLL
727 if (ufds[0].revents) /* well, should check POLLIN, but ... */
728#else
729 if (FD_ISSET(0, &rfds))
730#endif
731 {
732 len = read(0, G.buf, DATABUFSIZE);
733
734 if (len <= 0)
735 doexit(0);
736
737 TRACE(0, ("Read con: %d\n", len));
738
739 handlenetoutput(len);
740 }
741
742#ifdef USE_POLL
743 if (ufds[1].revents) /* well, should check POLLIN, but ... */
744#else
745 if (FD_ISSET(G.netfd, &rfds))
746#endif
747 {
748 len = read(G.netfd, G.buf, DATABUFSIZE);
749
750 if (len <= 0)
751 {
752 WriteCS(1, "Connection closed by foreign host.\r\n");
753 doexit(1);
754 }
755 TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len));
756
757 handlenetinput(len);
758 }
759 }
760 }
761}
762
763/*
764Local Variables:
765c-file-style: "linux"
766c-basic-offset: 4
767tab-width: 4
768End:
769*/
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 @@
1/* $Id: telnetd.c,v 1.13 2004/09/14 17:24:58 bug1 Exp $
2 *
3 * Simple telnet server
4 * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
5 *
6 * This file is distributed under the Gnu Public License (GPL),
7 * please see the file LICENSE for further information.
8 *
9 * ---------------------------------------------------------------------------
10 * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
11 ****************************************************************************
12 *
13 * The telnetd manpage says it all:
14 *
15 * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for
16 * a client, then creating a login process which has the slave side of the
17 * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the
18 * master side of the pseudo-terminal, implementing the telnet protocol and
19 * passing characters between the remote client and the login process.
20 *
21 * Vladimir Oleynik <dzo@simtreas.ru> 2001
22 * Set process group corrections, initial busybox port
23 */
24
25/*#define DEBUG 1 */
26
27#include <sys/time.h>
28#include <sys/socket.h>
29#include <sys/wait.h>
30#include <sys/ioctl.h>
31#include <string.h>
32#include <stdlib.h>
33#include <unistd.h>
34#include <errno.h>
35#include <netinet/in.h>
36#include <fcntl.h>
37#include <stdio.h>
38#include <signal.h>
39#include <termios.h>
40#ifdef DEBUG
41#define TELCMDS
42#define TELOPTS
43#endif
44#include <arpa/telnet.h>
45#include <ctype.h>
46#include <sys/syslog.h>
47
48#include "busybox.h"
49
50#define BUFSIZE 4000
51
52#ifdef CONFIG_LOGIN
53static const char *loginpath = "/bin/login";
54#else
55static const char *loginpath;
56#endif
57static const char *issuefile = "/etc/issue.net";
58
59/* shell name and arguments */
60
61static const char *argv_init[] = {NULL, NULL};
62
63/* structure that describes a session */
64
65struct tsession {
66#ifdef CONFIG_FEATURE_TELNETD_INETD
67 int sockfd_read, sockfd_write, ptyfd;
68#else /* CONFIG_FEATURE_TELNETD_INETD */
69 struct tsession *next;
70 int sockfd, ptyfd;
71#endif /* CONFIG_FEATURE_TELNETD_INETD */
72 int shell_pid;
73 /* two circular buffers */
74 char *buf1, *buf2;
75 int rdidx1, wridx1, size1;
76 int rdidx2, wridx2, size2;
77};
78
79/*
80
81 This is how the buffers are used. The arrows indicate the movement
82 of data.
83
84 +-------+ wridx1++ +------+ rdidx1++ +----------+
85 | | <-------------- | buf1 | <-------------- | |
86 | | size1-- +------+ size1++ | |
87 | pty | | socket |
88 | | rdidx2++ +------+ wridx2++ | |
89 | | --------------> | buf2 | --------------> | |
90 +-------+ size2++ +------+ size2-- +----------+
91
92 Each session has got two buffers.
93
94*/
95
96static int maxfd;
97
98static struct tsession *sessions;
99
100
101/*
102
103 Remove all IAC's from the buffer pointed to by bf (recieved IACs are ignored
104 and must be removed so as to not be interpreted by the terminal). Make an
105 uninterrupted string of characters fit for the terminal. Do this by packing
106 all characters meant for the terminal sequentially towards the end of bf.
107
108 Return a pointer to the beginning of the characters meant for the terminal.
109 and make *num_totty the number of characters that should be sent to
110 the terminal.
111
112 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
113 past (bf + len) then that IAC will be left unprocessed and *processed will be
114 less than len.
115
116 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
117 what is the escape character? We aren't handling that situation here.
118
119 CR-LF ->'s CR mapping is also done here, for convenience
120
121 */
122static char *
123remove_iacs(struct tsession *ts, int *pnum_totty) {
124 unsigned char *ptr0 = ts->buf1 + ts->wridx1;
125 unsigned char *ptr = ptr0;
126 unsigned char *totty = ptr;
127 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
128 int processed;
129 int num_totty;
130
131 while (ptr < end) {
132 if (*ptr != IAC) {
133 int c = *ptr;
134 *totty++ = *ptr++;
135 /* We now map \r\n ==> \r for pragmatic reasons.
136 * Many client implementations send \r\n when
137 * the user hits the CarriageReturn key.
138 */
139 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
140 ptr++;
141 }
142 else {
143 /*
144 * TELOPT_NAWS support!
145 */
146 if ((ptr+2) >= end) {
147 /* only the beginning of the IAC is in the
148 buffer we were asked to process, we can't
149 process this char. */
150 break;
151 }
152
153 /*
154 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
155 */
156 else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
157 struct winsize ws;
158 if ((ptr+8) >= end)
159 break; /* incomplete, can't process */
160 ws.ws_col = (ptr[3] << 8) | ptr[4];
161 ws.ws_row = (ptr[5] << 8) | ptr[6];
162 (void) ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
163 ptr += 9;
164 }
165 else {
166 /* skip 3-byte IAC non-SB cmd */
167#ifdef DEBUG
168 fprintf(stderr, "Ignoring IAC %s,%s\n",
169 TELCMD(*(ptr+1)), TELOPT(*(ptr+2)));
170#endif
171 ptr += 3;
172 }
173 }
174 }
175
176 processed = ptr - ptr0;
177 num_totty = totty - ptr0;
178 /* the difference between processed and num_to tty
179 is all the iacs we removed from the stream.
180 Adjust buf1 accordingly. */
181 ts->wridx1 += processed - num_totty;
182 ts->size1 -= processed - num_totty;
183 *pnum_totty = num_totty;
184 /* move the chars meant for the terminal towards the end of the
185 buffer. */
186 return memmove(ptr - num_totty, ptr0, num_totty);
187}
188
189
190static int
191getpty(char *line)
192{
193 int p;
194#ifdef CONFIG_FEATURE_DEVPTS
195 p = open("/dev/ptmx", 2);
196 if (p > 0) {
197 grantpt(p);
198 unlockpt(p);
199 strcpy(line, ptsname(p));
200 return(p);
201 }
202#else
203 struct stat stb;
204 int i;
205 int j;
206
207 strcpy(line, "/dev/ptyXX");
208
209 for (i = 0; i < 16; i++) {
210 line[8] = "pqrstuvwxyzabcde"[i];
211 line[9] = '0';
212 if (stat(line, &stb) < 0) {
213 continue;
214 }
215 for (j = 0; j < 16; j++) {
216 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
217 if ((p = open(line, O_RDWR | O_NOCTTY)) >= 0) {
218 line[5] = 't';
219 return p;
220 }
221 }
222 }
223#endif /* CONFIG_FEATURE_DEVPTS */
224 return -1;
225}
226
227
228static void
229send_iac(struct tsession *ts, unsigned char command, int option)
230{
231 /* We rely on that there is space in the buffer for now. */
232 char *b = ts->buf2 + ts->rdidx2;
233 *b++ = IAC;
234 *b++ = command;
235 *b++ = option;
236 ts->rdidx2 += 3;
237 ts->size2 += 3;
238}
239
240
241static struct tsession *
242#ifdef CONFIG_FEATURE_TELNETD_INETD
243make_new_session(void)
244#else /* CONFIG_FEATURE_TELNETD_INETD */
245make_new_session(int sockfd)
246#endif /* CONFIG_FEATURE_TELNETD_INETD */
247{
248 struct termios termbuf;
249 int pty, pid;
250 char tty_name[32];
251 struct tsession *ts = malloc(sizeof(struct tsession) + BUFSIZE * 2);
252
253 ts->buf1 = (char *)(&ts[1]);
254 ts->buf2 = ts->buf1 + BUFSIZE;
255
256#ifdef CONFIG_FEATURE_TELNETD_INETD
257 ts->sockfd_read = 0;
258 ts->sockfd_write = 1;
259#else /* CONFIG_FEATURE_TELNETD_INETD */
260 ts->sockfd = sockfd;
261#endif /* CONFIG_FEATURE_TELNETD_INETD */
262
263 ts->rdidx1 = ts->wridx1 = ts->size1 = 0;
264 ts->rdidx2 = ts->wridx2 = ts->size2 = 0;
265
266 /* Got a new connection, set up a tty and spawn a shell. */
267
268 pty = getpty(tty_name);
269
270 if (pty < 0) {
271 syslog(LOG_ERR, "All network ports in use!");
272 return 0;
273 }
274
275 if (pty > maxfd)
276 maxfd = pty;
277
278 ts->ptyfd = pty;
279
280 /* Make the telnet client understand we will echo characters so it
281 * should not do it locally. We don't tell the client to run linemode,
282 * because we want to handle line editing and tab completion and other
283 * stuff that requires char-by-char support.
284 */
285
286 send_iac(ts, DO, TELOPT_ECHO);
287 send_iac(ts, DO, TELOPT_NAWS);
288 send_iac(ts, DO, TELOPT_LFLOW);
289 send_iac(ts, WILL, TELOPT_ECHO);
290 send_iac(ts, WILL, TELOPT_SGA);
291
292
293 if ((pid = fork()) < 0) {
294 syslog(LOG_ERR, "Can`t forking");
295 }
296 if (pid == 0) {
297 /* In child, open the child's side of the tty. */
298 int i;
299
300 for(i = 0; i <= maxfd; i++)
301 close(i);
302 /* make new process group */
303 setsid();
304
305 if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) {
306 syslog(LOG_ERR, "Could not open tty");
307 exit(1);
308 }
309 dup(0);
310 dup(0);
311
312 tcsetpgrp(0, getpid());
313
314 /* The pseudo-terminal allocated to the client is configured to operate in
315 * cooked mode, and with XTABS CRMOD enabled (see tty(4)).
316 */
317
318 tcgetattr(0, &termbuf);
319 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
320 termbuf.c_oflag |= ONLCR|XTABS;
321 termbuf.c_iflag |= ICRNL;
322 termbuf.c_iflag &= ~IXOFF;
323 /*termbuf.c_lflag &= ~ICANON;*/
324 tcsetattr(0, TCSANOW, &termbuf);
325
326 print_login_issue(issuefile, NULL);
327
328 /* exec shell, with correct argv and env */
329 execv(loginpath, (char *const *)argv_init);
330
331 /* NOT REACHED */
332 syslog(LOG_ERR, "execv error");
333 exit(1);
334 }
335
336 ts->shell_pid = pid;
337
338 return ts;
339}
340
341#ifndef CONFIG_FEATURE_TELNETD_INETD
342static void
343free_session(struct tsession *ts)
344{
345 struct tsession *t = sessions;
346
347 /* Unlink this telnet session from the session list. */
348 if(t == ts)
349 sessions = ts->next;
350 else {
351 while(t->next != ts)
352 t = t->next;
353 t->next = ts->next;
354 }
355
356 kill(ts->shell_pid, SIGKILL);
357
358 wait4(ts->shell_pid, NULL, 0, NULL);
359
360 close(ts->ptyfd);
361 close(ts->sockfd);
362
363 if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
364 maxfd--;
365 if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
366 maxfd--;
367
368 free(ts);
369}
370#endif /* CONFIG_FEATURE_TELNETD_INETD */
371
372int
373telnetd_main(int argc, char **argv)
374{
375#ifndef CONFIG_FEATURE_TELNETD_INETD
376 struct sockaddr_in sa;
377 int master_fd;
378#endif /* CONFIG_FEATURE_TELNETD_INETD */
379 fd_set rdfdset, wrfdset;
380 int selret;
381#ifndef CONFIG_FEATURE_TELNETD_INETD
382 int on = 1;
383 int portnbr = 23;
384#endif /* CONFIG_FEATURE_TELNETD_INETD */
385 int c;
386 static const char options[] =
387#ifdef CONFIG_FEATURE_TELNETD_INETD
388 "f:l:";
389#else /* CONFIG_EATURE_TELNETD_INETD */
390 "f:l:p:";
391#endif /* CONFIG_FEATURE_TELNETD_INETD */
392 int maxlen, w, r;
393
394#ifndef CONFIG_LOGIN
395 loginpath = DEFAULT_SHELL;
396#endif
397
398 for (;;) {
399 c = getopt( argc, argv, options);
400 if (c == EOF) break;
401 switch (c) {
402 case 'f':
403 issuefile = optarg;
404 break;
405 case 'l':
406 loginpath = optarg;
407 break;
408#ifndef CONFIG_FEATURE_TELNETD_INETD
409 case 'p':
410 portnbr = atoi(optarg);
411 break;
412#endif /* CONFIG_FEATURE_TELNETD_INETD */
413 default:
414 bb_show_usage();
415 }
416 }
417
418 if (access(loginpath, X_OK) < 0) {
419 bb_error_msg_and_die ("'%s' unavailable.", loginpath);
420 }
421
422 argv_init[0] = loginpath;
423
424 openlog(bb_applet_name, 0, LOG_USER);
425
426#ifdef CONFIG_FEATURE_TELNETD_INETD
427 maxfd = 1;
428 sessions = make_new_session();
429#else /* CONFIG_EATURE_TELNETD_INETD */
430 sessions = 0;
431
432 /* Grab a TCP socket. */
433
434 master_fd = socket(AF_INET, SOCK_STREAM, 0);
435 if (master_fd < 0) {
436 bb_perror_msg_and_die("socket");
437 }
438 (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
439
440 /* Set it to listen to specified port. */
441
442 memset((void *)&sa, 0, sizeof(sa));
443 sa.sin_family = AF_INET;
444 sa.sin_port = htons(portnbr);
445
446 if (bind(master_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
447 bb_perror_msg_and_die("bind");
448 }
449
450 if (listen(master_fd, 1) < 0) {
451 bb_perror_msg_and_die("listen");
452 }
453
454 if (daemon(0, 0) < 0)
455 bb_perror_msg_and_die("daemon");
456
457
458 maxfd = master_fd;
459#endif /* CONFIG_FEATURE_TELNETD_INETD */
460
461 do {
462 struct tsession *ts;
463
464 FD_ZERO(&rdfdset);
465 FD_ZERO(&wrfdset);
466
467 /* select on the master socket, all telnet sockets and their
468 * ptys if there is room in their respective session buffers.
469 */
470
471#ifndef CONFIG_FEATURE_TELNETD_INETD
472 FD_SET(master_fd, &rdfdset);
473#endif /* CONFIG_FEATURE_TELNETD_INETD */
474
475 ts = sessions;
476#ifndef CONFIG_FEATURE_TELNETD_INETD
477 while (ts) {
478#endif /* CONFIG_FEATURE_TELNETD_INETD */
479 /* buf1 is used from socket to pty
480 * buf2 is used from pty to socket
481 */
482 if (ts->size1 > 0) {
483 FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */
484 }
485 if (ts->size1 < BUFSIZE) {
486#ifdef CONFIG_FEATURE_TELNETD_INETD
487 FD_SET(ts->sockfd_read, &rdfdset); /* can read from socket */
488#else /* CONFIG_FEATURE_TELNETD_INETD */
489 FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
490#endif /* CONFIG_FEATURE_TELNETD_INETD */
491 }
492 if (ts->size2 > 0) {
493#ifdef CONFIG_FEATURE_TELNETD_INETD
494 FD_SET(ts->sockfd_write, &wrfdset); /* can write to socket */
495#else /* CONFIG_FEATURE_TELNETD_INETD */
496 FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
497#endif /* CONFIG_FEATURE_TELNETD_INETD */
498 }
499 if (ts->size2 < BUFSIZE) {
500 FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */
501 }
502#ifndef CONFIG_FEATURE_TELNETD_INETD
503 ts = ts->next;
504 }
505#endif /* CONFIG_FEATURE_TELNETD_INETD */
506
507 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
508
509 if (!selret)
510 break;
511
512#ifndef CONFIG_FEATURE_TELNETD_INETD
513 /* First check for and accept new sessions. */
514 if (FD_ISSET(master_fd, &rdfdset)) {
515 int fd, salen;
516
517 salen = sizeof(sa);
518 if ((fd = accept(master_fd, (struct sockaddr *)&sa,
519 &salen)) < 0) {
520 continue;
521 } else {
522 /* Create a new session and link it into
523 our active list. */
524 struct tsession *new_ts = make_new_session(fd);
525 if (new_ts) {
526 new_ts->next = sessions;
527 sessions = new_ts;
528 if (fd > maxfd)
529 maxfd = fd;
530 } else {
531 close(fd);
532 }
533 }
534 }
535
536 /* Then check for data tunneling. */
537
538 ts = sessions;
539 while (ts) { /* For all sessions... */
540#endif /* CONFIG_FEATURE_TELNETD_INETD */
541#ifndef CONFIG_FEATURE_TELNETD_INETD
542 struct tsession *next = ts->next; /* in case we free ts. */
543#endif /* CONFIG_FEATURE_TELNETD_INETD */
544
545 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
546 int num_totty;
547 char *ptr;
548 /* Write to pty from buffer 1. */
549
550 ptr = remove_iacs(ts, &num_totty);
551
552 w = write(ts->ptyfd, ptr, num_totty);
553 if (w < 0) {
554#ifdef CONFIG_FEATURE_TELNETD_INETD
555 exit(0);
556#else /* CONFIG_FEATURE_TELNETD_INETD */
557 free_session(ts);
558 ts = next;
559 continue;
560#endif /* CONFIG_FEATURE_TELNETD_INETD */
561 }
562 ts->wridx1 += w;
563 ts->size1 -= w;
564 if (ts->wridx1 == BUFSIZE)
565 ts->wridx1 = 0;
566 }
567
568#ifdef CONFIG_FEATURE_TELNETD_INETD
569 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
570#else /* CONFIG_FEATURE_TELNETD_INETD */
571 if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
572#endif /* CONFIG_FEATURE_TELNETD_INETD */
573 /* Write to socket from buffer 2. */
574 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
575#ifdef CONFIG_FEATURE_TELNETD_INETD
576 w = write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
577 if (w < 0)
578 exit(0);
579#else /* CONFIG_FEATURE_TELNETD_INETD */
580 w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
581 if (w < 0) {
582 free_session(ts);
583 ts = next;
584 continue;
585 }
586#endif /* CONFIG_FEATURE_TELNETD_INETD */
587 ts->wridx2 += w;
588 ts->size2 -= w;
589 if (ts->wridx2 == BUFSIZE)
590 ts->wridx2 = 0;
591 }
592
593#ifdef CONFIG_FEATURE_TELNETD_INETD
594 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
595#else /* CONFIG_FEATURE_TELNETD_INETD */
596 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) {
597#endif /* CONFIG_FEATURE_TELNETD_INETD */
598 /* Read from socket to buffer 1. */
599 maxlen = MIN(BUFSIZE - ts->rdidx1,
600 BUFSIZE - ts->size1);
601#ifdef CONFIG_FEATURE_TELNETD_INETD
602 r = read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
603 if (!r || (r < 0 && errno != EINTR))
604 exit(0);
605#else /* CONFIG_FEATURE_TELNETD_INETD */
606 r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
607 if (!r || (r < 0 && errno != EINTR)) {
608 free_session(ts);
609 ts = next;
610 continue;
611 }
612#endif /* CONFIG_FEATURE_TELNETD_INETD */
613 if(!*(ts->buf1 + ts->rdidx1 + r - 1)) {
614 r--;
615 if(!r)
616 continue;
617 }
618 ts->rdidx1 += r;
619 ts->size1 += r;
620 if (ts->rdidx1 == BUFSIZE)
621 ts->rdidx1 = 0;
622 }
623
624 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
625 /* Read from pty to buffer 2. */
626 maxlen = MIN(BUFSIZE - ts->rdidx2,
627 BUFSIZE - ts->size2);
628 r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
629 if (!r || (r < 0 && errno != EINTR)) {
630#ifdef CONFIG_FEATURE_TELNETD_INETD
631 exit(0);
632#else /* CONFIG_FEATURE_TELNETD_INETD */
633 free_session(ts);
634 ts = next;
635 continue;
636#endif /* CONFIG_FEATURE_TELNETD_INETD */
637 }
638 ts->rdidx2 += r;
639 ts->size2 += r;
640 if (ts->rdidx2 == BUFSIZE)
641 ts->rdidx2 = 0;
642 }
643
644 if (ts->size1 == 0) {
645 ts->rdidx1 = 0;
646 ts->wridx1 = 0;
647 }
648 if (ts->size2 == 0) {
649 ts->rdidx2 = 0;
650 ts->wridx2 = 0;
651 }
652#ifndef CONFIG_FEATURE_TELNETD_INETD
653 ts = next;
654 }
655#endif /* CONFIG_FEATURE_TELNETD_INETD */
656
657 } while (1);
658
659 return 0;
660}
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 @@
1/* ------------------------------------------------------------------------- */
2/* tftp.c */
3/* */
4/* A simple tftp client for busybox. */
5/* Tries to follow RFC1350. */
6/* Only "octet" mode supported. */
7/* Optional blocksize negotiation (RFC2347 + RFC2348) */
8/* */
9/* Copyright (C) 2001 Magnus Damm <damm@opensource.se> */
10/* */
11/* Parts of the code based on: */
12/* */
13/* atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca> */
14/* and Remi Lefebvre <remi@debian.org> */
15/* */
16/* utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> */
17/* */
18/* This program is free software; you can redistribute it and/or modify */
19/* it under the terms of the GNU General Public License as published by */
20/* the Free Software Foundation; either version 2 of the License, or */
21/* (at your option) any later version. */
22/* */
23/* This program is distributed in the hope that it will be useful, */
24/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
25/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
26/* General Public License for more details. */
27/* */
28/* You should have received a copy of the GNU General Public License */
29/* along with this program; if not, write to the Free Software */
30/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
31/* */
32/* ------------------------------------------------------------------------- */
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <sys/types.h>
38#include <sys/socket.h>
39#include <sys/time.h>
40#include <sys/stat.h>
41#include <netdb.h>
42#include <netinet/in.h>
43#include <arpa/inet.h>
44#include <unistd.h>
45#include <fcntl.h>
46
47#include "busybox.h"
48
49//#define CONFIG_FEATURE_TFTP_DEBUG
50
51#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
52#define TFTP_TIMEOUT 5 /* seconds */
53
54/* opcodes we support */
55
56#define TFTP_RRQ 1
57#define TFTP_WRQ 2
58#define TFTP_DATA 3
59#define TFTP_ACK 4
60#define TFTP_ERROR 5
61#define TFTP_OACK 6
62
63static const char *tftp_bb_error_msg[] = {
64 "Undefined error",
65 "File not found",
66 "Access violation",
67 "Disk full or allocation error",
68 "Illegal TFTP operation",
69 "Unknown transfer ID",
70 "File already exists",
71 "No such user"
72};
73
74const int tftp_cmd_get = 1;
75const int tftp_cmd_put = 2;
76
77#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
78
79static int tftp_blocksize_check(int blocksize, int bufsize)
80{
81 /* Check if the blocksize is valid:
82 * RFC2348 says between 8 and 65464,
83 * but our implementation makes it impossible
84 * to use blocksizes smaller than 22 octets.
85 */
86
87 if ((bufsize && (blocksize > bufsize)) ||
88 (blocksize < 8) || (blocksize > 65464)) {
89 bb_error_msg("bad blocksize");
90 return 0;
91 }
92
93 return blocksize;
94}
95
96static char *tftp_option_get(char *buf, int len, char *option)
97{
98 int opt_val = 0;
99 int opt_found = 0;
100 int k;
101
102 while (len > 0) {
103
104 /* Make sure the options are terminated correctly */
105
106 for (k = 0; k < len; k++) {
107 if (buf[k] == '\0') {
108 break;
109 }
110 }
111
112 if (k >= len) {
113 break;
114 }
115
116 if (opt_val == 0) {
117 if (strcasecmp(buf, option) == 0) {
118 opt_found = 1;
119 }
120 }
121 else {
122 if (opt_found) {
123 return buf;
124 }
125 }
126
127 k++;
128
129 buf += k;
130 len -= k;
131
132 opt_val ^= 1;
133 }
134
135 return NULL;
136}
137
138#endif
139
140static inline int tftp(const int cmd, const struct hostent *host,
141 const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize)
142{
143 const int cmd_get = cmd & tftp_cmd_get;
144 const int cmd_put = cmd & tftp_cmd_put;
145 const int bb_tftp_num_retries = 5;
146
147 struct sockaddr_in sa;
148 struct sockaddr_in from;
149 struct timeval tv;
150 socklen_t fromlen;
151 fd_set rfds;
152 char *cp;
153 unsigned short tmp;
154 int socketfd;
155 int len;
156 int opcode = 0;
157 int finished = 0;
158 int timeout = bb_tftp_num_retries;
159 unsigned short block_nr = 1;
160
161#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
162 int want_option_ack = 0;
163#endif
164
165 /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
166 * size varies meaning BUFFERS_GO_ON_STACK would fail */
167 char *buf=xmalloc(tftp_bufsize + 4);
168
169 tftp_bufsize += 4;
170
171 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
172 bb_perror_msg("socket");
173 return EXIT_FAILURE;
174 }
175
176 len = sizeof(sa);
177
178 memset(&sa, 0, len);
179 bind(socketfd, (struct sockaddr *)&sa, len);
180
181 sa.sin_family = host->h_addrtype;
182 sa.sin_port = port;
183 memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
184 sizeof(sa.sin_addr));
185
186 /* build opcode */
187
188 if (cmd_get) {
189 opcode = TFTP_RRQ;
190 }
191
192 if (cmd_put) {
193 opcode = TFTP_WRQ;
194 }
195
196 while (1) {
197
198 cp = buf;
199
200 /* first create the opcode part */
201
202 *((unsigned short *) cp) = htons(opcode);
203
204 cp += 2;
205
206 /* add filename and mode */
207
208 if ((cmd_get && (opcode == TFTP_RRQ)) ||
209 (cmd_put && (opcode == TFTP_WRQ))) {
210 int too_long = 0;
211
212 /* see if the filename fits into buf */
213 /* and fill in packet */
214
215 len = strlen(remotefile) + 1;
216
217 if ((cp + len) >= &buf[tftp_bufsize - 1]) {
218 too_long = 1;
219 }
220 else {
221 safe_strncpy(cp, remotefile, len);
222 cp += len;
223 }
224
225 if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
226 bb_error_msg("too long remote-filename");
227 break;
228 }
229
230 /* add "mode" part of the package */
231
232 memcpy(cp, "octet", 6);
233 cp += 6;
234
235#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
236
237 len = tftp_bufsize - 4; /* data block size */
238
239 if (len != TFTP_BLOCKSIZE_DEFAULT) {
240
241 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
242 bb_error_msg("too long remote-filename");
243 break;
244 }
245
246 /* add "blksize" + number of blocks */
247
248 memcpy(cp, "blksize", 8);
249 cp += 8;
250
251 cp += snprintf(cp, 6, "%d", len) + 1;
252
253 want_option_ack = 1;
254 }
255#endif
256 }
257
258 /* add ack and data */
259
260 if ((cmd_get && (opcode == TFTP_ACK)) ||
261 (cmd_put && (opcode == TFTP_DATA))) {
262
263 *((unsigned short *) cp) = htons(block_nr);
264
265 cp += 2;
266
267 block_nr++;
268
269 if (cmd_put && (opcode == TFTP_DATA)) {
270 len = bb_full_read(localfd, cp, tftp_bufsize - 4);
271
272 if (len < 0) {
273 bb_perror_msg("read");
274 break;
275 }
276
277 if (len != (tftp_bufsize - 4)) {
278 finished++;
279 }
280
281 cp += len;
282 }
283 }
284
285
286 /* send packet */
287
288
289 timeout = bb_tftp_num_retries; /* re-initialize */
290 do {
291
292 len = cp - buf;
293
294#ifdef CONFIG_FEATURE_TFTP_DEBUG
295 fprintf(stderr, "sending %u bytes\n", len);
296 for (cp = buf; cp < &buf[len]; cp++)
297 fprintf(stderr, "%02x ", (unsigned char)*cp);
298 fprintf(stderr, "\n");
299#endif
300 if (sendto(socketfd, buf, len, 0,
301 (struct sockaddr *) &sa, sizeof(sa)) < 0) {
302 bb_perror_msg("send");
303 len = -1;
304 break;
305 }
306
307
308 if (finished && (opcode == TFTP_ACK)) {
309 break;
310 }
311
312 /* receive packet */
313
314 memset(&from, 0, sizeof(from));
315 fromlen = sizeof(from);
316
317 tv.tv_sec = TFTP_TIMEOUT;
318 tv.tv_usec = 0;
319
320 FD_ZERO(&rfds);
321 FD_SET(socketfd, &rfds);
322
323 switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
324 case 1:
325 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
326 (struct sockaddr *) &from, &fromlen);
327
328 if (len < 0) {
329 bb_perror_msg("recvfrom");
330 break;
331 }
332
333 timeout = 0;
334
335 if (sa.sin_port == port) {
336 sa.sin_port = from.sin_port;
337 }
338 if (sa.sin_port == from.sin_port) {
339 break;
340 }
341
342 /* fall-through for bad packets! */
343 /* discard the packet - treat as timeout */
344 timeout = bb_tftp_num_retries;
345
346 case 0:
347 bb_error_msg("timeout");
348
349 timeout--;
350 if (timeout == 0) {
351 len = -1;
352 bb_error_msg("last timeout");
353 }
354 break;
355
356 default:
357 bb_perror_msg("select");
358 len = -1;
359 }
360
361 } while (timeout && (len >= 0));
362
363 if ((finished) || (len < 0)) {
364 break;
365 }
366
367 /* process received packet */
368
369
370 opcode = ntohs(*((unsigned short *) buf));
371 tmp = ntohs(*((unsigned short *) &buf[2]));
372
373#ifdef CONFIG_FEATURE_TFTP_DEBUG
374 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
375#endif
376
377 if (opcode == TFTP_ERROR) {
378 char *msg = NULL;
379
380 if (buf[4] != '\0') {
381 msg = &buf[4];
382 buf[tftp_bufsize - 1] = '\0';
383 } else if (tmp < (sizeof(tftp_bb_error_msg)
384 / sizeof(char *))) {
385
386 msg = (char *) tftp_bb_error_msg[tmp];
387 }
388
389 if (msg) {
390 bb_error_msg("server says: %s", msg);
391 }
392
393 break;
394 }
395
396#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
397 if (want_option_ack) {
398
399 want_option_ack = 0;
400
401 if (opcode == TFTP_OACK) {
402
403 /* server seems to support options */
404
405 char *res;
406
407 res = tftp_option_get(&buf[2], len-2,
408 "blksize");
409
410 if (res) {
411 int blksize = atoi(res);
412
413 if (tftp_blocksize_check(blksize,
414 tftp_bufsize - 4)) {
415
416 if (cmd_put) {
417 opcode = TFTP_DATA;
418 }
419 else {
420 opcode = TFTP_ACK;
421 }
422#ifdef CONFIG_FEATURE_TFTP_DEBUG
423 fprintf(stderr, "using blksize %u\n", blksize);
424#endif
425 tftp_bufsize = blksize + 4;
426 block_nr = 0;
427 continue;
428 }
429 }
430 /* FIXME:
431 * we should send ERROR 8 */
432 bb_error_msg("bad server option");
433 break;
434 }
435
436 bb_error_msg("warning: blksize not supported by server"
437 " - reverting to 512");
438
439 tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
440 }
441#endif
442
443 if (cmd_get && (opcode == TFTP_DATA)) {
444
445 if (tmp == block_nr) {
446
447 len = bb_full_write(localfd, &buf[4], len - 4);
448
449 if (len < 0) {
450 bb_perror_msg("write");
451 break;
452 }
453
454 if (len != (tftp_bufsize - 4)) {
455 finished++;
456 }
457
458 opcode = TFTP_ACK;
459 continue;
460 }
461 }
462
463 if (cmd_put && (opcode == TFTP_ACK)) {
464
465 if (tmp == (unsigned short)(block_nr - 1)) {
466 if (finished) {
467 break;
468 }
469
470 opcode = TFTP_DATA;
471 continue;
472 }
473 }
474 }
475
476#ifdef CONFIG_FEATURE_CLEAN_UP
477 close(socketfd);
478
479 free(buf);
480#endif
481
482 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
483}
484
485int tftp_main(int argc, char **argv)
486{
487 struct hostent *host = NULL;
488 const char *localfile = NULL;
489 const char *remotefile = NULL;
490 int port;
491 int cmd = 0;
492 int fd = -1;
493 int flags = 0;
494 int opt;
495 int result;
496 int blocksize = TFTP_BLOCKSIZE_DEFAULT;
497
498 /* figure out what to pass to getopt */
499
500#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
501#define BS "b:"
502#else
503#define BS
504#endif
505
506#ifdef CONFIG_FEATURE_TFTP_GET
507#define GET "g"
508#else
509#define GET
510#endif
511
512#ifdef CONFIG_FEATURE_TFTP_PUT
513#define PUT "p"
514#else
515#define PUT
516#endif
517
518 while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) {
519 switch (opt) {
520#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
521 case 'b':
522 blocksize = atoi(optarg);
523 if (!tftp_blocksize_check(blocksize, 0)) {
524 return EXIT_FAILURE;
525 }
526 break;
527#endif
528#ifdef CONFIG_FEATURE_TFTP_GET
529 case 'g':
530 cmd = tftp_cmd_get;
531 flags = O_WRONLY | O_CREAT | O_TRUNC;
532 break;
533#endif
534#ifdef CONFIG_FEATURE_TFTP_PUT
535 case 'p':
536 cmd = tftp_cmd_put;
537 flags = O_RDONLY;
538 break;
539#endif
540 case 'l':
541 localfile = optarg;
542 break;
543 case 'r':
544 remotefile = optarg;
545 break;
546 }
547 }
548
549 if ((cmd == 0) || (optind == argc)) {
550 bb_show_usage();
551 }
552 if(localfile && strcmp(localfile, "-") == 0) {
553 fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
554 }
555 if(localfile == NULL)
556 localfile = remotefile;
557 if(remotefile == NULL)
558 remotefile = localfile;
559 if (fd==-1) {
560 fd = open(localfile, flags, 0644);
561 }
562 if (fd < 0) {
563 bb_perror_msg_and_die("local file");
564 }
565
566 host = xgethostbyname(argv[optind]);
567 port = bb_lookup_port(argv[optind + 1], "udp", 69);
568
569#ifdef CONFIG_FEATURE_TFTP_DEBUG
570 fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
571 "localfile \"%s\".\n",
572 inet_ntoa(*((struct in_addr *) host->h_addr)),
573 remotefile, localfile);
574#endif
575
576 result = tftp(cmd, host, remotefile, fd, port, blocksize);
577
578#ifdef CONFIG_FEATURE_CLEAN_UP
579 if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
580 close(fd);
581 }
582#endif
583 return(result);
584}
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 @@
1/*-
2 * Copyright (c) 1990, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Van Jacobson.
7 *
8 * Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34/*
35 * traceroute host - trace the route ip packets follow going to "host".
36 * Notes
37 * -----
38 * This program must be run by root or be setuid. (I suggest that
39 * you *don't* make it setuid -- casual use could result in a lot
40 * of unnecessary traffic on our poor, congested nets.)
41 *
42 * I stole the idea for this program from Steve Deering. Since
43 * the first release, I've learned that had I attended the right
44 * IETF working group meetings, I also could have stolen it from Guy
45 * Almes or Matt Mathis. I don't know (or care) who came up with
46 * the idea first. I envy the originators' perspicacity and I'm
47 * glad they didn't keep the idea a secret.
48 *
49 * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
50 * enhancements to the original distribution.
51 *
52 * I've hacked up a round-trip-route version of this that works by
53 * sending a loose-source-routed udp datagram through the destination
54 * back to yourself. Unfortunately, SO many gateways botch source
55 * routing, the thing is almost worthless. Maybe one day...
56 *
57 * -- Van Jacobson (van@helios.ee.lbl.gov)
58 * Tue Dec 20 03:50:13 PST 1988
59 */
60
61#undef CONFIG_FEATURE_TRACEROUTE_VERBOSE
62//#define CONFIG_FEATURE_TRACEROUTE_VERBOSE
63#undef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG /* not in documentation man */
64
65#include <stdio.h>
66#include <errno.h>
67#include <stdlib.h>
68#include <string.h>
69#include <unistd.h>
70#include <sys/time.h>
71#include "inet_common.h"
72#include <netdb.h>
73#include <endian.h>
74#include <netinet/udp.h>
75#include <netinet/ip.h>
76#include <netinet/ip_icmp.h>
77
78
79#define MAXPACKET 65535 /* max ip packet size */
80#ifndef MAXHOSTNAMELEN
81#define MAXHOSTNAMELEN 64
82#endif
83
84/*
85 * format of a (udp) probe packet.
86 */
87struct opacket {
88 struct ip ip;
89 struct udphdr udp;
90 u_char seq; /* sequence number of this packet */
91 u_char ttl; /* ttl packet left with */
92 struct timeval tv; /* time packet left */
93};
94
95/*
96 * Definitions for internet protocol version 4.
97 * Per RFC 791, September 1981.
98 */
99#define IPVERSION 4
100
101
102#include "busybox.h"
103
104static u_char packet[512]; /* last inbound (icmp) packet */
105static struct opacket *outpacket; /* last output (udp) packet */
106
107static int s; /* receive (icmp) socket file descriptor */
108static int sndsock; /* send (udp) socket file descriptor */
109
110static struct sockaddr whereto; /* Who to try to reach */
111static int datalen; /* How much data */
112
113static char *hostname;
114
115static int max_ttl = 30;
116static u_short ident;
117static u_short port = 32768+666; /* start udp dest port # for probe packets */
118
119#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
120static int verbose;
121#endif
122static int waittime = 5; /* time to wait for response (in seconds) */
123static int nflag; /* print addresses numerically */
124
125/*
126 * Construct an Internet address representation.
127 * If the nflag has been supplied, give
128 * numeric value, otherwise try for symbolic name.
129 */
130static inline void
131inetname(struct sockaddr_in *from)
132{
133 char *cp;
134 static char domain[MAXHOSTNAMELEN + 1];
135 char name[MAXHOSTNAMELEN + 1];
136 static int first = 1;
137 const char *ina;
138
139 if (first && !nflag) {
140 first = 0;
141 if (getdomainname(domain, MAXHOSTNAMELEN) != 0)
142 domain[0] = 0;
143 }
144 cp = 0;
145 if (!nflag && from->sin_addr.s_addr != INADDR_ANY) {
146 if(INET_rresolve(name, sizeof(name), from, 0x4000, 0xffffffff) >= 0) {
147 if ((cp = strchr(name, '.')) &&
148 !strcmp(cp + 1, domain))
149 *cp = 0;
150 cp = (char *)name;
151 }
152 }
153 ina = inet_ntoa(from->sin_addr);
154 if (nflag)
155 printf(" %s", ina);
156 else
157 printf(" %s (%s)", (cp ? cp : ina), ina);
158}
159
160static inline void
161print(u_char *buf, int cc, struct sockaddr_in *from)
162{
163 struct ip *ip;
164 int hlen;
165
166 ip = (struct ip *) buf;
167 hlen = ip->ip_hl << 2;
168 cc -= hlen;
169
170 inetname(from);
171#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
172 if (verbose)
173 printf (" %d bytes to %s", cc, inet_ntoa (ip->ip_dst));
174#endif
175}
176
177static inline double
178deltaT(struct timeval *t1p, struct timeval *t2p)
179{
180 double dt;
181
182 dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
183 (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
184 return (dt);
185}
186
187static inline int
188wait_for_reply(int sock, struct sockaddr_in *from, int reset_timer)
189{
190 fd_set fds;
191 static struct timeval wait;
192 int cc = 0;
193 int fromlen = sizeof (*from);
194
195 FD_ZERO(&fds);
196 FD_SET(sock, &fds);
197 if (reset_timer) {
198 /*
199 * traceroute could hang if someone else has a ping
200 * running and our ICMP reply gets dropped but we don't
201 * realize it because we keep waking up to handle those
202 * other ICMP packets that keep coming in. To fix this,
203 * "reset_timer" will only be true if the last packet that
204 * came in was for us or if this is the first time we're
205 * waiting for a reply since sending out a probe. Note
206 * that this takes advantage of the select() feature on
207 * Linux where the remaining timeout is written to the
208 * struct timeval area.
209 */
210 wait.tv_sec = waittime;
211 wait.tv_usec = 0;
212 }
213
214 if (select(sock+1, &fds, (fd_set *)0, (fd_set *)0, &wait) > 0)
215 cc=recvfrom(s, (char *)packet, sizeof(packet), 0,
216 (struct sockaddr *)from, &fromlen);
217
218 return(cc);
219}
220
221#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
222/*
223 * Convert an ICMP "type" field to a printable string.
224 */
225static inline const char *
226pr_type(u_char t)
227{
228 static const char * const ttab[] = {
229 "Echo Reply", "ICMP 1", "ICMP 2", "Dest Unreachable",
230 "Source Quench", "Redirect", "ICMP 6", "ICMP 7",
231 "Echo", "ICMP 9", "ICMP 10", "Time Exceeded",
232 "Param Problem", "Timestamp", "Timestamp Reply", "Info Request",
233 "Info Reply"
234 };
235
236 if(t > 16)
237 return("OUT-OF-RANGE");
238
239 return(ttab[t]);
240}
241#endif
242
243static inline int
244packet_ok(u_char *buf, int cc, struct sockaddr_in *from, int seq)
245{
246 struct icmp *icp;
247 u_char type, code;
248 int hlen;
249 struct ip *ip;
250
251 ip = (struct ip *) buf;
252 hlen = ip->ip_hl << 2;
253 if (cc < hlen + ICMP_MINLEN) {
254#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
255 if (verbose)
256 printf("packet too short (%d bytes) from %s\n", cc,
257 inet_ntoa(from->sin_addr));
258#endif
259 return (0);
260 }
261 cc -= hlen;
262 icp = (struct icmp *)(buf + hlen);
263 type = icp->icmp_type; code = icp->icmp_code;
264 if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) ||
265 type == ICMP_UNREACH) {
266 struct ip *hip;
267 struct udphdr *up;
268
269 hip = &icp->icmp_ip;
270 hlen = hip->ip_hl << 2;
271 up = (struct udphdr *)((u_char *)hip + hlen);
272 if (hlen + 12 <= cc && hip->ip_p == IPPROTO_UDP &&
273 up->source == htons(ident) &&
274 up->dest == htons(port+seq))
275 return (type == ICMP_TIMXCEED? -1 : code+1);
276 }
277#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
278 if (verbose) {
279 int i;
280 u_long *lp = (u_long *)&icp->icmp_ip;
281
282 printf("\n%d bytes from %s to %s: icmp type %d (%s) code %d\n",
283 cc, inet_ntoa(from->sin_addr), inet_ntoa(ip->ip_dst),
284 type, pr_type(type), icp->icmp_code);
285 for (i = 4; i < cc ; i += sizeof(long))
286 printf("%2d: x%8.8lx\n", i, *lp++);
287 }
288#endif
289 return(0);
290}
291
292static void /* not inline */
293send_probe(int seq, int ttl)
294{
295 struct opacket *op = outpacket;
296 struct ip *ip = &op->ip;
297 struct udphdr *up = &op->udp;
298 int i;
299 struct timezone tz;
300
301 ip->ip_off = 0;
302 ip->ip_hl = sizeof(*ip) >> 2;
303 ip->ip_p = IPPROTO_UDP;
304 ip->ip_len = datalen;
305 ip->ip_ttl = ttl;
306 ip->ip_v = IPVERSION;
307 ip->ip_id = htons(ident+seq);
308
309 up->source = htons(ident);
310 up->dest = htons(port+seq);
311 up->len = htons((u_short)(datalen - sizeof(struct ip)));
312 up->check = 0;
313
314 op->seq = seq;
315 op->ttl = ttl;
316 (void) gettimeofday(&op->tv, &tz);
317
318 i = sendto(sndsock, (char *)outpacket, datalen, 0, &whereto,
319 sizeof(struct sockaddr));
320 if (i < 0 || i != datalen) {
321 if (i<0)
322 perror("sendto");
323 printf("traceroute: wrote %s %d chars, ret=%d\n", hostname,
324 datalen, i);
325 (void) fflush(stdout);
326 }
327}
328
329
330int
331#ifndef CONFIG_TRACEROUTE
332main(int argc, char *argv[])
333#else
334traceroute_main(int argc, char *argv[])
335#endif
336{
337 extern char *optarg;
338 extern int optind;
339 struct hostent *hp;
340 struct sockaddr_in from, *to;
341 int ch, i, on, probe, seq, tos, ttl;
342
343 int options = 0; /* socket options */
344 char *source = 0;
345 int nprobes = 3;
346
347 on = 1;
348 seq = tos = 0;
349 to = (struct sockaddr_in *)&whereto;
350 while ((ch = getopt(argc, argv, "dm:np:q:rs:t:w:v")) != EOF)
351 switch(ch) {
352 case 'd':
353#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
354 options |= SO_DEBUG;
355#endif
356 break;
357 case 'm':
358 max_ttl = atoi(optarg);
359 if (max_ttl <= 1)
360 bb_error_msg_and_die("max ttl must be >1.");
361 break;
362 case 'n':
363 nflag++;
364 break;
365 case 'p':
366 port = atoi(optarg);
367 if (port < 1)
368 bb_error_msg_and_die("port must be >0.");
369 break;
370 case 'q':
371 nprobes = atoi(optarg);
372 if (nprobes < 1)
373 bb_error_msg_and_die("nprobes must be >0.");
374 break;
375 case 'r':
376 options |= SO_DONTROUTE;
377 break;
378 case 's':
379 /*
380 * set the ip source address of the outbound
381 * probe (e.g., on a multi-homed host).
382 */
383 source = optarg;
384 break;
385 case 't':
386 tos = atoi(optarg);
387 if (tos < 0 || tos > 255)
388 bb_error_msg_and_die("tos must be 0 to 255.");
389 break;
390 case 'v':
391#ifdef CONFIG_FEATURE_TRACEROUTE_VERBOSE
392 verbose++;
393#endif
394 break;
395 case 'w':
396 waittime = atoi(optarg);
397 if (waittime <= 1)
398 bb_error_msg_and_die("wait must be >1 sec.");
399 break;
400 default:
401 bb_show_usage();
402 }
403 argc -= optind;
404 argv += optind;
405
406 if (argc < 1)
407 bb_show_usage();
408
409 setlinebuf (stdout);
410
411 memset(&whereto, 0, sizeof(struct sockaddr));
412 hp = xgethostbyname(*argv);
413 to->sin_family = hp->h_addrtype;
414 memcpy(&to->sin_addr, hp->h_addr, hp->h_length);
415 hostname = (char *)hp->h_name;
416 if (*++argv)
417 datalen = atoi(*argv);
418 if (datalen < 0 || datalen >= MAXPACKET - sizeof(struct opacket))
419 bb_error_msg_and_die("packet size must be 0 <= s < %d.",
420 MAXPACKET - sizeof(struct opacket));
421 datalen += sizeof(struct opacket);
422 outpacket = (struct opacket *)xmalloc((unsigned)datalen);
423 memset(outpacket, 0, datalen);
424 outpacket->ip.ip_dst = to->sin_addr;
425 outpacket->ip.ip_tos = tos;
426 outpacket->ip.ip_v = IPVERSION;
427 outpacket->ip.ip_id = 0;
428
429 ident = (getpid() & 0xffff) | 0x8000;
430
431 if ((sndsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
432 bb_perror_msg_and_die(bb_msg_can_not_create_raw_socket);
433
434 s = create_icmp_socket();
435
436#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
437 if (options & SO_DEBUG)
438 (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
439 (char *)&on, sizeof(on));
440#endif
441 if (options & SO_DONTROUTE)
442 (void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
443 (char *)&on, sizeof(on));
444#ifdef SO_SNDBUF
445 if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&datalen,
446 sizeof(datalen)) < 0)
447 bb_perror_msg_and_die("SO_SNDBUF");
448#endif
449#ifdef IP_HDRINCL
450 if (setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
451 sizeof(on)) < 0)
452 bb_perror_msg_and_die("IP_HDRINCL");
453#endif
454#ifdef CONFIG_FEATURE_TRACEROUTE_SO_DEBUG
455 if (options & SO_DEBUG)
456 (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
457 (char *)&on, sizeof(on));
458#endif
459 if (options & SO_DONTROUTE)
460 (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
461 (char *)&on, sizeof(on));
462
463 if (source) {
464 memset(&from, 0, sizeof(struct sockaddr));
465 from.sin_family = AF_INET;
466 from.sin_addr.s_addr = inet_addr(source);
467 if (from.sin_addr.s_addr == -1)
468 bb_error_msg_and_die("unknown host %s", source);
469 outpacket->ip.ip_src = from.sin_addr;
470#ifndef IP_HDRINCL
471 if (bind(sndsock, (struct sockaddr *)&from, sizeof(from)) < 0)
472 bb_perror_msg_and_die("bind");
473#endif
474 }
475
476 fprintf(stderr, "traceroute to %s (%s)", hostname,
477 inet_ntoa(to->sin_addr));
478 if (source)
479 fprintf(stderr, " from %s", source);
480 fprintf(stderr, ", %d hops max, %d byte packets\n", max_ttl, datalen);
481
482 for (ttl = 1; ttl <= max_ttl; ++ttl) {
483 u_long lastaddr = 0;
484 int got_there = 0;
485 int unreachable = 0;
486
487 printf("%2d ", ttl);
488 for (probe = 0; probe < nprobes; ++probe) {
489 int cc, reset_timer;
490 struct timeval t1, t2;
491 struct timezone tz;
492 struct ip *ip;
493
494 (void) gettimeofday(&t1, &tz);
495 send_probe(++seq, ttl);
496 reset_timer = 1;
497 while ((cc = wait_for_reply(s, &from, reset_timer)) != 0) {
498 (void) gettimeofday(&t2, &tz);
499 if ((i = packet_ok(packet, cc, &from, seq))) {
500 reset_timer = 1;
501 if (from.sin_addr.s_addr != lastaddr) {
502 print(packet, cc, &from);
503 lastaddr = from.sin_addr.s_addr;
504 }
505 printf(" %g ms", deltaT(&t1, &t2));
506 switch(i - 1) {
507 case ICMP_UNREACH_PORT:
508 ip = (struct ip *)packet;
509 if (ip->ip_ttl <= 1)
510 printf(" !");
511 ++got_there;
512 break;
513 case ICMP_UNREACH_NET:
514 ++unreachable;
515 printf(" !N");
516 break;
517 case ICMP_UNREACH_HOST:
518 ++unreachable;
519 printf(" !H");
520 break;
521 case ICMP_UNREACH_PROTOCOL:
522 ++got_there;
523 printf(" !P");
524 break;
525 case ICMP_UNREACH_NEEDFRAG:
526 ++unreachable;
527 printf(" !F");
528 break;
529 case ICMP_UNREACH_SRCFAIL:
530 ++unreachable;
531 printf(" !S");
532 break;
533 }
534 break;
535 } else
536 reset_timer = 0;
537 }
538 if (cc == 0)
539 printf(" *");
540 (void) fflush(stdout);
541 }
542 putchar('\n');
543 if (got_there || unreachable >= nprobes-1)
544 return 0;
545 }
546
547 return 0;
548}
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 @@
1udhcp server/client package
2-----------------------
3
4Russ Dill <Russ.Dill@asu.edu>
5Matthew Ramsay <matthewr@moreton.com.au>
6Chris Trew <christ@moreton.com.au>
7
8Other Credits:
9--------------
10Moreton Bay (http://www.moretonbay.com/)
11Vladimir Oleynik <dzo@simtrea.ru> Size optimizations
12
13
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 @@
1 GNU GENERAL PUBLIC LICENSE
2 Version 2, June 1991
3
4 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5 675 Mass Ave, Cambridge, MA 02139, USA
6 Everyone is permitted to copy and distribute verbatim copies
7 of this license document, but changing it is not allowed.
8
9 Preamble
10
11 The licenses for most software are designed to take away your
12freedom to share and change it. By contrast, the GNU General Public
13License is intended to guarantee your freedom to share and change free
14software--to make sure the software is free for all its users. This
15General Public License applies to most of the Free Software
16Foundation's software and to any other program whose authors commit to
17using it. (Some other Free Software Foundation software is covered by
18the GNU Library General Public License instead.) You can apply it to
19your programs, too.
20
21 When we speak of free software, we are referring to freedom, not
22price. Our General Public Licenses are designed to make sure that you
23have the freedom to distribute copies of free software (and charge for
24this service if you wish), that you receive source code or can get it
25if you want it, that you can change the software or use pieces of it
26in new free programs; and that you know you can do these things.
27
28 To protect your rights, we need to make restrictions that forbid
29anyone to deny you these rights or to ask you to surrender the rights.
30These restrictions translate to certain responsibilities for you if you
31distribute copies of the software, or if you modify it.
32
33 For example, if you distribute copies of such a program, whether
34gratis or for a fee, you must give the recipients all the rights that
35you have. You must make sure that they, too, receive or can get the
36source code. And you must show them these terms so they know their
37rights.
38
39 We protect your rights with two steps: (1) copyright the software, and
40(2) offer you this license which gives you legal permission to copy,
41distribute and/or modify the software.
42
43 Also, for each author's protection and ours, we want to make certain
44that everyone understands that there is no warranty for this free
45software. If the software is modified by someone else and passed on, we
46want its recipients to know that what they have is not the original, so
47that any problems introduced by others will not reflect on the original
48authors' reputations.
49
50 Finally, any free program is threatened constantly by software
51patents. We wish to avoid the danger that redistributors of a free
52program will individually obtain patent licenses, in effect making the
53program proprietary. To prevent this, we have made it clear that any
54patent must be licensed for everyone's free use or not licensed at all.
55
56 The precise terms and conditions for copying, distribution and
57modification follow.
58
59 GNU GENERAL PUBLIC LICENSE
60 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
62 0. This License applies to any program or other work which contains
63a notice placed by the copyright holder saying it may be distributed
64under the terms of this General Public License. The "Program", below,
65refers to any such program or work, and a "work based on the Program"
66means either the Program or any derivative work under copyright law:
67that is to say, a work containing the Program or a portion of it,
68either verbatim or with modifications and/or translated into another
69language. (Hereinafter, translation is included without limitation in
70the term "modification".) Each licensee is addressed as "you".
71
72Activities other than copying, distribution and modification are not
73covered by this License; they are outside its scope. The act of
74running the Program is not restricted, and the output from the Program
75is covered only if its contents constitute a work based on the
76Program (independent of having been made by running the Program).
77Whether that is true depends on what the Program does.
78
79 1. You may copy and distribute verbatim copies of the Program's
80source code as you receive it, in any medium, provided that you
81conspicuously and appropriately publish on each copy an appropriate
82copyright notice and disclaimer of warranty; keep intact all the
83notices that refer to this License and to the absence of any warranty;
84and give any other recipients of the Program a copy of this License
85along with the Program.
86
87You may charge a fee for the physical act of transferring a copy, and
88you may at your option offer warranty protection in exchange for a fee.
89
90 2. You may modify your copy or copies of the Program or any portion
91of it, thus forming a work based on the Program, and copy and
92distribute such modifications or work under the terms of Section 1
93above, provided that you also meet all of these conditions:
94
95 a) You must cause the modified files to carry prominent notices
96 stating that you changed the files and the date of any change.
97
98 b) You must cause any work that you distribute or publish, that in
99 whole or in part contains or is derived from the Program or any
100 part thereof, to be licensed as a whole at no charge to all third
101 parties under the terms of this License.
102
103 c) If the modified program normally reads commands interactively
104 when run, you must cause it, when started running for such
105 interactive use in the most ordinary way, to print or display an
106 announcement including an appropriate copyright notice and a
107 notice that there is no warranty (or else, saying that you provide
108 a warranty) and that users may redistribute the program under
109 these conditions, and telling the user how to view a copy of this
110 License. (Exception: if the Program itself is interactive but
111 does not normally print such an announcement, your work based on
112 the Program is not required to print an announcement.)
113
114These requirements apply to the modified work as a whole. If
115identifiable sections of that work are not derived from the Program,
116and can be reasonably considered independent and separate works in
117themselves, then this License, and its terms, do not apply to those
118sections when you distribute them as separate works. But when you
119distribute the same sections as part of a whole which is a work based
120on the Program, the distribution of the whole must be on the terms of
121this License, whose permissions for other licensees extend to the
122entire whole, and thus to each and every part regardless of who wrote it.
123
124Thus, it is not the intent of this section to claim rights or contest
125your rights to work written entirely by you; rather, the intent is to
126exercise the right to control the distribution of derivative or
127collective works based on the Program.
128
129In addition, mere aggregation of another work not based on the Program
130with the Program (or with a work based on the Program) on a volume of
131a storage or distribution medium does not bring the other work under
132the scope of this License.
133
134 3. You may copy and distribute the Program (or a work based on it,
135under Section 2) in object code or executable form under the terms of
136Sections 1 and 2 above provided that you also do one of the following:
137
138 a) Accompany it with the complete corresponding machine-readable
139 source code, which must be distributed under the terms of Sections
140 1 and 2 above on a medium customarily used for software interchange; or,
141
142 b) Accompany it with a written offer, valid for at least three
143 years, to give any third party, for a charge no more than your
144 cost of physically performing source distribution, a complete
145 machine-readable copy of the corresponding source code, to be
146 distributed under the terms of Sections 1 and 2 above on a medium
147 customarily used for software interchange; or,
148
149 c) Accompany it with the information you received as to the offer
150 to distribute corresponding source code. (This alternative is
151 allowed only for noncommercial distribution and only if you
152 received the program in object code or executable form with such
153 an offer, in accord with Subsection b above.)
154
155The source code for a work means the preferred form of the work for
156making modifications to it. For an executable work, complete source
157code means all the source code for all modules it contains, plus any
158associated interface definition files, plus the scripts used to
159control compilation and installation of the executable. However, as a
160special exception, the source code distributed need not include
161anything that is normally distributed (in either source or binary
162form) with the major components (compiler, kernel, and so on) of the
163operating system on which the executable runs, unless that component
164itself accompanies the executable.
165
166If distribution of executable or object code is made by offering
167access to copy from a designated place, then offering equivalent
168access to copy the source code from the same place counts as
169distribution of the source code, even though third parties are not
170compelled to copy the source along with the object code.
171
172 4. You may not copy, modify, sublicense, or distribute the Program
173except as expressly provided under this License. Any attempt
174otherwise to copy, modify, sublicense or distribute the Program is
175void, and will automatically terminate your rights under this License.
176However, parties who have received copies, or rights, from you under
177this License will not have their licenses terminated so long as such
178parties remain in full compliance.
179
180 5. You are not required to accept this License, since you have not
181signed it. However, nothing else grants you permission to modify or
182distribute the Program or its derivative works. These actions are
183prohibited by law if you do not accept this License. Therefore, by
184modifying or distributing the Program (or any work based on the
185Program), you indicate your acceptance of this License to do so, and
186all its terms and conditions for copying, distributing or modifying
187the Program or works based on it.
188
189 6. Each time you redistribute the Program (or any work based on the
190Program), the recipient automatically receives a license from the
191original licensor to copy, distribute or modify the Program subject to
192these terms and conditions. You may not impose any further
193restrictions on the recipients' exercise of the rights granted herein.
194You are not responsible for enforcing compliance by third parties to
195this License.
196
197 7. If, as a consequence of a court judgment or allegation of patent
198infringement or for any other reason (not limited to patent issues),
199conditions are imposed on you (whether by court order, agreement or
200otherwise) that contradict the conditions of this License, they do not
201excuse you from the conditions of this License. If you cannot
202distribute so as to satisfy simultaneously your obligations under this
203License and any other pertinent obligations, then as a consequence you
204may not distribute the Program at all. For example, if a patent
205license would not permit royalty-free redistribution of the Program by
206all those who receive copies directly or indirectly through you, then
207the only way you could satisfy both it and this License would be to
208refrain entirely from distribution of the Program.
209
210If any portion of this section is held invalid or unenforceable under
211any particular circumstance, the balance of the section is intended to
212apply and the section as a whole is intended to apply in other
213circumstances.
214
215It is not the purpose of this section to induce you to infringe any
216patents or other property right claims or to contest validity of any
217such claims; this section has the sole purpose of protecting the
218integrity of the free software distribution system, which is
219implemented by public license practices. Many people have made
220generous contributions to the wide range of software distributed
221through that system in reliance on consistent application of that
222system; it is up to the author/donor to decide if he or she is willing
223to distribute software through any other system and a licensee cannot
224impose that choice.
225
226This section is intended to make thoroughly clear what is believed to
227be a consequence of the rest of this License.
228
229 8. If the distribution and/or use of the Program is restricted in
230certain countries either by patents or by copyrighted interfaces, the
231original copyright holder who places the Program under this License
232may add an explicit geographical distribution limitation excluding
233those countries, so that distribution is permitted only in or among
234countries not thus excluded. In such case, this License incorporates
235the limitation as if written in the body of this License.
236
237 9. The Free Software Foundation may publish revised and/or new versions
238of the General Public License from time to time. Such new versions will
239be similar in spirit to the present version, but may differ in detail to
240address new problems or concerns.
241
242Each version is given a distinguishing version number. If the Program
243specifies a version number of this License which applies to it and "any
244later version", you have the option of following the terms and conditions
245either of that version or of any later version published by the Free
246Software Foundation. If the Program does not specify a version number of
247this License, you may choose any version ever published by the Free Software
248Foundation.
249
250 10. If you wish to incorporate parts of the Program into other free
251programs whose distribution conditions are different, write to the author
252to ask for permission. For software which is copyrighted by the Free
253Software Foundation, write to the Free Software Foundation; we sometimes
254make exceptions for this. Our decision will be guided by the two goals
255of preserving the free status of all derivatives of our free software and
256of promoting the sharing and reuse of software generally.
257
258 NO WARRANTY
259
260 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268REPAIR OR CORRECTION.
269
270 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278POSSIBILITY OF SUCH DAMAGES.
279
280 END OF TERMS AND CONDITIONS
281
282 Appendix: How to Apply These Terms to Your New Programs
283
284 If you develop a new program, and you want it to be of the greatest
285possible use to the public, the best way to achieve this is to make it
286free software which everyone can redistribute and change under these terms.
287
288 To do so, attach the following notices to the program. It is safest
289to attach them to the start of each source file to most effectively
290convey the exclusion of warranty; and each file should have at least
291the "copyright" line and a pointer to where the full notice is found.
292
293 <one line to give the program's name and a brief idea of what it does.>
294 Copyright (C) 19yy <name of author>
295
296 This program is free software; you can redistribute it and/or modify
297 it under the terms of the GNU General Public License as published by
298 the Free Software Foundation; either version 2 of the License, or
299 (at your option) any later version.
300
301 This program is distributed in the hope that it will be useful,
302 but WITHOUT ANY WARRANTY; without even the implied warranty of
303 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 GNU General Public License for more details.
305
306 You should have received a copy of the GNU General Public License
307 along with this program; if not, write to the Free Software
308 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
309
310Also add information on how to contact you by electronic and paper mail.
311
312If the program is interactive, make it output a short notice like this
313when it starts in an interactive mode:
314
315 Gnomovision version 69, Copyright (C) 19yy name of author
316 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 This is free software, and you are welcome to redistribute it
318 under certain conditions; type `show c' for details.
319
320The hypothetical commands `show w' and `show c' should show the appropriate
321parts of the General Public License. Of course, the commands you use may
322be called something other than `show w' and `show c'; they could even be
323mouse-clicks or menu items--whatever suits your program.
324
325You should also get your employer (if you work as a programmer) or your
326school, if any, to sign a "copyright disclaimer" for the program, if
327necessary. Here is a sample; alter the names:
328
329 Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
332 <signature of Ty Coon>, 1 April 1989
333 Ty Coon, President of Vice
334
335This General Public License does not permit incorporating your program into
336proprietary programs. If your program is a subroutine library, you may
337consider it more useful to permit linking proprietary applications with the
338library. If this is what you want to do, use the GNU Library General
339Public 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 @@
10.9.9 (pending)
2+ Various other size optimizations (Vladimir)
3+ Change strerror(errno) to %m (Vladimir N. Oleynik <dzo@simtreas.ru>)
4+ Fixed a little endian problem in mton (Bastian Blank <waldi@debian.org>)
5+ Fixed a arpping alignment problem (Rui He <rhe@3eti.com>)
6+ Added sanity check for max_leases (udhcp bug #1285) (me)
7+ Finally got rid of the trailing space in enviromental vars (me)
8+ added an new enviromental variable: $mask. It contains the number
9 of subnet bits for tools like ip route that require it.
10 (Bastian Blank <waldi@debian.org>, me)
11
120.9.8 (021031)
13+ split up README files (me)
14+ use /dev/urandom to seed xid's (instead of time(0)) (me)
15+ fixed renew behavior (me)
16+ udhcp now fits nicely into busybox
17 (Glenn McGrath <bug1@iinet.net.au> as well as myself)
18+ updated client manpage (me)
19+ both client and server now use sockets for signal handling,
20 hopefully, this will be the last needed change in signal
21 handling, I'm fairly certain all the possible races are now
22 closed. (me)
23+ The server now restarts the auto_time timer when it receives
24 a SIGUSR1 (write out config file). (me)
25+ Improve signal handling (David Poole)
26+ Fix to config file parsing (Matt Kraai)
27+ Fix load lease logic (me)
28+ Fix clear_lease logic (me)
29+ -h is now an alias for -H (udhcp bug #1253)
30+ Shorter timeout on not receiving offers (me)
31+ Improved signal behavior by client (me)
32+ Would never assign end address (Keith Smith <keith@ksmith.com>)
33+ Was improperly reporting yiaddr as siaddr (ben-udhcp@bdlow.net)
34 udhcp bug#1256
35+ Fixed reading of client id (David Poole <davep@portsmith.com>)
36+ change sys_errlist[] to strerror() as it aparently doesn't exist
37 (Erik Andersen <andersee@codepoet.org>)
38+ fixed get_raw_packet so it returns -2 on non fatal errors
39 (Ted Lemon <Ted.Lemon@nominum.com>)
40+ Improved (hopefully) NAKing behavior (me)
41+ Added -b option (Jouni Malinen)
42+ Compute checksums correctly on big endian hosts
43 (Jouni Malinen <jkmaline@cc.hut.fi>)
44
450.9.7 (020526)
46+ Use add_lease in read_leases, sanitizes leases more, and clears out exprired
47 ones if there is no more room (me)
48+ Moved udhcpd.leases to /var/lib/misc/udhcpd.leases (Debian bug #147747)
49+ Change (obsolete) AF_INET in arping.c to PF_PACKET (Debian bug #127049)
50+ Added script hook for DHCPNAK (nak), as well as providing the message option
51 (me)
52+ Generate the paramaters request list by seeing what options in options.c are
53 ored with OPTION_REQ in options.c
54+ Fix dhcp renew forgetfullness on client (bug #1230)
55+ Fix dhcp release bug on client (bug #1231)
56+ Set option request list for DHCP renew (bug #1233)
57+ Set BOOTREQUEST/REPLY properly
58+ Change client-identifier field to popularly expected behavior (me)
59+ Only reopen port on errors (me)
60+ Change fork/close/setsid structures to daemon() (me)
61+ Allow user to specify udhcpd config file at run time (Steven, me)
62+ Write pidfile after changing it (Steven CTR Carr <Steven.CTR.Carr@tc.faa.gov>)
63+ Added env var docs to udhcpc man page (Matt)
64+ Standardized lowercase udhcp in documentation (me)
65+ Accept packets without a UDP checksum (me)
66+ Accept packets with extra garbage (me)
67+ Better error handling in files.c (me)
68+ Combined read_interface function to reduce COMBINED_BINARY size (me)
69+ Drop calc_length(), some servers choke on smaller packets (me)
70+ Try to clean some fat out (me)
71
720.9.6 (011001)
73+ Added bootp paramaters to server (me)
74+ Added bootp paramaters to client (me)
75+ Added vendor id to client (me)
76+ Better pidfile handling in client and server (me)
77+ Added man pages (Matt Kraai <kraai@alumni.carnegiemellon.edu>)
78
790.9.5 (010914)
80+ Fixed $HOME and $PATH env passing (me)
81+ Fixed client to only listen for raw packets on correct interface (me)
82+ added --quit,-q option to quit after a lease is obtained (me)
83+ Fixed 100% CPU utilization by client when interface is down (me)
84
850.9.4 (010827)
86+ Force broadcast to broken clients that request unicast (ie, MSFT 98)
87+ Make install rules (Adam J. Richter <adam@yggdrasil.com>)
88+ One scripts, instead of many (Adam)
89+ Removed script paramater info files (env vars only) (Adam)
90+ Controlling of forking behavior in client (Adam)
91+ General script.c/dhcpc.c cleanups (Adam)
92
930.9.3 (010820)
94+ Increased debugging verbosity (me)
95+ Cut trailing whitespace when reading config file (me)
96+ added hostname option to client (me)
97+ fixed a strncpy bug in script.c (me)
98+ fixed a leaky socket in dhcpc.c (me)
99+ fixed a leaky socket in dhcpd.c (me)
100
1010.9.2 (010810)
102+ Added raw sockets to client (me)
103+ alignment fixes (Mark Huang)
104+ compiler warning fixes (Mark Huang)
105+ client now sends parameter list (Mark Huang/me)
106+ added ipttl option
107+ Does now not request broadcast packets
108
1090.9.1 (010806)
110+ Added udhcpc client
111+ reorganized functions/files
112+ listening socket now only binds to one interface
113
1140.9.0 (010720) Major rewrite, current changes, goals:
115+ should not segfault on bogus packets.
116+ Options can be read from sname and file fields.
117+ supports all DHCP messages (release, decline, inform).
118+ IP block is now specified by a range of IP's.
119+ Leases file now contains lease time (relative, or absolute).
120+ Just about any DHCP option is now supported.
121+ DNS entries are no longer read from resolv.conf
122+ Lease file can be written periodically when the process receives a SIGUSR1
123+ arpping should be supported on all arches.
124+ support for DHCP relays.
125+ DHCP messages can be unicast if the client requests it.
126+ many, many, many other things.
127
1280.8.29 (000323)
129+ stable(?) release
130
131
1320.8.28 (000323)
133+ removed alarm as it was causing server to go down
134+ removed debugging
135+ break down dhcpd.c into manageable files
136
137
1380.8.27 (000221)
139+ OFFER also sends gateway/subnet (for picky dhcp clients)
140+ multiple DNS now handled from resolv.conf if available
141+ multiple WINS (from dhcpd.conf)
142
1430.8.25 (000120)
144+ now compiles *and* runs on a generic linux system
145 tested with a windows 98 client and the sample config
146 files in the samples directory.
147
1480.8.24 (000117)
149+ makeiplist tool has basic functionality in place
150+ new sample config files
151+ route add -host 255.255.255.255 dev eth0 added for generic linux
152
1530.8.23 (000117)
154+ NETtel specific fix for ignoring dhcp requests on 2nd interface
155
1560.8.22 (000113)
157+ minor changes to compile under a generic linux system
158+ minor config file location changes for a generic linux system
159+ makeiplist fixes.. still incomplete.. but etting closer
160
1610.8.21 (000113)
162+ now sends the correct server ip instead of hardcoded value
163+ minor debugging fixes for critical messages
164
1650.8.20 (000106)
166+ cut out dhcp server checking.. this was causing dialout ppp
167 sessions with idle time set to never time out.
168+ also removed the 10 second pause before launching.. as this
169 was originally to stop it replying to a dhcp client
170 on a NETtel which was really a bad way to do it in the
171 first place :-)
172
1730.8.19 (000104)
174+ fixes for route add -host on a machine that needs to run both
175 a DHCP client and server (dual eth box)
176
1770.8.18 (991220)
178
179+ Race conditions fixed by disabling alarm whilst the server is busy
180+ Fixed continous clearing of the offered array so that it is only cleared
181 when it is dirty - (could change the position of when dirty is set)
182
1830.8.17 (991212)
184
185- has problems clearing out the offered array
186
1870.8.16 (991203)
188+ Non blocking error is changes to informational as it is not really
189 an error
190
1910.8.15 (991129)
192+ Servs the dns field 3 times (Nettel only) so that windows servers
193 dont time out whilst nettel is booting
194
1950.8.14 (991126)
196+ added owner check for the offered array so clean out time may be
197 increased
198+ added new func to print out chadder/MAC
199
2000.8.13 (991125)
201+ added win95 support (w95 changed xid halfway through conversation)
202+ had to change the offered array to use hardware addresses instead of xid
203+ fixed re offered bug
204+ added more debugging
205
2060.8.12 (991111)
207+ debugging was real bad.. cleaned up a bit.. needs overhaul
208
209
2100.8.11 (991110)
211+ fixed up offeredAddr array to actually be used now!! offeredAddr is
212 used to see if another simultaneous connecting client was offered
213 an address that we are about to offer another client (multiple
214 client bug)
215+ removed re_offered variable as it breaks multiple client support
216+ added lease time to ACK -- doesn't work if in OFFER
217+ decreased internal array clear delay to 60 seconds
218+ minor findAddr bug (returning -1 instead of 0)
219+ if clients xid already in offeredAddr offer the same addr and don't add a
220 new addr to offered (caused by a client issuing multiple DISCOVERs)
221
2220.8.10 (991105)
223+ \n bug in arpping
224+ minor debugging changes (removed printfs etc)
225+ started browseiplist (not finished)
226
2270.8.9 (19991105)
228+ fixed options array size bug (options were cut off)
229
2300.8.8 (19991105)
231+ ignores requests from dhcpcd on the same machine
232
2330.8.7 (19991104)
234+ don't die if we can't bind to search for existing DHCP server
235+ slightly more verbose syslogging
236
2370.8.6 (19991103)
238+ added makeiplist (not finished -- core dumps)
239+ minor debug changes
240
2410.8.5 (19991029)
242+ exits if another DHCP server is already on the network
243+ added Linux Makefile
244
2450.8.4 (19991026)
246+ minor bug fix in findaddr preventing an addr being found
247
2480.8.3 (19991025)
249+ fixed up debugging
250+ minor hwaddr issues
251
2520.8.2 (19991022)
253+ free leases (new arpping code from dhcpcd)
254+ fixed bug where crashes if no leases/iplist file
255+ syslogging and debugging switch
256+ serve DNS from resolv.conf
257+ fixed bug where new lease added if same mac offered
258+ now checks the ip is free b4 offering
259+ now supports wins server
260
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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "udhcp Server/Client"
7
8config CONFIG_UDHCPD
9 bool "udhcp Server (udhcpd)"
10 default n
11 help
12 uDHCPd is a DHCP server geared primarily toward embedded systems,
13 while striving to be fully functional and RFC compliant.
14
15 See http://udhcp.busybox.net for further details.
16
17config CONFIG_UDHCPC
18 bool "udhcp Client (udhcpc)"
19 default n
20 help
21 uDHCPc is a DHCP client geared primarily toward embedded systems,
22 while striving to be fully functional and RFC compliant.
23
24 The udhcp client negotiates a lease with the DHCP server and
25 notifies a set of scripts when a lease is obtained or lost.
26
27 See http://udhcp.busybox.net for further details.
28
29config CONFIG_DUMPLEASES
30 bool "Lease display utility (dumpleases)"
31 default n
32 depends on CONFIG_UDHCPD
33 help
34 dumpleases displays the leases written out by the udhcpd server.
35 Lease times are stored in the file by time remaining in lease, or
36 by the absolute time that it expires in seconds from epoch.
37
38 See http://udhcp.busybox.net for further details.
39
40config CONFIG_FEATURE_UDHCP_SYSLOG
41 bool " Log udhcp messages to syslog (instead of stdout)"
42 default n
43 depends on CONFIG_UDHCPD || CONFIG_UDHCPC
44 help
45 If selected, udhcpd will log all its messages to syslog, otherwise,
46 it will attempt to log them to stdout.
47
48 See http://udhcp.busybox.net for further details.
49
50config CONFIG_FEATURE_UDHCP_DEBUG
51 bool " Compile udhcp with noisy debugging messages"
52 default n
53 depends on CONFIG_UDHCPD || CONFIG_UDHCPC
54 help
55 If selected, udhcpd will output extra debugging output. If using
56 this option, compile uDHCP with "-g", and do not fork the daemon to
57 the background.
58
59 See http://udhcp.busybox.net for further details.
60
61endmenu
62
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=../..
21top_builddir=../..
22srcdir=$(top_srcdir)/networking/udhcp
23UDHCP_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20UDHCP_AR:=udhcp.a
21ifndef $(UDHCP_DIR)
22UDHCP_DIR:=$(top_builddir)/networking/udhcp/
23endif
24srcdir=$(top_srcdir)/networking/udhcp
25
26#ok, so I forgot how to do an or, but this is a quick and dirty hack
27ifeq ($(CONFIG_UDHCPC), y)
28CONFIG_UDHCP_SHARED=y
29else
30ifeq ($(CONFIG_UDHCPD), y)
31CONFIG_UDHCP_SHARED=y
32else
33CONFIG_UDHCP_SHARED=n
34endif
35endif
36
37UDHCP-y:=
38UDHCP-$(CONFIG_UDHCP_SHARED) += common.c options.c packet.c pidfile.c \
39 signalpipe.c socket.c
40UDHCP-$(CONFIG_UDHCPC) += dhcpc.c clientpacket.c clientsocket.c \
41 script.c
42UDHCP-$(CONFIG_UDHCPD) += dhcpd.c arpping.c files.c leases.c \
43 serverpacket.c static_leases.c
44UDHCP-$(CONFIG_DUMPLEASES) += dumpleases.c
45UDHCP_OBJS=$(patsubst %.c,$(UDHCP_DIR)%.o, $(UDHCP-y))
46
47libraries-y+=$(UDHCP_DIR)$(UDHCP_AR)
48
49$(UDHCP_DIR)$(UDHCP_AR): $(UDHCP_OBJS)
50 $(AR) -ro $@ $(UDHCP_OBJS)
51
52$(UDHCP_OBJS): $(UDHCP_DIR)%.o : $(srcdir)/%.c
53 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DIN_BUSYBOX -c $< -o $@
54
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 @@
1udhcp server/client package readme
2-------------------------
3
4The udhcp server/client package is primarily geared towards embedded
5systems. It does however, strive to be fully functional, and RFC
6compliant.
7
8
9compile time options
10-------------------
11
12The Makefile contains three of the compile time options:
13
14 UDHCP_DEBUG: If UDHCP_DEBUG is defined, udhcpd will output extra
15 debugging output, compile with -g, and not fork to the background when
16 run.
17 UDHCP_SYSLOG: If UDHCP_SYSLOG is defined, udhcpd will log all its
18 messages syslog, otherwise, it will attempt to log them to stdout.
19
20 COMBINED_BINARY: If COMBINED_BINARY is define, one binary, udhcpd,
21 is created. If called as udhcpd, the dhcp server will be started.
22 If called as udhcpc, the dhcp client will be started.
23
24dhcpd.h contains the other three compile time options:
25
26 LEASE_TIME: The default lease time if not specified in the config
27 file.
28
29 LEASES_FILE: The default file for storing leases.
30
31 DHCPD_CONFIG_FILE: The defualt config file to use.
32
33options.c contains a set of dhcp options for the client:
34
35 name[10]: The name of the option as it will appear in scripts
36
37 flags: The type of option, as well as if it will be requested
38 by the client (OPTION_REQ)
39
40 code: The DHCP code for this option
41
42
43busybox drop-in
44--------------
45udhcp is now a drop-in component for busybox (http://busybox.net).
46To update busybox to the latest revision, simply do a:
47
48cp *.[ch] README AUTHORS COPYING ChangeLog TODO \
49 <busybox_source>/networking/udhcp
50
51The only two files udhcp does not provide are config.in and
52Makefile.in, so these may need to be updated from time to time.
53
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 @@
1udhcp lease dump (dumpleases)
2----------------------------
3
4dumpleases displays the leases written out by the udhcpd server. Lease
5times are stored in the file by time remaining in lease (for systems
6without clock that works when there is no power), or by the absolute
7time that it expires in seconds from epoch. dumpleases accepts the
8following command line options:
9
10-a, --absolute Interpret lease times as expiration time.
11-r, --remaining Interpret lease times as remaining time.
12-f, --file=FILE Read lease information from FILE.
13-h, --help Display help.
14
15Note that if udhcpd has not written a leases file recently, the output
16of may not be up to date.
17
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 @@
1udhcp client (udhcpc)
2--------------------
3
4The udhcp client negotiates a lease with the DHCP server and notifies
5a set of scripts when a leases is obtained or lost.
6
7
8command line options
9-------------------
10
11The command line options for the udhcp client are:
12
13-c, --clientid=CLIENTID Client identifier
14-H, --hostname=HOSTNAME Client hostname
15-h, Alias for -H
16-f, --foreground Do not fork after getting lease
17-b, --background Fork to background if lease cannot be
18 immediately negotiated.
19-i, --interface=INTERFACE Interface to use (default: eth0)
20-n, --now Exit with failure if lease cannot be
21 immediately negotiated.
22-p, --pidfile=file Store process ID of daemon in file
23-q, --quit Quit after obtaining lease
24-r, --request=IP IP address to request (default: none)
25-s, --script=file Run file at dhcp events (default:
26 /usr/share/udhcpc/default.script)
27-v, --version Display version
28
29
30If the requested IP address cannot be obtained, the client accepts the
31address that the server offers.
32
33
34udhcp client scripts
35-------------------
36
37When an event occurs, udhcpc calls the action script. udhcpc never does
38any configuration of the network interface itself, but instead relies on
39a set of scripts. The script by default is
40/usr/share/udhcpc/default.script but this can be changed via the command
41line arguments. The three possible arguments to the script are:
42
43 deconfig: This argument is used when udhcpc starts, and
44 when a leases is lost. The script must put the interface in an
45 up, but deconfigured state, ie: ifconfig $interface 0.0.0.0.
46
47 bound: This argument is used when udhcpc moves from an
48 unbound, to a bound state. All of the paramaters are set in
49 enviromental variables, The script should configure the interface,
50 and set any other relavent parameters (default gateway, dns server,
51 etc).
52
53 renew: This argument is used when a DHCP lease is renewed. All of
54 the paramaters are set in enviromental variables. This argument is
55 used when the interface is already configured, so the IP address,
56 will not change, however, the other DHCP paramaters, such as the
57 default gateway, subnet mask, and dns server may change.
58
59 nak: This argument is used with udhcpc receives a NAK message.
60 The script with the deconfig argument will be called directly
61 afterwards, so no changes to the network interface are neccessary.
62 This hook is provided for purely informational purposes (the
63 message option may contain a reason for the NAK).
64
65The paramaters for enviromental variables are as follows:
66
67 $HOME - The set $HOME env or "/"
68 $PATH - the set $PATH env or "/bin:/usr/bin:/sbin:/usr/sbin"
69 $1 - What action the script should perform
70 interface - The interface this was obtained on
71 ip - The obtained IP
72 mask - The number of bits in the netmask (ie: 24)
73 siaddr - The bootp next server option
74 sname - The bootp server name option
75 boot_file - The bootp boot file option
76 subnet - The assigend subnet mask
77 timezone - Offset in seconds from UTC
78 router - A list of routers
79 timesvr - A list of time servers
80 namesvr - A list of IEN 116 name servers
81 dns - A list of DNS server
82 logsvr - A list of MIT-LCS UDP log servers
83 cookiesvr - A list of RFC 865 cookie servers
84 lprsvr - A list of LPR servers
85 hostname - The assigned hostname
86 bootsize - The length in 512 octect blocks of the bootfile
87 domain - The domain name of the network
88 swapsvr - The IP address of the client's swap server
89 rootpath - The path name of the client's root disk
90 ipttl - The TTL to use for this network
91 mtu - The MTU to use for this network
92 broadcast - The broadcast address for this network
93 ntpsrv - A list of NTP servers
94 wins - A list of WINS servers
95 lease - The lease time, in seconds
96 dhcptype - DHCP message type (safely ignored)
97 serverid - The IP of the server
98 message - Reason for a DHCPNAK
99 tftp - The TFTP server name
100 bootfile - The bootfile name
101
102additional options are easily added in options.c.
103
104
105note on udhcpc's random seed
106---------------------------
107
108udhcpc will seed its random number generator (used for generating xid's)
109by reading /dev/urandom. If you have a lot of embedded systems on the same
110network, with no entropy, you can either seed /dev/urandom by a method of
111your own, or doing the following on startup:
112
113ifconfig eth0 > /dev/urandom
114
115in order to seed /dev/urandom with some data (mac address) unique to your
116system. If reading /dev/urandom fails, udhcpc will fall back to its old
117behavior of seeding with time(0).
118
119
120signals accepted by udhcpc
121-------------------------
122
123udhcpc also responds to SIGUSR1 and SIGUSR2. SIGUSR1 will force a renew state,
124and SIGUSR2 will force a release of the current lease, and cause udhcpc to
125go into an inactive state (until it is killed, or receives a SIGUSR1). You do
126not need to sleep between sending signals, as signals received are processed
127sequencially in the order they are received.
128
129
130compile time options
131-------------------
132
133options.c contains a set of dhcp options for the client:
134
135 name[10]: The name of the option as it will appear in scripts
136
137 flags: The type of option, as well as if it will be requested
138 by the client (OPTION_REQ)
139
140 code: The DHCP code for this option
141
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 @@
1udhcp server (udhcpd)
2--------------------
3
4The only command line argument to udhcpd is an optional specifed
5config file. If no config file is specified, udhcpd uses the default
6config file, /etc/udhcpd.conf. Ex:
7
8udhcpd /etc/udhcpd.eth1.conf
9
10The udhcp server employs a number of simple config files:
11
12udhcpd.leases
13------------
14
15The udhcpd.leases behavior is designed for an embedded system. The
16file is written either every auto_time seconds, or when a SIGUSR1
17is received (the auto_time timer restarts if a SIGUSR1 is received).
18If you send a SIGTERM to udhcpd directly after a SIGUSR1, udhcpd will
19finish writing the leases file and wait for the aftermentioned script
20to be executed and finish before quiting, so you do not need to sleep
21between sending signals. When the file is written, a script can be
22optionally called to commit the file to flash. Lease times are stored
23in the file by time remaining in lease (for systems without clock
24that works when there is no power), or by the absolute time that it
25expires in seconds from epoch. In the remaining format, expired leases
26are stored as zero. The file is of the format:
27
2816 byte MAC
294 byte ip address
30u32 expire time
3116 byte MAC
324 byte ip address
33u32 expire time
34.
35etc.
36
37example: hexdump udhcpd.leases
38
390000000 1000 c95a 27d9 0000 0000 0000 0000 0000
400000010 a8c0 150a 0d00 2d29 5000 23fc 8566 0000
410000020 0000 0000 0000 0000 a8c0 140a 0d00 4e29
420000030
43
44
45udhcpd.conf
46----------
47
48The format is fairly simple, there is a sample file with all the
49available options and comments describing them in samples/udhcpd.conf
50
51compile time options
52-------------------
53
54dhcpd.h contains the other two compile time options:
55
56 LEASE_TIME: The default lease time if not specified in the config
57 file.
58
59 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 @@
1TODO
2----
3+ Check for valid IP, netmask, hostname, paths, strings, etc
4+ Integrade README.*'s with manpages
5+ using time(0) breaks if the system clock changes, find a portable solution
6+ make failure of reading functions revert to previous value, not the default
7+ sanity code for option[OPT_LEN]
8+ fix aliasing (ie: eth0:0)
9+ better standard linux distro support
10+ make sure packet generation works on a wide varitey of arches
11+ Interoperability testing
12+ Hooks within the DHCP server
13 * Server notification when a lease is added/removed
14+ Additional bootp support in client/server
15+ Make serverid option in server configurable
16+ 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 @@
1/*
2 * arpping.c
3 *
4 * Mostly stolen from: dhcpcd - DHCP client daemon
5 * by Yoichi Hariguchi <yoichi@fore.com>
6 */
7
8#include <sys/time.h>
9#include <time.h>
10#include <sys/socket.h>
11#include <netinet/if_ether.h>
12#include <net/if_arp.h>
13#include <netinet/in.h>
14#include <string.h>
15#include <unistd.h>
16#include <errno.h>
17
18#include "dhcpd.h"
19#include "arpping.h"
20#include "common.h"
21
22/* args: yiaddr - what IP to ping
23 * ip - our ip
24 * mac - our arp address
25 * interface - interface to use
26 * retn: 1 addr free
27 * 0 addr used
28 * -1 error
29 */
30
31/* FIXME: match response against chaddr */
32int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *mac, char *interface)
33{
34
35 int timeout = 2;
36 int optval = 1;
37 int s; /* socket */
38 int rv = 1; /* return value */
39 struct sockaddr addr; /* for interface name */
40 struct arpMsg arp;
41 fd_set fdset;
42 struct timeval tm;
43 time_t prevTime;
44
45
46 if ((s = socket (PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) == -1) {
47#ifdef IN_BUSYBOX
48 LOG(LOG_ERR, bb_msg_can_not_create_raw_socket);
49#else
50 LOG(LOG_ERR, "Could not open raw socket");
51#endif
52 return -1;
53 }
54
55 if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) {
56 LOG(LOG_ERR, "Could not setsocketopt on raw socket");
57 close(s);
58 return -1;
59 }
60
61 /* send arp request */
62 memset(&arp, 0, sizeof(arp));
63 memcpy(arp.h_dest, MAC_BCAST_ADDR, 6); /* MAC DA */
64 memcpy(arp.h_source, mac, 6); /* MAC SA */
65 arp.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */
66 arp.htype = htons(ARPHRD_ETHER); /* hardware type */
67 arp.ptype = htons(ETH_P_IP); /* protocol type (ARP message) */
68 arp.hlen = 6; /* hardware address length */
69 arp.plen = 4; /* protocol address length */
70 arp.operation = htons(ARPOP_REQUEST); /* ARP op code */
71 memcpy(arp.sInaddr, &ip, sizeof(ip)); /* source IP address */
72 memcpy(arp.sHaddr, mac, 6); /* source hardware address */
73 memcpy(arp.tInaddr, &yiaddr, sizeof(yiaddr)); /* target IP address */
74
75 memset(&addr, 0, sizeof(addr));
76 strcpy(addr.sa_data, interface);
77 if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0)
78 rv = 0;
79
80 /* wait arp reply, and check it */
81 tm.tv_usec = 0;
82 prevTime = uptime();
83 while (timeout > 0) {
84 FD_ZERO(&fdset);
85 FD_SET(s, &fdset);
86 tm.tv_sec = timeout;
87 if (select(s + 1, &fdset, (fd_set *) NULL, (fd_set *) NULL, &tm) < 0) {
88 DEBUG(LOG_ERR, "Error on ARPING request: %m");
89 if (errno != EINTR) rv = 0;
90 } else if (FD_ISSET(s, &fdset)) {
91 if (recv(s, &arp, sizeof(arp), 0) < 0 ) rv = 0;
92 if (arp.operation == htons(ARPOP_REPLY) &&
93 bcmp(arp.tHaddr, mac, 6) == 0 &&
94 *((uint32_t *) arp.sInaddr) == yiaddr) {
95 DEBUG(LOG_INFO, "Valid arp reply receved for this address");
96 rv = 0;
97 break;
98 }
99 }
100 timeout -= uptime() - prevTime;
101 prevTime = uptime();
102 }
103 close(s);
104 DEBUG(LOG_INFO, "%salid arp replies for this address", rv ? "No v" : "V");
105 return rv;
106}
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 @@
1/*
2 * arpping .h
3 */
4
5#ifndef ARPPING_H
6#define ARPPING_H
7
8#include <netinet/if_ether.h>
9#include <net/if_arp.h>
10#include <net/if.h>
11#include <netinet/in.h>
12
13struct arpMsg {
14 /* Ethernet header */
15 u_char h_dest[6]; /* destination ether addr */
16 u_char h_source[6]; /* source ether addr */
17 u_short h_proto; /* packet type ID field */
18
19 /* ARP packet */
20 uint16_t htype; /* hardware type (must be ARPHRD_ETHER) */
21 uint16_t ptype; /* protocol type (must be ETH_P_IP) */
22 uint8_t hlen; /* hardware address length (must be 6) */
23 uint8_t plen; /* protocol address length (must be 4) */
24 uint16_t operation; /* ARP opcode */
25 uint8_t sHaddr[6]; /* sender's hardware address */
26 uint8_t sInaddr[4]; /* sender's IP address */
27 uint8_t tHaddr[6]; /* target's hardware address */
28 uint8_t tInaddr[4]; /* target's IP address */
29 uint8_t pad[18]; /* pad for min. Ethernet payload (60 bytes) */
30} __attribute__ ((packed));
31
32/* function prototypes */
33int arpping(uint32_t yiaddr, uint32_t ip, uint8_t *arp, char *interface);
34
35#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 @@
1/* clientpacket.c
2 *
3 * Packet generation and dispatching functions for the DHCP client.
4 *
5 * Russ Dill <Russ.Dill@asu.edu> July 2001
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include <string.h>
23#include <sys/socket.h>
24#include <features.h>
25#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
26#include <netpacket/packet.h>
27#include <net/ethernet.h>
28#else
29#include <asm/types.h>
30#include <linux/if_packet.h>
31#include <linux/if_ether.h>
32#endif
33#include <stdlib.h>
34#include <time.h>
35#include <unistd.h>
36#include <netinet/in.h>
37#include <arpa/inet.h>
38#include <fcntl.h>
39
40
41#include "dhcpd.h"
42#include "clientpacket.h"
43#include "options.h"
44#include "dhcpc.h"
45#include "common.h"
46
47
48/* Create a random xid */
49unsigned long random_xid(void)
50{
51 static int initialized;
52 if (!initialized) {
53 int fd;
54 unsigned long seed;
55
56 fd = open("/dev/urandom", 0);
57 if (fd < 0 || read(fd, &seed, sizeof(seed)) < 0) {
58 LOG(LOG_WARNING, "Could not load seed from /dev/urandom: %m");
59 seed = time(0);
60 }
61 if (fd >= 0) close(fd);
62 srand(seed);
63 initialized++;
64 }
65 return rand();
66}
67
68
69/* initialize a packet with the proper defaults */
70static void init_packet(struct dhcpMessage *packet, char type)
71{
72 struct vendor {
73 char vendor, length;
74 char str[sizeof("udhcp "VERSION)];
75 } vendor_id = { DHCP_VENDOR, sizeof("udhcp "VERSION) - 1, "udhcp "VERSION};
76
77 init_header(packet, type);
78 memcpy(packet->chaddr, client_config.arp, 6);
79 add_option_string(packet->options, client_config.clientid);
80 if (client_config.hostname) add_option_string(packet->options, client_config.hostname);
81 add_option_string(packet->options, (uint8_t *) &vendor_id);
82}
83
84
85/* Add a parameter request list for stubborn DHCP servers. Pull the data
86 * from the struct in options.c. Don't do bounds checking here because it
87 * goes towards the head of the packet. */
88static void add_requests(struct dhcpMessage *packet)
89{
90 int end = end_option(packet->options);
91 int i, len = 0;
92
93 packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
94 for (i = 0; dhcp_options[i].code; i++)
95 if (dhcp_options[i].flags & OPTION_REQ)
96 packet->options[end + OPT_DATA + len++] = dhcp_options[i].code;
97 packet->options[end + OPT_LEN] = len;
98 packet->options[end + OPT_DATA + len] = DHCP_END;
99
100}
101
102
103/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
104int send_discover(unsigned long xid, unsigned long requested)
105{
106 struct dhcpMessage packet;
107
108 init_packet(&packet, DHCPDISCOVER);
109 packet.xid = xid;
110 if (requested)
111 add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
112
113 add_requests(&packet);
114 LOG(LOG_DEBUG, "Sending discover...");
115 return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
116 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
117}
118
119
120/* Broadcasts a DHCP request message */
121int send_selecting(unsigned long xid, unsigned long server, unsigned long requested)
122{
123 struct dhcpMessage packet;
124 struct in_addr addr;
125
126 init_packet(&packet, DHCPREQUEST);
127 packet.xid = xid;
128
129 add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
130 add_simple_option(packet.options, DHCP_SERVER_ID, server);
131
132 add_requests(&packet);
133 addr.s_addr = requested;
134 LOG(LOG_DEBUG, "Sending select for %s...", inet_ntoa(addr));
135 return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
136 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
137}
138
139
140/* Unicasts or broadcasts a DHCP renew message */
141int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr)
142{
143 struct dhcpMessage packet;
144 int ret = 0;
145
146 init_packet(&packet, DHCPREQUEST);
147 packet.xid = xid;
148 packet.ciaddr = ciaddr;
149
150 add_requests(&packet);
151 LOG(LOG_DEBUG, "Sending renew...");
152 if (server)
153 ret = kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
154 else ret = raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
155 SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
156 return ret;
157}
158
159
160/* Unicasts a DHCP release message */
161int send_release(unsigned long server, unsigned long ciaddr)
162{
163 struct dhcpMessage packet;
164
165 init_packet(&packet, DHCPRELEASE);
166 packet.xid = random_xid();
167 packet.ciaddr = ciaddr;
168
169 add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr);
170 add_simple_option(packet.options, DHCP_SERVER_ID, server);
171
172 LOG(LOG_DEBUG, "Sending release...");
173 return kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
174}
175
176
177/* return -1 on errors that are fatal for the socket, -2 for those that aren't */
178int get_raw_packet(struct dhcpMessage *payload, int fd)
179{
180 int bytes;
181 struct udp_dhcp_packet packet;
182 uint32_t source, dest;
183 uint16_t check;
184
185 memset(&packet, 0, sizeof(struct udp_dhcp_packet));
186 bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet));
187 if (bytes < 0) {
188 DEBUG(LOG_INFO, "couldn't read on raw listening socket -- ignoring");
189 usleep(500000); /* possible down interface, looping condition */
190 return -1;
191 }
192
193 if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) {
194 DEBUG(LOG_INFO, "message too short, ignoring");
195 return -2;
196 }
197
198 if (bytes < ntohs(packet.ip.tot_len)) {
199 DEBUG(LOG_INFO, "Truncated packet");
200 return -2;
201 }
202
203 /* ignore any extra garbage bytes */
204 bytes = ntohs(packet.ip.tot_len);
205
206 /* Make sure its the right packet for us, and that it passes sanity checks */
207 if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION ||
208 packet.ip.ihl != sizeof(packet.ip) >> 2 || packet.udp.dest != htons(CLIENT_PORT) ||
209 bytes > (int) sizeof(struct udp_dhcp_packet) ||
210 ntohs(packet.udp.len) != (uint16_t) (bytes - sizeof(packet.ip))) {
211 DEBUG(LOG_INFO, "unrelated/bogus packet");
212 return -2;
213 }
214
215 /* check IP checksum */
216 check = packet.ip.check;
217 packet.ip.check = 0;
218 if (check != checksum(&(packet.ip), sizeof(packet.ip))) {
219 DEBUG(LOG_INFO, "bad IP header checksum, ignoring");
220 return -1;
221 }
222
223 /* verify the UDP checksum by replacing the header with a psuedo header */
224 source = packet.ip.saddr;
225 dest = packet.ip.daddr;
226 check = packet.udp.check;
227 packet.udp.check = 0;
228 memset(&packet.ip, 0, sizeof(packet.ip));
229
230 packet.ip.protocol = IPPROTO_UDP;
231 packet.ip.saddr = source;
232 packet.ip.daddr = dest;
233 packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */
234 if (check && check != checksum(&packet, bytes)) {
235 DEBUG(LOG_ERR, "packet with bad UDP checksum received, ignoring");
236 return -2;
237 }
238
239 memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp)));
240
241 if (ntohl(payload->cookie) != DHCP_MAGIC) {
242 LOG(LOG_ERR, "received bogus message (bad magic) -- ignoring");
243 return -2;
244 }
245 DEBUG(LOG_INFO, "oooooh!!! got some!");
246 return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
247
248}
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 @@
1#ifndef _CLIENTPACKET_H
2#define _CLIENTPACKET_H
3
4#include "packet.h"
5
6unsigned long random_xid(void);
7int send_discover(unsigned long xid, unsigned long requested);
8int send_selecting(unsigned long xid, unsigned long server, unsigned long requested);
9int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
10int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
11int send_release(unsigned long server, unsigned long ciaddr);
12int get_raw_packet(struct dhcpMessage *payload, int fd);
13
14#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 @@
1/*
2 * clientsocket.c -- DHCP client socket creation
3 *
4 * udhcp client
5 *
6 * Russ Dill <Russ.Dill@asu.edu> July 2001
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23#include <sys/types.h>
24#include <sys/socket.h>
25#include <unistd.h>
26#include <netinet/in.h>
27#include <features.h>
28#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
29#include <netpacket/packet.h>
30#include <net/ethernet.h>
31#else
32#include <asm/types.h>
33#include <linux/if_packet.h>
34#include <linux/if_ether.h>
35#endif
36
37#include "clientsocket.h"
38#include "common.h"
39
40
41int raw_socket(int ifindex)
42{
43 int fd;
44 struct sockaddr_ll sock;
45
46 DEBUG(LOG_INFO, "Opening raw socket on ifindex %d", ifindex);
47 if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
48 DEBUG(LOG_ERR, "socket call failed: %m");
49 return -1;
50 }
51
52 sock.sll_family = AF_PACKET;
53 sock.sll_protocol = htons(ETH_P_IP);
54 sock.sll_ifindex = ifindex;
55 if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) {
56 DEBUG(LOG_ERR, "bind call failed: %m");
57 close(fd);
58 return -1;
59 }
60
61 return fd;
62}
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 @@
1/* clientsocket.h */
2#ifndef _CLIENTSOCKET_H
3#define _CLIENTSOCKET_H
4
5int raw_socket(int ifindex);
6
7#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 @@
1/* common.c
2 *
3 * Functions for debugging and logging as well as some other
4 * simple helper functions.
5 *
6 * Russ Dill <Russ.Dill@asu.edu> 2001-2003
7 * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24#include <fcntl.h>
25#include <unistd.h>
26#include <errno.h>
27#include <string.h>
28#include <stdlib.h>
29#include <signal.h>
30#include <paths.h>
31#include <sys/socket.h>
32#include <stdarg.h>
33
34#include "common.h"
35#include "pidfile.h"
36
37
38static int daemonized;
39
40long uptime(void)
41{
42 struct sysinfo info;
43 sysinfo(&info);
44 return info.uptime;
45}
46
47
48/*
49 * This function makes sure our first socket calls
50 * aren't going to fd 1 (printf badness...) and are
51 * not later closed by daemon()
52 */
53static inline void sanitize_fds(void)
54{
55 int zero;
56 if ((zero = open(_PATH_DEVNULL, O_RDWR, 0)) < 0) return;
57 while (zero < 3) zero = dup(zero);
58 close(zero);
59}
60
61
62void background(const char *pidfile)
63{
64#ifdef __uClinux__
65 LOG(LOG_ERR, "Cannot background in uclinux (yet)");
66#else /* __uClinux__ */
67 int pid_fd;
68
69 /* hold lock during fork. */
70 pid_fd = pidfile_acquire(pidfile);
71 if (daemon(0, 0) == -1) {
72 perror("fork");
73 exit(1);
74 }
75 daemonized++;
76 pidfile_write_release(pid_fd);
77#endif /* __uClinux__ */
78}
79
80
81#ifdef UDHCP_SYSLOG
82void udhcp_logging(int level, const char *fmt, ...)
83{
84 va_list p;
85 va_list p2;
86
87 va_start(p, fmt);
88 __va_copy(p2, p);
89 if(!daemonized) {
90 vprintf(fmt, p);
91 putchar('\n');
92 }
93 vsyslog(level, fmt, p2);
94 va_end(p);
95}
96
97
98void start_log_and_pid(const char *client_server, const char *pidfile)
99{
100 int pid_fd;
101
102 /* Make sure our syslog fd isn't overwritten */
103 sanitize_fds();
104
105 /* do some other misc startup stuff while we are here to save bytes */
106 pid_fd = pidfile_acquire(pidfile);
107 pidfile_write_release(pid_fd);
108
109 /* equivelent of doing a fflush after every \n */
110 setlinebuf(stdout);
111
112 openlog(client_server, LOG_PID | LOG_CONS, LOG_LOCAL0);
113 udhcp_logging(LOG_INFO, "%s (v%s) started", client_server, VERSION);
114}
115
116
117#else
118
119
120static char *syslog_level_msg[] = {
121 [LOG_EMERG] = "EMERGENCY!",
122 [LOG_ALERT] = "ALERT!",
123 [LOG_CRIT] = "critical!",
124 [LOG_WARNING] = "warning",
125 [LOG_ERR] = "error",
126 [LOG_INFO] = "info",
127 [LOG_DEBUG] = "debug"
128};
129
130
131void udhcp_logging(int level, const char *fmt, ...)
132{
133 va_list p;
134
135 va_start(p, fmt);
136 if(!daemonized) {
137 printf("%s, ", syslog_level_msg[level]);
138 vprintf(fmt, p);
139 putchar('\n');
140 }
141 va_end(p);
142}
143
144
145void start_log_and_pid(const char *client_server, const char *pidfile)
146{
147 int pid_fd;
148
149 /* Make sure our syslog fd isn't overwritten */
150 sanitize_fds();
151
152 /* do some other misc startup stuff while we are here to save bytes */
153 pid_fd = pidfile_acquire(pidfile);
154 pidfile_write_release(pid_fd);
155
156 /* equivelent of doing a fflush after every \n */
157 setlinebuf(stdout);
158
159 udhcp_logging(LOG_INFO, "%s (v%s) started", client_server, VERSION);
160}
161#endif
162
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 @@
1/* common.h
2 *
3 * Russ Dill <Russ.Dill@asu.edu> September 2001
4 * Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21#ifndef _COMMON_H
22#define _COMMON_H
23
24#include "version.h"
25#include "libbb_udhcp.h"
26
27
28#ifndef UDHCP_SYSLOG
29enum syslog_levels {
30 LOG_EMERG = 0,
31 LOG_ALERT,
32 LOG_CRIT,
33 LOG_WARNING,
34 LOG_ERR,
35 LOG_INFO,
36 LOG_DEBUG
37};
38#else
39#include <syslog.h>
40#endif
41
42long uptime(void);
43void background(const char *pidfile);
44void start_log_and_pid(const char *client_server, const char *pidfile);
45void background(const char *pidfile);
46void udhcp_logging(int level, const char *fmt, ...);
47
48#define LOG(level, str, args...) udhcp_logging(level, str, ## args)
49
50#ifdef UDHCP_DEBUG
51# define DEBUG(level, str, args...) LOG(level, str, ## args)
52#else
53# define DEBUG(level, str, args...) do {;} while(0)
54#endif
55
56#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 @@
1/* dhcpc.c
2 *
3 * udhcp DHCP client
4 *
5 * Russ Dill <Russ.Dill@asu.edu> July 2001
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include <sys/time.h>
23#include <sys/file.h>
24#include <unistd.h>
25#include <getopt.h>
26#include <stdlib.h>
27#include <sys/socket.h>
28#include <netinet/in.h>
29#include <arpa/inet.h>
30#include <signal.h>
31#include <time.h>
32#include <string.h>
33#include <sys/ioctl.h>
34#include <net/if.h>
35#include <errno.h>
36
37#include "dhcpd.h"
38#include "dhcpc.h"
39#include "options.h"
40#include "clientpacket.h"
41#include "clientsocket.h"
42#include "script.h"
43#include "socket.h"
44#include "common.h"
45#include "signalpipe.h"
46
47static int state;
48static unsigned long requested_ip; /* = 0 */
49static unsigned long server_addr;
50static unsigned long timeout;
51static int packet_num; /* = 0 */
52static int fd = -1;
53
54#define LISTEN_NONE 0
55#define LISTEN_KERNEL 1
56#define LISTEN_RAW 2
57static int listen_mode;
58
59struct client_config_t client_config = {
60 /* Default options. */
61 abort_if_no_lease: 0,
62 foreground: 0,
63 quit_after_lease: 0,
64 background_if_no_lease: 0,
65 interface: "eth0",
66 pidfile: NULL,
67 script: DEFAULT_SCRIPT,
68 clientid: NULL,
69 hostname: NULL,
70 ifindex: 0,
71 arp: "\0\0\0\0\0\0", /* appease gcc-3.0 */
72};
73
74#ifndef IN_BUSYBOX
75static void __attribute__ ((noreturn)) show_usage(void)
76{
77 printf(
78"Usage: udhcpc [OPTIONS]\n\n"
79" -c, --clientid=CLIENTID Client identifier\n"
80" -H, --hostname=HOSTNAME Client hostname\n"
81" -h Alias for -H\n"
82" -f, --foreground Do not fork after getting lease\n"
83" -b, --background Fork to background if lease cannot be\n"
84" immediately negotiated.\n"
85" -i, --interface=INTERFACE Interface to use (default: eth0)\n"
86" -n, --now Exit with failure if lease cannot be\n"
87" immediately negotiated.\n"
88" -p, --pidfile=file Store process ID of daemon in file\n"
89" -q, --quit Quit after obtaining lease\n"
90" -r, --request=IP IP address to request (default: none)\n"
91" -s, --script=file Run file at dhcp events (default:\n"
92" " DEFAULT_SCRIPT ")\n"
93" -v, --version Display version\n"
94 );
95 exit(0);
96}
97#else
98#define show_usage bb_show_usage
99extern void show_usage(void) __attribute__ ((noreturn));
100#endif
101
102
103/* just a little helper */
104static void change_mode(int new_mode)
105{
106 DEBUG(LOG_INFO, "entering %s listen mode",
107 new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none");
108 if (fd >= 0) close(fd);
109 fd = -1;
110 listen_mode = new_mode;
111}
112
113
114/* perform a renew */
115static void perform_renew(void)
116{
117 LOG(LOG_INFO, "Performing a DHCP renew");
118 switch (state) {
119 case BOUND:
120 change_mode(LISTEN_KERNEL);
121 case RENEWING:
122 case REBINDING:
123 state = RENEW_REQUESTED;
124 break;
125 case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
126 run_script(NULL, "deconfig");
127 case REQUESTING:
128 case RELEASED:
129 change_mode(LISTEN_RAW);
130 state = INIT_SELECTING;
131 break;
132 case INIT_SELECTING:
133 break;
134 }
135
136 /* start things over */
137 packet_num = 0;
138
139 /* Kill any timeouts because the user wants this to hurry along */
140 timeout = 0;
141}
142
143
144/* perform a release */
145static void perform_release(void)
146{
147 char buffer[16];
148 struct in_addr temp_addr;
149
150 /* send release packet */
151 if (state == BOUND || state == RENEWING || state == REBINDING) {
152 temp_addr.s_addr = server_addr;
153 sprintf(buffer, "%s", inet_ntoa(temp_addr));
154 temp_addr.s_addr = requested_ip;
155 LOG(LOG_INFO, "Unicasting a release of %s to %s",
156 inet_ntoa(temp_addr), buffer);
157 send_release(server_addr, requested_ip); /* unicast */
158 run_script(NULL, "deconfig");
159 }
160 LOG(LOG_INFO, "Entering released state");
161
162 change_mode(LISTEN_NONE);
163 state = RELEASED;
164 timeout = 0x7fffffff;
165}
166
167
168static void client_background(void)
169{
170 background(client_config.pidfile);
171 client_config.foreground = 1; /* Do not fork again. */
172 client_config.background_if_no_lease = 0;
173}
174
175
176#ifdef COMBINED_BINARY
177int udhcpc_main(int argc, char *argv[])
178#else
179int main(int argc, char *argv[])
180#endif
181{
182 uint8_t *temp, *message;
183 unsigned long t1 = 0, t2 = 0, xid = 0;
184 unsigned long start = 0, lease;
185 fd_set rfds;
186 int retval;
187 struct timeval tv;
188 int c, len;
189 struct dhcpMessage packet;
190 struct in_addr temp_addr;
191 long now;
192 int max_fd;
193 int sig;
194
195 static const struct option arg_options[] = {
196 {"clientid", required_argument, 0, 'c'},
197 {"foreground", no_argument, 0, 'f'},
198 {"background", no_argument, 0, 'b'},
199 {"hostname", required_argument, 0, 'H'},
200 {"hostname", required_argument, 0, 'h'},
201 {"interface", required_argument, 0, 'i'},
202 {"now", no_argument, 0, 'n'},
203 {"pidfile", required_argument, 0, 'p'},
204 {"quit", no_argument, 0, 'q'},
205 {"request", required_argument, 0, 'r'},
206 {"script", required_argument, 0, 's'},
207 {"version", no_argument, 0, 'v'},
208 {0, 0, 0, 0}
209 };
210
211 /* get options */
212 while (1) {
213 int option_index = 0;
214 c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:s:v", arg_options, &option_index);
215 if (c == -1) break;
216
217 switch (c) {
218 case 'c':
219 len = strlen(optarg) > 255 ? 255 : strlen(optarg);
220 if (client_config.clientid) free(client_config.clientid);
221 client_config.clientid = xmalloc(len + 2);
222 client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
223 client_config.clientid[OPT_LEN] = len;
224 client_config.clientid[OPT_DATA] = '\0';
225 strncpy(client_config.clientid + OPT_DATA, optarg, len);
226 break;
227 case 'f':
228 client_config.foreground = 1;
229 break;
230 case 'b':
231 client_config.background_if_no_lease = 1;
232 break;
233 case 'h':
234 case 'H':
235 len = strlen(optarg) > 255 ? 255 : strlen(optarg);
236 if (client_config.hostname) free(client_config.hostname);
237 client_config.hostname = xmalloc(len + 2);
238 client_config.hostname[OPT_CODE] = DHCP_HOST_NAME;
239 client_config.hostname[OPT_LEN] = len;
240 strncpy(client_config.hostname + 2, optarg, len);
241 break;
242 case 'i':
243 client_config.interface = optarg;
244 break;
245 case 'n':
246 client_config.abort_if_no_lease = 1;
247 break;
248 case 'p':
249 client_config.pidfile = optarg;
250 break;
251 case 'q':
252 client_config.quit_after_lease = 1;
253 break;
254 case 'r':
255 requested_ip = inet_addr(optarg);
256 break;
257 case 's':
258 client_config.script = optarg;
259 break;
260 case 'v':
261 printf("udhcpcd, version %s\n\n", VERSION);
262 return 0;
263 break;
264 default:
265 show_usage();
266 }
267 }
268
269 /* Start the log, sanitize fd's, and write a pid file */
270 start_log_and_pid("udhcpc", client_config.pidfile);
271
272 if (read_interface(client_config.interface, &client_config.ifindex,
273 NULL, client_config.arp) < 0)
274 return 1;
275
276 if (!client_config.clientid) {
277 client_config.clientid = xmalloc(6 + 3);
278 client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
279 client_config.clientid[OPT_LEN] = 7;
280 client_config.clientid[OPT_DATA] = 1;
281 memcpy(client_config.clientid + 3, client_config.arp, 6);
282 }
283
284 /* setup the signal pipe */
285 udhcp_sp_setup();
286
287 state = INIT_SELECTING;
288 run_script(NULL, "deconfig");
289 change_mode(LISTEN_RAW);
290
291 for (;;) {
292
293 tv.tv_sec = timeout - uptime();
294 tv.tv_usec = 0;
295
296 if (listen_mode != LISTEN_NONE && fd < 0) {
297 if (listen_mode == LISTEN_KERNEL)
298 fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface);
299 else
300 fd = raw_socket(client_config.ifindex);
301 if (fd < 0) {
302 LOG(LOG_ERR, "FATAL: couldn't listen on socket, %m");
303 return 0;
304 }
305 }
306 max_fd = udhcp_sp_fd_set(&rfds, fd);
307
308 if (tv.tv_sec > 0) {
309 DEBUG(LOG_INFO, "Waiting on select...");
310 retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
311 } else retval = 0; /* If we already timed out, fall through */
312
313 now = uptime();
314 if (retval == 0) {
315 /* timeout dropped to zero */
316 switch (state) {
317 case INIT_SELECTING:
318 if (packet_num < 3) {
319 if (packet_num == 0)
320 xid = random_xid();
321
322 /* send discover packet */
323 send_discover(xid, requested_ip); /* broadcast */
324
325 timeout = now + ((packet_num == 2) ? 4 : 2);
326 packet_num++;
327 } else {
328 run_script(NULL, "leasefail");
329 if (client_config.background_if_no_lease) {
330 LOG(LOG_INFO, "No lease, forking to background.");
331 client_background();
332 } else if (client_config.abort_if_no_lease) {
333 LOG(LOG_INFO, "No lease, failing.");
334 return 1;
335 }
336 /* wait to try again */
337 packet_num = 0;
338 timeout = now + 60;
339 }
340 break;
341 case RENEW_REQUESTED:
342 case REQUESTING:
343 if (packet_num < 3) {
344 /* send request packet */
345 if (state == RENEW_REQUESTED)
346 send_renew(xid, server_addr, requested_ip); /* unicast */
347 else send_selecting(xid, server_addr, requested_ip); /* broadcast */
348
349 timeout = now + ((packet_num == 2) ? 10 : 2);
350 packet_num++;
351 } else {
352 /* timed out, go back to init state */
353 if (state == RENEW_REQUESTED) run_script(NULL, "deconfig");
354 state = INIT_SELECTING;
355 timeout = now;
356 packet_num = 0;
357 change_mode(LISTEN_RAW);
358 }
359 break;
360 case BOUND:
361 /* Lease is starting to run out, time to enter renewing state */
362 state = RENEWING;
363 change_mode(LISTEN_KERNEL);
364 DEBUG(LOG_INFO, "Entering renew state");
365 /* fall right through */
366 case RENEWING:
367 /* Either set a new T1, or enter REBINDING state */
368 if ((t2 - t1) <= (lease / 14400 + 1)) {
369 /* timed out, enter rebinding state */
370 state = REBINDING;
371 timeout = now + (t2 - t1);
372 DEBUG(LOG_INFO, "Entering rebinding state");
373 } else {
374 /* send a request packet */
375 send_renew(xid, server_addr, requested_ip); /* unicast */
376
377 t1 = (t2 - t1) / 2 + t1;
378 timeout = t1 + start;
379 }
380 break;
381 case REBINDING:
382 /* Either set a new T2, or enter INIT state */
383 if ((lease - t2) <= (lease / 14400 + 1)) {
384 /* timed out, enter init state */
385 state = INIT_SELECTING;
386 LOG(LOG_INFO, "Lease lost, entering init state");
387 run_script(NULL, "deconfig");
388 timeout = now;
389 packet_num = 0;
390 change_mode(LISTEN_RAW);
391 } else {
392 /* send a request packet */
393 send_renew(xid, 0, requested_ip); /* broadcast */
394
395 t2 = (lease - t2) / 2 + t2;
396 timeout = t2 + start;
397 }
398 break;
399 case RELEASED:
400 /* yah, I know, *you* say it would never happen */
401 timeout = 0x7fffffff;
402 break;
403 }
404 } else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) {
405 /* a packet is ready, read it */
406
407 if (listen_mode == LISTEN_KERNEL)
408 len = get_packet(&packet, fd);
409 else len = get_raw_packet(&packet, fd);
410
411 if (len == -1 && errno != EINTR) {
412 DEBUG(LOG_INFO, "error on read, %m, reopening socket");
413 change_mode(listen_mode); /* just close and reopen */
414 }
415 if (len < 0) continue;
416
417 if (packet.xid != xid) {
418 DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)",
419 (unsigned long) packet.xid, xid);
420 continue;
421 }
422
423 if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
424 DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring");
425 continue;
426 }
427
428 switch (state) {
429 case INIT_SELECTING:
430 /* Must be a DHCPOFFER to one of our xid's */
431 if (*message == DHCPOFFER) {
432 if ((temp = get_option(&packet, DHCP_SERVER_ID))) {
433 memcpy(&server_addr, temp, 4);
434 xid = packet.xid;
435 requested_ip = packet.yiaddr;
436
437 /* enter requesting state */
438 state = REQUESTING;
439 timeout = now;
440 packet_num = 0;
441 } else {
442 DEBUG(LOG_ERR, "No server ID in message");
443 }
444 }
445 break;
446 case RENEW_REQUESTED:
447 case REQUESTING:
448 case RENEWING:
449 case REBINDING:
450 if (*message == DHCPACK) {
451 if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) {
452 LOG(LOG_ERR, "No lease time with ACK, using 1 hour lease");
453 lease = 60 * 60;
454 } else {
455 memcpy(&lease, temp, 4);
456 lease = ntohl(lease);
457 }
458
459 /* enter bound state */
460 t1 = lease / 2;
461
462 /* little fixed point for n * .875 */
463 t2 = (lease * 0x7) >> 3;
464 temp_addr.s_addr = packet.yiaddr;
465 LOG(LOG_INFO, "Lease of %s obtained, lease time %ld",
466 inet_ntoa(temp_addr), lease);
467 start = now;
468 timeout = t1 + start;
469 requested_ip = packet.yiaddr;
470 run_script(&packet,
471 ((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
472
473 state = BOUND;
474 change_mode(LISTEN_NONE);
475 if (client_config.quit_after_lease)
476 return 0;
477 if (!client_config.foreground)
478 client_background();
479
480 } else if (*message == DHCPNAK) {
481 /* return to init state */
482 LOG(LOG_INFO, "Received DHCP NAK");
483 run_script(&packet, "nak");
484 if (state != REQUESTING)
485 run_script(NULL, "deconfig");
486 state = INIT_SELECTING;
487 timeout = now;
488 requested_ip = 0;
489 packet_num = 0;
490 change_mode(LISTEN_RAW);
491 sleep(3); /* avoid excessive network traffic */
492 }
493 break;
494 /* case BOUND, RELEASED: - ignore all packets */
495 }
496 } else if (retval > 0 && (sig = udhcp_sp_read(&rfds))) {
497 switch (sig) {
498 case SIGUSR1:
499 perform_renew();
500 break;
501 case SIGUSR2:
502 perform_release();
503 break;
504 case SIGTERM:
505 LOG(LOG_INFO, "Received SIGTERM");
506 return 0;
507 }
508 } else if (retval == -1 && errno == EINTR) {
509 /* a signal was caught */
510 } else {
511 /* An error occured */
512 DEBUG(LOG_ERR, "Error on select");
513 }
514
515 }
516 return 0;
517}
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 @@
1/* dhcpc.h */
2#ifndef _DHCPC_H
3#define _DHCPC_H
4
5#define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script"
6
7/* allow libbb_udhcp.h to redefine DEFAULT_SCRIPT */
8#include "libbb_udhcp.h"
9
10#define INIT_SELECTING 0
11#define REQUESTING 1
12#define BOUND 2
13#define RENEWING 3
14#define REBINDING 4
15#define INIT_REBOOT 5
16#define RENEW_REQUESTED 6
17#define RELEASED 7
18
19
20struct client_config_t {
21 char foreground; /* Do not fork */
22 char quit_after_lease; /* Quit after obtaining lease */
23 char abort_if_no_lease; /* Abort if no lease */
24 char background_if_no_lease; /* Fork to background if no lease */
25 char *interface; /* The name of the interface to use */
26 char *pidfile; /* Optionally store the process ID */
27 char *script; /* User script to run at dhcp events */
28 uint8_t *clientid; /* Optional client id to use */
29 uint8_t *hostname; /* Optional hostname to use */
30 int ifindex; /* Index number of the interface to use */
31 uint8_t arp[6]; /* Our arp address */
32};
33
34extern struct client_config_t client_config;
35
36
37#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 @@
1/* dhcpd.c
2 *
3 * udhcp Server
4 * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
5 * Chris Trew <ctrew@moreton.com.au>
6 *
7 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24#include <fcntl.h>
25#include <string.h>
26#include <stdlib.h>
27#include <sys/wait.h>
28#include <arpa/inet.h>
29#include <netdb.h>
30#include <netinet/in.h>
31#include <sys/socket.h>
32#include <unistd.h>
33#include <signal.h>
34#include <errno.h>
35#include <sys/ioctl.h>
36#include <time.h>
37#include <sys/time.h>
38
39#include "dhcpd.h"
40#include "arpping.h"
41#include "socket.h"
42#include "options.h"
43#include "files.h"
44#include "serverpacket.h"
45#include "common.h"
46#include "signalpipe.h"
47#include "static_leases.h"
48
49
50/* globals */
51struct dhcpOfferedAddr *leases;
52struct server_config_t server_config;
53
54
55#ifdef COMBINED_BINARY
56int udhcpd_main(int argc, char *argv[])
57#else
58int main(int argc, char *argv[])
59#endif
60{
61 fd_set rfds;
62 struct timeval tv;
63 int server_socket = -1;
64 int bytes, retval;
65 struct dhcpMessage packet;
66 uint8_t *state;
67 uint8_t *server_id, *requested;
68 uint32_t server_id_align, requested_align;
69 unsigned long timeout_end;
70 struct option_set *option;
71 struct dhcpOfferedAddr *lease;
72 struct dhcpOfferedAddr static_lease;
73 int max_sock;
74 unsigned long num_ips;
75
76 uint32_t static_lease_ip;
77
78 memset(&server_config, 0, sizeof(struct server_config_t));
79 read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]);
80
81 /* Start the log, sanitize fd's, and write a pid file */
82 start_log_and_pid("udhcpd", server_config.pidfile);
83
84 if ((option = find_option(server_config.options, DHCP_LEASE_TIME))) {
85 memcpy(&server_config.lease, option->data + 2, 4);
86 server_config.lease = ntohl(server_config.lease);
87 }
88 else server_config.lease = LEASE_TIME;
89
90 /* Sanity check */
91 num_ips = ntohl(server_config.end) - ntohl(server_config.start);
92 if (server_config.max_leases > num_ips) {
93 LOG(LOG_ERR, "max_leases value (%lu) not sane, "
94 "setting to %lu instead",
95 server_config.max_leases, num_ips);
96 server_config.max_leases = num_ips;
97 }
98
99 leases = xcalloc(server_config.max_leases, sizeof(struct dhcpOfferedAddr));
100 read_leases(server_config.lease_file);
101
102 if (read_interface(server_config.interface, &server_config.ifindex,
103 &server_config.server, server_config.arp) < 0)
104 return 1;
105
106#ifndef UDHCP_DEBUG
107 background(server_config.pidfile); /* hold lock during fork. */
108#endif
109
110 /* Setup the signal pipe */
111 udhcp_sp_setup();
112
113 timeout_end = time(0) + server_config.auto_time;
114 while(1) { /* loop until universe collapses */
115
116 if (server_socket < 0)
117 if ((server_socket = listen_socket(INADDR_ANY, SERVER_PORT, server_config.interface)) < 0) {
118 LOG(LOG_ERR, "FATAL: couldn't create server socket, %m");
119 return 2;
120 }
121
122 max_sock = udhcp_sp_fd_set(&rfds, server_socket);
123 if (server_config.auto_time) {
124 tv.tv_sec = timeout_end - time(0);
125 tv.tv_usec = 0;
126 }
127 if (!server_config.auto_time || tv.tv_sec > 0) {
128 retval = select(max_sock + 1, &rfds, NULL, NULL,
129 server_config.auto_time ? &tv : NULL);
130 } else retval = 0; /* If we already timed out, fall through */
131
132 if (retval == 0) {
133 write_leases();
134 timeout_end = time(0) + server_config.auto_time;
135 continue;
136 } else if (retval < 0 && errno != EINTR) {
137 DEBUG(LOG_INFO, "error on select");
138 continue;
139 }
140
141 switch (udhcp_sp_read(&rfds)) {
142 case SIGUSR1:
143 LOG(LOG_INFO, "Received a SIGUSR1");
144 write_leases();
145 /* why not just reset the timeout, eh */
146 timeout_end = time(0) + server_config.auto_time;
147 continue;
148 case SIGTERM:
149 LOG(LOG_INFO, "Received a SIGTERM");
150 return 0;
151 case 0: break; /* no signal */
152 default: continue; /* signal or error (probably EINTR) */
153 }
154
155 if ((bytes = get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */
156 if (bytes == -1 && errno != EINTR) {
157 DEBUG(LOG_INFO, "error on read, %m, reopening socket");
158 close(server_socket);
159 server_socket = -1;
160 }
161 continue;
162 }
163
164 if ((state = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
165 DEBUG(LOG_ERR, "couldn't get option from packet, ignoring");
166 continue;
167 }
168
169 /* Look for a static lease */
170 static_lease_ip = getIpByMac(server_config.static_leases, &packet.chaddr);
171
172 if(static_lease_ip)
173 {
174 printf("Found static lease: %x\n", static_lease_ip);
175
176 memcpy(&static_lease.chaddr, &packet.chaddr, 16);
177 static_lease.yiaddr = static_lease_ip;
178 static_lease.expires = 0;
179
180 lease = &static_lease;
181
182 }
183 else
184 {
185 lease = find_lease_by_chaddr(packet.chaddr);
186 }
187
188 switch (state[0]) {
189 case DHCPDISCOVER:
190 DEBUG(LOG_INFO,"received DISCOVER");
191
192 if (sendOffer(&packet) < 0) {
193 LOG(LOG_ERR, "send OFFER failed");
194 }
195 break;
196 case DHCPREQUEST:
197 DEBUG(LOG_INFO, "received REQUEST");
198
199 requested = get_option(&packet, DHCP_REQUESTED_IP);
200 server_id = get_option(&packet, DHCP_SERVER_ID);
201
202 if (requested) memcpy(&requested_align, requested, 4);
203 if (server_id) memcpy(&server_id_align, server_id, 4);
204
205 if (lease) {
206 if (server_id) {
207 /* SELECTING State */
208 DEBUG(LOG_INFO, "server_id = %08x", ntohl(server_id_align));
209 if (server_id_align == server_config.server && requested &&
210 requested_align == lease->yiaddr) {
211 sendACK(&packet, lease->yiaddr);
212 }
213 } else {
214 if (requested) {
215 /* INIT-REBOOT State */
216 if (lease->yiaddr == requested_align)
217 sendACK(&packet, lease->yiaddr);
218 else sendNAK(&packet);
219 } else {
220 /* RENEWING or REBINDING State */
221 if (lease->yiaddr == packet.ciaddr)
222 sendACK(&packet, lease->yiaddr);
223 else {
224 /* don't know what to do!!!! */
225 sendNAK(&packet);
226 }
227 }
228 }
229
230 /* what to do if we have no record of the client */
231 } else if (server_id) {
232 /* SELECTING State */
233
234 } else if (requested) {
235 /* INIT-REBOOT State */
236 if ((lease = find_lease_by_yiaddr(requested_align))) {
237 if (lease_expired(lease)) {
238 /* probably best if we drop this lease */
239 memset(lease->chaddr, 0, 16);
240 /* make some contention for this address */
241 } else sendNAK(&packet);
242 } else if (requested_align < server_config.start ||
243 requested_align > server_config.end) {
244 sendNAK(&packet);
245 } /* else remain silent */
246
247 } else {
248 /* RENEWING or REBINDING State */
249 }
250 break;
251 case DHCPDECLINE:
252 DEBUG(LOG_INFO,"received DECLINE");
253 if (lease) {
254 memset(lease->chaddr, 0, 16);
255 lease->expires = time(0) + server_config.decline_time;
256 }
257 break;
258 case DHCPRELEASE:
259 DEBUG(LOG_INFO,"received RELEASE");
260 if (lease) lease->expires = time(0);
261 break;
262 case DHCPINFORM:
263 DEBUG(LOG_INFO,"received INFORM");
264 send_inform(&packet);
265 break;
266 default:
267 LOG(LOG_WARNING, "unsupported DHCP message (%02x) -- ignoring", state[0]);
268 }
269 }
270
271 return 0;
272}
273
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 @@
1/* dhcpd.h */
2#ifndef _DHCPD_H
3#define _DHCPD_H
4
5#include <netinet/ip.h>
6#include <netinet/udp.h>
7
8#include "libbb_udhcp.h"
9#include "leases.h"
10#include "version.h"
11
12/************************************/
13/* Defaults _you_ may want to tweak */
14/************************************/
15
16/* the period of time the client is allowed to use that address */
17#define LEASE_TIME (60*60*24*10) /* 10 days of seconds */
18#define LEASES_FILE "/var/lib/misc/udhcpd.leases"
19
20/* where to find the DHCP server configuration file */
21#define DHCPD_CONF_FILE "/etc/udhcpd.conf"
22
23/*****************************************************************/
24/* Do not modify below here unless you know what you are doing!! */
25/*****************************************************************/
26
27/* DHCP protocol -- see RFC 2131 */
28#define SERVER_PORT 67
29#define CLIENT_PORT 68
30
31#define DHCP_MAGIC 0x63825363
32
33/* DHCP option codes (partial list) */
34#define DHCP_PADDING 0x00
35#define DHCP_SUBNET 0x01
36#define DHCP_TIME_OFFSET 0x02
37#define DHCP_ROUTER 0x03
38#define DHCP_TIME_SERVER 0x04
39#define DHCP_NAME_SERVER 0x05
40#define DHCP_DNS_SERVER 0x06
41#define DHCP_LOG_SERVER 0x07
42#define DHCP_COOKIE_SERVER 0x08
43#define DHCP_LPR_SERVER 0x09
44#define DHCP_HOST_NAME 0x0c
45#define DHCP_BOOT_SIZE 0x0d
46#define DHCP_DOMAIN_NAME 0x0f
47#define DHCP_SWAP_SERVER 0x10
48#define DHCP_ROOT_PATH 0x11
49#define DHCP_IP_TTL 0x17
50#define DHCP_MTU 0x1a
51#define DHCP_BROADCAST 0x1c
52#define DHCP_NTP_SERVER 0x2a
53#define DHCP_WINS_SERVER 0x2c
54#define DHCP_REQUESTED_IP 0x32
55#define DHCP_LEASE_TIME 0x33
56#define DHCP_OPTION_OVER 0x34
57#define DHCP_MESSAGE_TYPE 0x35
58#define DHCP_SERVER_ID 0x36
59#define DHCP_PARAM_REQ 0x37
60#define DHCP_MESSAGE 0x38
61#define DHCP_MAX_SIZE 0x39
62#define DHCP_T1 0x3a
63#define DHCP_T2 0x3b
64#define DHCP_VENDOR 0x3c
65#define DHCP_CLIENT_ID 0x3d
66
67#define DHCP_END 0xFF
68
69
70#define BOOTREQUEST 1
71#define BOOTREPLY 2
72
73#define ETH_10MB 1
74#define ETH_10MB_LEN 6
75
76#define DHCPDISCOVER 1
77#define DHCPOFFER 2
78#define DHCPREQUEST 3
79#define DHCPDECLINE 4
80#define DHCPACK 5
81#define DHCPNAK 6
82#define DHCPRELEASE 7
83#define DHCPINFORM 8
84
85#define BROADCAST_FLAG 0x8000
86
87#define OPTION_FIELD 0
88#define FILE_FIELD 1
89#define SNAME_FIELD 2
90
91/* miscellaneous defines */
92#define MAC_BCAST_ADDR (uint8_t *) "\xff\xff\xff\xff\xff\xff"
93#define OPT_CODE 0
94#define OPT_LEN 1
95#define OPT_DATA 2
96
97struct option_set {
98 uint8_t *data;
99 struct option_set *next;
100};
101
102struct static_lease {
103 uint8_t *mac;
104 uint32_t *ip;
105 struct static_lease *next;
106};
107
108struct server_config_t {
109 uint32_t server; /* Our IP, in network order */
110 uint32_t start; /* Start address of leases, network order */
111 uint32_t end; /* End of leases, network order */
112 struct option_set *options; /* List of DHCP options loaded from the config file */
113 char *interface; /* The name of the interface to use */
114 int ifindex; /* Index number of the interface to use */
115 uint8_t arp[6]; /* Our arp address */
116 unsigned long lease; /* lease time in seconds (host order) */
117 unsigned long max_leases; /* maximum number of leases (including reserved address) */
118 char remaining; /* should the lease file be interpreted as lease time remaining, or
119 * as the time the lease expires */
120 unsigned long auto_time; /* how long should udhcpd wait before writing a config file.
121 * if this is zero, it will only write one on SIGUSR1 */
122 unsigned long decline_time; /* how long an address is reserved if a client returns a
123 * decline message */
124 unsigned long conflict_time; /* how long an arp conflict offender is leased for */
125 unsigned long offer_time; /* how long an offered address is reserved */
126 unsigned long min_lease; /* minimum lease a client can request*/
127 char *lease_file;
128 char *pidfile;
129 char *notify_file; /* What to run whenever leases are written */
130 uint32_t siaddr; /* next server bootp option */
131 char *sname; /* bootp server name */
132 char *boot_file; /* bootp boot file option */
133 struct static_lease *static_leases; /* List of ip/mac pairs to assign static leases */
134};
135
136extern struct server_config_t server_config;
137extern struct dhcpOfferedAddr *leases;
138
139
140#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 @@
1#include <fcntl.h>
2#include <string.h>
3#include <stdlib.h>
4#include <stdio.h>
5#include <sys/wait.h>
6#include <arpa/inet.h>
7#include <netdb.h>
8#include <netinet/in.h>
9#include <stdio.h>
10#include <sys/socket.h>
11#include <unistd.h>
12#include <getopt.h>
13#include <time.h>
14
15#include "dhcpd.h"
16#include "leases.h"
17#include "libbb_udhcp.h"
18
19#define REMAINING 0
20#define ABSOLUTE 1
21
22
23#ifndef IN_BUSYBOX
24static void __attribute__ ((noreturn)) show_usage(void)
25{
26 printf(
27"Usage: dumpleases -f <file> -[r|a]\n\n"
28" -f, --file=FILENAME Leases file to load\n"
29" -r, --remaining Interepret lease times as time remaing\n"
30" -a, --absolute Interepret lease times as expire time\n");
31 exit(0);
32}
33#else
34#define show_usage bb_show_usage
35#endif
36
37
38#ifdef IN_BUSYBOX
39int dumpleases_main(int argc, char *argv[])
40#else
41int main(int argc, char *argv[])
42#endif
43{
44 FILE *fp;
45 int i, c, mode = REMAINING;
46 long expires;
47 const char *file = LEASES_FILE;
48 struct dhcpOfferedAddr lease;
49 struct in_addr addr;
50
51 static const struct option options[] = {
52 {"absolute", 0, 0, 'a'},
53 {"remaining", 0, 0, 'r'},
54 {"file", 1, 0, 'f'},
55 {0, 0, 0, 0}
56 };
57
58 while (1) {
59 int option_index = 0;
60 c = getopt_long(argc, argv, "arf:", options, &option_index);
61 if (c == -1) break;
62
63 switch (c) {
64 case 'a': mode = ABSOLUTE; break;
65 case 'r': mode = REMAINING; break;
66 case 'f':
67 file = optarg;
68 break;
69 default:
70 show_usage();
71 }
72 }
73
74 fp = xfopen(file, "r");
75
76 printf("Mac Address IP-Address Expires %s\n", mode == REMAINING ? "in" : "at");
77 /* "00:00:00:00:00:00 255.255.255.255 Wed Jun 30 21:49:08 1993" */
78 while (fread(&lease, sizeof(lease), 1, fp)) {
79
80 for (i = 0; i < 6; i++) {
81 printf("%02x", lease.chaddr[i]);
82 if (i != 5) printf(":");
83 }
84 addr.s_addr = lease.yiaddr;
85 printf(" %-15s", inet_ntoa(addr));
86 expires = ntohl(lease.expires);
87 printf(" ");
88 if (mode == REMAINING) {
89 if (!expires) printf("expired\n");
90 else {
91 if (expires > 60*60*24) {
92 printf("%ld days, ", expires / (60*60*24));
93 expires %= 60*60*24;
94 }
95 if (expires > 60*60) {
96 printf("%ld hours, ", expires / (60*60));
97 expires %= 60*60;
98 }
99 if (expires > 60) {
100 printf("%ld minutes, ", expires / 60);
101 expires %= 60;
102 }
103 printf("%ld seconds\n", expires);
104 }
105 } else printf("%s", ctime(&expires));
106 }
107 fclose(fp);
108
109 return 0;
110}
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 @@
1/*
2 * files.c -- DHCP server file manipulation *
3 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
4 */
5
6#include <sys/socket.h>
7#include <arpa/inet.h>
8#include <string.h>
9#include <stdlib.h>
10#include <time.h>
11#include <ctype.h>
12#include <netdb.h>
13
14#include <netinet/ether.h>
15#include "static_leases.h"
16
17#include "dhcpd.h"
18#include "files.h"
19#include "options.h"
20#include "common.h"
21
22/*
23 * Domain names may have 254 chars, and string options can be 254
24 * chars long. However, 80 bytes will be enough for most, and won't
25 * hog up memory. If you have a special application, change it
26 */
27#define READ_CONFIG_BUF_SIZE 80
28
29/* on these functions, make sure you datatype matches */
30static int read_ip(const char *line, void *arg)
31{
32 struct in_addr *addr = arg;
33 struct hostent *host;
34 int retval = 1;
35
36 if (!inet_aton(line, addr)) {
37 if ((host = gethostbyname(line)))
38 addr->s_addr = *((unsigned long *) host->h_addr_list[0]);
39 else retval = 0;
40 }
41 return retval;
42}
43
44static int read_mac(const char *line, void *arg)
45{
46 uint8_t *mac_bytes = arg;
47 struct ether_addr *temp_ether_addr;
48 int retval = 1;
49
50 temp_ether_addr = ether_aton(line);
51
52 if(temp_ether_addr == NULL)
53 retval = 0;
54 else
55 memcpy(mac_bytes, temp_ether_addr, 6);
56
57 return retval;
58}
59
60
61static int read_str(const char *line, void *arg)
62{
63 char **dest = arg;
64
65 if (*dest) free(*dest);
66 *dest = strdup(line);
67
68 return 1;
69}
70
71
72static int read_u32(const char *line, void *arg)
73{
74 uint32_t *dest = arg;
75 char *endptr;
76 *dest = strtoul(line, &endptr, 0);
77 return endptr[0] == '\0';
78}
79
80
81static int read_yn(const char *line, void *arg)
82{
83 char *dest = arg;
84 int retval = 1;
85
86 if (!strcasecmp("yes", line))
87 *dest = 1;
88 else if (!strcasecmp("no", line))
89 *dest = 0;
90 else retval = 0;
91
92 return retval;
93}
94
95
96/* read a dhcp option and add it to opt_list */
97static int read_opt(const char *const_line, void *arg)
98{
99 struct option_set **opt_list = arg;
100 char *opt, *val, *endptr;
101 struct dhcp_option *option;
102 int retval = 0, length;
103 char buffer[8];
104 char *line;
105 uint16_t *result_u16 = (uint16_t *) buffer;
106 uint32_t *result_u32 = (uint32_t *) buffer;
107
108 /* Cheat, the only const line we'll actually get is "" */
109 line = (char *) const_line;
110 if (!(opt = strtok(line, " \t="))) return 0;
111
112 for (option = dhcp_options; option->code; option++)
113 if (!strcasecmp(option->name, opt))
114 break;
115
116 if (!option->code) return 0;
117
118 do {
119 if (!(val = strtok(NULL, ", \t"))) break;
120 length = option_lengths[option->flags & TYPE_MASK];
121 retval = 0;
122 opt = buffer; /* new meaning for variable opt */
123 switch (option->flags & TYPE_MASK) {
124 case OPTION_IP:
125 retval = read_ip(val, buffer);
126 break;
127 case OPTION_IP_PAIR:
128 retval = read_ip(val, buffer);
129 if (!(val = strtok(NULL, ", \t/-"))) retval = 0;
130 if (retval) retval = read_ip(val, buffer + 4);
131 break;
132 case OPTION_STRING:
133 length = strlen(val);
134 if (length > 0) {
135 if (length > 254) length = 254;
136 opt = val;
137 retval = 1;
138 }
139 break;
140 case OPTION_BOOLEAN:
141 retval = read_yn(val, buffer);
142 break;
143 case OPTION_U8:
144 buffer[0] = strtoul(val, &endptr, 0);
145 retval = (endptr[0] == '\0');
146 break;
147 case OPTION_U16:
148 *result_u16 = htons(strtoul(val, &endptr, 0));
149 retval = (endptr[0] == '\0');
150 break;
151 case OPTION_S16:
152 *result_u16 = htons(strtol(val, &endptr, 0));
153 retval = (endptr[0] == '\0');
154 break;
155 case OPTION_U32:
156 *result_u32 = htonl(strtoul(val, &endptr, 0));
157 retval = (endptr[0] == '\0');
158 break;
159 case OPTION_S32:
160 *result_u32 = htonl(strtol(val, &endptr, 0));
161 retval = (endptr[0] == '\0');
162 break;
163 default:
164 break;
165 }
166 if (retval)
167 attach_option(opt_list, option, opt, length);
168 } while (retval && option->flags & OPTION_LIST);
169 return retval;
170}
171
172static int read_staticlease(const char *const_line, void *arg)
173{
174
175 char *line;
176 char *mac_string;
177 char *ip_string;
178 uint8_t *mac_bytes;
179 uint32_t *ip;
180
181
182 /* Allocate memory for addresses */
183 mac_bytes = xmalloc(sizeof(unsigned char) * 8);
184 ip = xmalloc(sizeof(uint32_t));
185
186 /* Read mac */
187 line = (char *) const_line;
188 mac_string = strtok(line, " \t");
189 read_mac(mac_string, mac_bytes);
190
191 /* Read ip */
192 ip_string = strtok(NULL, " \t");
193 read_ip(ip_string, ip);
194
195 addStaticLease(arg, mac_bytes, ip);
196
197#ifdef UDHCP_DEBUG
198 printStaticLeases(arg);
199#endif
200
201 return 1;
202
203}
204
205
206static const struct config_keyword keywords[] = {
207 /* keyword handler variable address default */
208 {"start", read_ip, &(server_config.start), "192.168.0.20"},
209 {"end", read_ip, &(server_config.end), "192.168.0.254"},
210 {"interface", read_str, &(server_config.interface), "eth0"},
211 {"option", read_opt, &(server_config.options), ""},
212 {"opt", read_opt, &(server_config.options), ""},
213 {"max_leases", read_u32, &(server_config.max_leases), "254"},
214 {"remaining", read_yn, &(server_config.remaining), "yes"},
215 {"auto_time", read_u32, &(server_config.auto_time), "7200"},
216 {"decline_time",read_u32, &(server_config.decline_time),"3600"},
217 {"conflict_time",read_u32,&(server_config.conflict_time),"3600"},
218 {"offer_time", read_u32, &(server_config.offer_time), "60"},
219 {"min_lease", read_u32, &(server_config.min_lease), "60"},
220 {"lease_file", read_str, &(server_config.lease_file), LEASES_FILE},
221 {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"},
222 {"notify_file", read_str, &(server_config.notify_file), ""},
223 {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"},
224 {"sname", read_str, &(server_config.sname), ""},
225 {"boot_file", read_str, &(server_config.boot_file), ""},
226 {"static_lease",read_staticlease, &(server_config.static_leases), ""},
227 /*ADDME: static lease */
228 {"", NULL, NULL, ""}
229};
230
231
232int read_config(const char *file)
233{
234 FILE *in;
235 char buffer[READ_CONFIG_BUF_SIZE], *token, *line;
236#ifdef UDHCP_DEBUG
237 char orig[READ_CONFIG_BUF_SIZE];
238#endif
239 int i, lm = 0;
240
241 for (i = 0; keywords[i].keyword[0]; i++)
242 if (keywords[i].def[0])
243 keywords[i].handler(keywords[i].def, keywords[i].var);
244
245 if (!(in = fopen(file, "r"))) {
246 LOG(LOG_ERR, "unable to open config file: %s", file);
247 return 0;
248 }
249
250 while (fgets(buffer, READ_CONFIG_BUF_SIZE, in)) {
251 lm++;
252 if (strchr(buffer, '\n')) *(strchr(buffer, '\n')) = '\0';
253#ifdef UDHCP_DEBUG
254 strcpy(orig, buffer);
255#endif
256 if (strchr(buffer, '#')) *(strchr(buffer, '#')) = '\0';
257
258 if (!(token = strtok(buffer, " \t"))) continue;
259 if (!(line = strtok(NULL, ""))) continue;
260
261 /* eat leading whitespace */
262 line = line + strspn(line, " \t=");
263 /* eat trailing whitespace */
264 for (i = strlen(line); i > 0 && isspace(line[i - 1]); i--);
265 line[i] = '\0';
266
267 for (i = 0; keywords[i].keyword[0]; i++)
268 if (!strcasecmp(token, keywords[i].keyword))
269 if (!keywords[i].handler(line, keywords[i].var)) {
270 LOG(LOG_ERR, "Failure parsing line %d of %s", lm, file);
271 DEBUG(LOG_ERR, "unable to parse '%s'", orig);
272 /* reset back to the default value */
273 keywords[i].handler(keywords[i].def, keywords[i].var);
274 }
275 }
276 fclose(in);
277 return 1;
278}
279
280
281void write_leases(void)
282{
283 FILE *fp;
284 unsigned int i;
285 char buf[255];
286 time_t curr = time(0);
287 unsigned long tmp_time;
288
289 if (!(fp = fopen(server_config.lease_file, "w"))) {
290 LOG(LOG_ERR, "Unable to open %s for writing", server_config.lease_file);
291 return;
292 }
293
294 for (i = 0; i < server_config.max_leases; i++) {
295 if (leases[i].yiaddr != 0) {
296
297 /* screw with the time in the struct, for easier writing */
298 tmp_time = leases[i].expires;
299
300 if (server_config.remaining) {
301 if (lease_expired(&(leases[i])))
302 leases[i].expires = 0;
303 else leases[i].expires -= curr;
304 } /* else stick with the time we got */
305 leases[i].expires = htonl(leases[i].expires);
306 fwrite(&leases[i], sizeof(struct dhcpOfferedAddr), 1, fp);
307
308 /* Then restore it when done. */
309 leases[i].expires = tmp_time;
310 }
311 }
312 fclose(fp);
313
314 if (server_config.notify_file) {
315 sprintf(buf, "%s %s", server_config.notify_file, server_config.lease_file);
316 system(buf);
317 }
318}
319
320
321void read_leases(const char *file)
322{
323 FILE *fp;
324 unsigned int i = 0;
325 struct dhcpOfferedAddr lease;
326
327 if (!(fp = fopen(file, "r"))) {
328 LOG(LOG_ERR, "Unable to open %s for reading", file);
329 return;
330 }
331
332 while (i < server_config.max_leases && (fread(&lease, sizeof lease, 1, fp) == 1)) {
333 /* ADDME: is it a static lease */
334 if (lease.yiaddr >= server_config.start && lease.yiaddr <= server_config.end) {
335 lease.expires = ntohl(lease.expires);
336 if (!server_config.remaining) lease.expires -= time(0);
337 if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) {
338 LOG(LOG_WARNING, "Too many leases while loading %s\n", file);
339 break;
340 }
341 i++;
342 }
343 }
344 DEBUG(LOG_INFO, "Read %d leases", i);
345 fclose(fp);
346}
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 @@
1/* files.h */
2#ifndef _FILES_H
3#define _FILES_H
4
5struct config_keyword {
6 const char *keyword;
7 int (* const handler)(const char *line, void *var);
8 void *var;
9 const char *def;
10};
11
12
13int read_config(const char *file);
14void write_leases(void);
15void read_leases(const char *file);
16
17#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 @@
1#include <string.h>
2
3extern int udhcpd_main(int argc, char *argv[]);
4extern int udhcpc_main(int argc, char *argv[]);
5
6int main(int argc, char *argv[])
7{
8 int ret = 0;
9 char *base = strrchr(argv[0], '/');
10
11 if (strstr(base ? (base + 1) : argv[0], "dhcpd"))
12 ret = udhcpd_main(argc, argv);
13 else ret = udhcpc_main(argc, argv);
14
15 return ret;
16}
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 @@
1/*
2 * leases.c -- tools to manage DHCP leases
3 * Russ Dill <Russ.Dill@asu.edu> July 2001
4 */
5
6#include <time.h>
7#include <string.h>
8#include <sys/socket.h>
9#include <netinet/in.h>
10#include <arpa/inet.h>
11
12#include "dhcpd.h"
13#include "files.h"
14#include "options.h"
15#include "leases.h"
16#include "arpping.h"
17#include "common.h"
18
19#include "static_leases.h"
20
21
22uint8_t blank_chaddr[] = {[0 ... 15] = 0};
23
24/* clear every lease out that chaddr OR yiaddr matches and is nonzero */
25void clear_lease(uint8_t *chaddr, uint32_t yiaddr)
26{
27 unsigned int i, j;
28
29 for (j = 0; j < 16 && !chaddr[j]; j++);
30
31 for (i = 0; i < server_config.max_leases; i++)
32 if ((j != 16 && !memcmp(leases[i].chaddr, chaddr, 16)) ||
33 (yiaddr && leases[i].yiaddr == yiaddr)) {
34 memset(&(leases[i]), 0, sizeof(struct dhcpOfferedAddr));
35 }
36}
37
38
39/* add a lease into the table, clearing out any old ones */
40struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease)
41{
42 struct dhcpOfferedAddr *oldest;
43
44 /* clean out any old ones */
45 clear_lease(chaddr, yiaddr);
46
47 oldest = oldest_expired_lease();
48
49 if (oldest) {
50 memcpy(oldest->chaddr, chaddr, 16);
51 oldest->yiaddr = yiaddr;
52 oldest->expires = time(0) + lease;
53 }
54
55 return oldest;
56}
57
58
59/* true if a lease has expired */
60int lease_expired(struct dhcpOfferedAddr *lease)
61{
62 return (lease->expires < (unsigned long) time(0));
63}
64
65
66/* Find the oldest expired lease, NULL if there are no expired leases */
67struct dhcpOfferedAddr *oldest_expired_lease(void)
68{
69 struct dhcpOfferedAddr *oldest = NULL;
70 unsigned long oldest_lease = time(0);
71 unsigned int i;
72
73
74 for (i = 0; i < server_config.max_leases; i++)
75 if (oldest_lease > leases[i].expires) {
76 oldest_lease = leases[i].expires;
77 oldest = &(leases[i]);
78 }
79 return oldest;
80
81}
82
83
84/* Find the first lease that matches chaddr, NULL if no match */
85struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr)
86{
87 unsigned int i;
88
89 for (i = 0; i < server_config.max_leases; i++)
90 if (!memcmp(leases[i].chaddr, chaddr, 16)) return &(leases[i]);
91
92 return NULL;
93}
94
95
96/* Find the first lease that matches yiaddr, NULL is no match */
97struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr)
98{
99 unsigned int i;
100
101 for (i = 0; i < server_config.max_leases; i++)
102 if (leases[i].yiaddr == yiaddr) return &(leases[i]);
103
104 return NULL;
105}
106
107
108/* check is an IP is taken, if it is, add it to the lease table */
109static int check_ip(uint32_t addr)
110{
111 struct in_addr temp;
112
113 if (arpping(addr, server_config.server, server_config.arp, server_config.interface) == 0) {
114 temp.s_addr = addr;
115 LOG(LOG_INFO, "%s belongs to someone, reserving it for %ld seconds",
116 inet_ntoa(temp), server_config.conflict_time);
117 add_lease(blank_chaddr, addr, server_config.conflict_time);
118 return 1;
119 } else return 0;
120}
121
122
123/* find an assignable address, it check_expired is true, we check all the expired leases as well.
124 * Maybe this should try expired leases by age... */
125uint32_t find_address(int check_expired)
126{
127 uint32_t addr, ret;
128 struct dhcpOfferedAddr *lease = NULL;
129
130 addr = ntohl(server_config.start); /* addr is in host order here */
131 for (;addr <= ntohl(server_config.end); addr++) {
132
133 /* ie, 192.168.55.0 */
134 if (!(addr & 0xFF)) continue;
135
136 /* ie, 192.168.55.255 */
137 if ((addr & 0xFF) == 0xFF) continue;
138
139 /* Only do if it isn't an assigned as a static lease */
140 if(!reservedIp(server_config.static_leases, htonl(addr)))
141 {
142
143 /* lease is not taken */
144 ret = htonl(addr);
145 if ((!(lease = find_lease_by_yiaddr(ret)) ||
146
147 /* or it expired and we are checking for expired leases */
148 (check_expired && lease_expired(lease))) &&
149
150 /* and it isn't on the network */
151 !check_ip(ret)) {
152 return ret;
153 break;
154 }
155 }
156 }
157 return 0;
158}
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 @@
1/* leases.h */
2#ifndef _LEASES_H
3#define _LEASES_H
4
5
6struct dhcpOfferedAddr {
7 uint8_t chaddr[16];
8 uint32_t yiaddr; /* network order */
9 uint32_t expires; /* host order */
10};
11
12extern uint8_t blank_chaddr[];
13
14void clear_lease(uint8_t *chaddr, uint32_t yiaddr);
15struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease);
16int lease_expired(struct dhcpOfferedAddr *lease);
17struct dhcpOfferedAddr *oldest_expired_lease(void);
18struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr);
19struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr);
20uint32_t find_address(int check_expired);
21
22
23#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 @@
1/* libbb_udhcp.h - busybox compatability wrapper */
2
3/* bit of a hack, do this no matter what the order of the includes.
4 * (for busybox) */
5
6#ifdef CONFIG_INSTALL_NO_USR
7#undef DEFAULT_SCRIPT
8#define DEFAULT_SCRIPT "/share/udhcpc/default.script"
9#endif
10
11#ifndef _LIBBB_UDHCP_H
12#define _LIBBB_UDHCP_H
13
14#ifdef IN_BUSYBOX
15#include "busybox.h"
16
17#ifdef CONFIG_FEATURE_UDHCP_SYSLOG
18#define UDHCP_SYSLOG
19#endif
20
21#ifdef CONFIG_FEATURE_UDHCP_DEBUG
22#define UDHCP_DEBUG
23#endif
24
25#define COMBINED_BINARY
26#include "version.h"
27
28#define xfopen bb_xfopen
29
30#else /* ! BB_VER */
31
32#include <stdlib.h>
33#include <stdio.h>
34#include <sys/sysinfo.h>
35
36#define TRUE 1
37#define FALSE 0
38
39#define xmalloc malloc
40#define xcalloc calloc
41
42static inline FILE *xfopen(const char *file, const char *mode)
43{
44 FILE *fp;
45 if (!(fp = fopen(file, mode))) {
46 perror("could not open input file");
47 exit(0);
48 }
49 return fp;
50}
51
52#endif /* BB_VER */
53
54#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 @@
1/*
2 * options.c -- DHCP server option packet tools
3 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
4 */
5
6#include <stdlib.h>
7#include <string.h>
8
9#include "dhcpd.h"
10#include "files.h"
11#include "options.h"
12#include "common.h"
13
14
15/* supported options are easily added here */
16struct dhcp_option dhcp_options[] = {
17 /* name[10] flags code */
18 {"subnet", OPTION_IP | OPTION_REQ, 0x01},
19 {"timezone", OPTION_S32, 0x02},
20 {"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03},
21 {"timesvr", OPTION_IP | OPTION_LIST, 0x04},
22 {"namesvr", OPTION_IP | OPTION_LIST, 0x05},
23 {"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06},
24 {"logsvr", OPTION_IP | OPTION_LIST, 0x07},
25 {"cookiesvr", OPTION_IP | OPTION_LIST, 0x08},
26 {"lprsvr", OPTION_IP | OPTION_LIST, 0x09},
27 {"hostname", OPTION_STRING | OPTION_REQ, 0x0c},
28 {"bootsize", OPTION_U16, 0x0d},
29 {"domain", OPTION_STRING | OPTION_REQ, 0x0f},
30 {"swapsvr", OPTION_IP, 0x10},
31 {"rootpath", OPTION_STRING, 0x11},
32 {"ipttl", OPTION_U8, 0x17},
33 {"mtu", OPTION_U16, 0x1a},
34 {"broadcast", OPTION_IP | OPTION_REQ, 0x1c},
35 {"ntpsrv", OPTION_IP | OPTION_LIST, 0x2a},
36 {"wins", OPTION_IP | OPTION_LIST, 0x2c},
37 {"requestip", OPTION_IP, 0x32},
38 {"lease", OPTION_U32, 0x33},
39 {"dhcptype", OPTION_U8, 0x35},
40 {"serverid", OPTION_IP, 0x36},
41 {"message", OPTION_STRING, 0x38},
42 {"tftp", OPTION_STRING, 0x42},
43 {"bootfile", OPTION_STRING, 0x43},
44 {"", 0x00, 0x00}
45};
46
47/* Lengths of the different option types */
48int option_lengths[] = {
49 [OPTION_IP] = 4,
50 [OPTION_IP_PAIR] = 8,
51 [OPTION_BOOLEAN] = 1,
52 [OPTION_STRING] = 1,
53 [OPTION_U8] = 1,
54 [OPTION_U16] = 2,
55 [OPTION_S16] = 2,
56 [OPTION_U32] = 4,
57 [OPTION_S32] = 4
58};
59
60
61/* get an option with bounds checking (warning, not aligned). */
62uint8_t *get_option(struct dhcpMessage *packet, int code)
63{
64 int i, length;
65 uint8_t *optionptr;
66 int over = 0, done = 0, curr = OPTION_FIELD;
67
68 optionptr = packet->options;
69 i = 0;
70 length = 308;
71 while (!done) {
72 if (i >= length) {
73 LOG(LOG_WARNING, "bogus packet, option fields too long.");
74 return NULL;
75 }
76 if (optionptr[i + OPT_CODE] == code) {
77 if (i + 1 + optionptr[i + OPT_LEN] >= length) {
78 LOG(LOG_WARNING, "bogus packet, option fields too long.");
79 return NULL;
80 }
81 return optionptr + i + 2;
82 }
83 switch (optionptr[i + OPT_CODE]) {
84 case DHCP_PADDING:
85 i++;
86 break;
87 case DHCP_OPTION_OVER:
88 if (i + 1 + optionptr[i + OPT_LEN] >= length) {
89 LOG(LOG_WARNING, "bogus packet, option fields too long.");
90 return NULL;
91 }
92 over = optionptr[i + 3];
93 i += optionptr[OPT_LEN] + 2;
94 break;
95 case DHCP_END:
96 if (curr == OPTION_FIELD && over & FILE_FIELD) {
97 optionptr = packet->file;
98 i = 0;
99 length = 128;
100 curr = FILE_FIELD;
101 } else if (curr == FILE_FIELD && over & SNAME_FIELD) {
102 optionptr = packet->sname;
103 i = 0;
104 length = 64;
105 curr = SNAME_FIELD;
106 } else done = 1;
107 break;
108 default:
109 i += optionptr[OPT_LEN + i] + 2;
110 }
111 }
112 return NULL;
113}
114
115
116/* return the position of the 'end' option (no bounds checking) */
117int end_option(uint8_t *optionptr)
118{
119 int i = 0;
120
121 while (optionptr[i] != DHCP_END) {
122 if (optionptr[i] == DHCP_PADDING) i++;
123 else i += optionptr[i + OPT_LEN] + 2;
124 }
125 return i;
126}
127
128
129/* add an option string to the options (an option string contains an option code,
130 * length, then data) */
131int add_option_string(uint8_t *optionptr, uint8_t *string)
132{
133 int end = end_option(optionptr);
134
135 /* end position + string length + option code/length + end option */
136 if (end + string[OPT_LEN] + 2 + 1 >= 308) {
137 LOG(LOG_ERR, "Option 0x%02x did not fit into the packet!", string[OPT_CODE]);
138 return 0;
139 }
140 DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]);
141 memcpy(optionptr + end, string, string[OPT_LEN] + 2);
142 optionptr[end + string[OPT_LEN] + 2] = DHCP_END;
143 return string[OPT_LEN] + 2;
144}
145
146
147/* add a one to four byte option to a packet */
148int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data)
149{
150 char length = 0;
151 int i;
152 uint8_t option[2 + 4];
153 uint8_t *u8;
154 uint16_t *u16;
155 uint32_t *u32;
156 uint32_t aligned;
157 u8 = (uint8_t *) &aligned;
158 u16 = (uint16_t *) &aligned;
159 u32 = &aligned;
160
161 for (i = 0; dhcp_options[i].code; i++)
162 if (dhcp_options[i].code == code) {
163 length = option_lengths[dhcp_options[i].flags & TYPE_MASK];
164 }
165
166 if (!length) {
167 DEBUG(LOG_ERR, "Could not add option 0x%02x", code);
168 return 0;
169 }
170
171 option[OPT_CODE] = code;
172 option[OPT_LEN] = length;
173
174 switch (length) {
175 case 1: *u8 = data; break;
176 case 2: *u16 = data; break;
177 case 4: *u32 = data; break;
178 }
179 memcpy(option + 2, &aligned, length);
180 return add_option_string(optionptr, option);
181}
182
183
184/* find option 'code' in opt_list */
185struct option_set *find_option(struct option_set *opt_list, char code)
186{
187 while (opt_list && opt_list->data[OPT_CODE] < code)
188 opt_list = opt_list->next;
189
190 if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list;
191 else return NULL;
192}
193
194
195/* add an option to the opt_list */
196void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length)
197{
198 struct option_set *existing, *new, **curr;
199
200 /* add it to an existing option */
201 if ((existing = find_option(*opt_list, option->code))) {
202 DEBUG(LOG_INFO, "Attaching option %s to existing member of list", option->name);
203 if (option->flags & OPTION_LIST) {
204 if (existing->data[OPT_LEN] + length <= 255) {
205 existing->data = realloc(existing->data,
206 existing->data[OPT_LEN] + length + 2);
207 memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length);
208 existing->data[OPT_LEN] += length;
209 } /* else, ignore the data, we could put this in a second option in the future */
210 } /* else, ignore the new data */
211 } else {
212 DEBUG(LOG_INFO, "Attaching option %s to list", option->name);
213
214 /* make a new option */
215 new = xmalloc(sizeof(struct option_set));
216 new->data = xmalloc(length + 2);
217 new->data[OPT_CODE] = option->code;
218 new->data[OPT_LEN] = length;
219 memcpy(new->data + 2, buffer, length);
220
221 curr = opt_list;
222 while (*curr && (*curr)->data[OPT_CODE] < option->code)
223 curr = &(*curr)->next;
224
225 new->next = *curr;
226 *curr = new;
227 }
228}
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 @@
1/* options.h */
2#ifndef _OPTIONS_H
3#define _OPTIONS_H
4
5#include "packet.h"
6
7#define TYPE_MASK 0x0F
8
9enum {
10 OPTION_IP=1,
11 OPTION_IP_PAIR,
12 OPTION_STRING,
13 OPTION_BOOLEAN,
14 OPTION_U8,
15 OPTION_U16,
16 OPTION_S16,
17 OPTION_U32,
18 OPTION_S32
19};
20
21#define OPTION_REQ 0x10 /* have the client request this option */
22#define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */
23
24struct dhcp_option {
25 char name[10];
26 char flags;
27 uint8_t code;
28};
29
30extern struct dhcp_option dhcp_options[];
31extern int option_lengths[];
32
33uint8_t *get_option(struct dhcpMessage *packet, int code);
34int end_option(uint8_t *optionptr);
35int add_option_string(uint8_t *optionptr, uint8_t *string);
36int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data);
37struct option_set *find_option(struct option_set *opt_list, char code);
38void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length);
39
40#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 @@
1#include <unistd.h>
2#include <string.h>
3#include <netinet/in.h>
4#include <sys/types.h>
5#include <sys/socket.h>
6#include <features.h>
7#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
8#include <netpacket/packet.h>
9#include <net/ethernet.h>
10#else
11#include <asm/types.h>
12#include <linux/if_packet.h>
13#include <linux/if_ether.h>
14#endif
15#include <errno.h>
16
17#include "packet.h"
18#include "dhcpd.h"
19#include "options.h"
20#include "common.h"
21
22
23void init_header(struct dhcpMessage *packet, char type)
24{
25 memset(packet, 0, sizeof(struct dhcpMessage));
26 switch (type) {
27 case DHCPDISCOVER:
28 case DHCPREQUEST:
29 case DHCPRELEASE:
30 case DHCPINFORM:
31 packet->op = BOOTREQUEST;
32 break;
33 case DHCPOFFER:
34 case DHCPACK:
35 case DHCPNAK:
36 packet->op = BOOTREPLY;
37 }
38 packet->htype = ETH_10MB;
39 packet->hlen = ETH_10MB_LEN;
40 packet->cookie = htonl(DHCP_MAGIC);
41 packet->options[0] = DHCP_END;
42 add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type);
43}
44
45
46/* read a packet from socket fd, return -1 on read error, -2 on packet error */
47int get_packet(struct dhcpMessage *packet, int fd)
48{
49 int bytes;
50 int i;
51 const char broken_vendors[][8] = {
52 "MSFT 98",
53 ""
54 };
55 char unsigned *vendor;
56
57 memset(packet, 0, sizeof(struct dhcpMessage));
58 bytes = read(fd, packet, sizeof(struct dhcpMessage));
59 if (bytes < 0) {
60 DEBUG(LOG_INFO, "couldn't read on listening socket, ignoring");
61 return -1;
62 }
63
64 if (ntohl(packet->cookie) != DHCP_MAGIC) {
65 LOG(LOG_ERR, "received bogus message, ignoring");
66 return -2;
67 }
68 DEBUG(LOG_INFO, "Received a packet");
69
70 if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) {
71 for (i = 0; broken_vendors[i][0]; i++) {
72 if (vendor[OPT_LEN - 2] == (uint8_t) strlen(broken_vendors[i]) &&
73 !strncmp(vendor, broken_vendors[i], vendor[OPT_LEN - 2])) {
74 DEBUG(LOG_INFO, "broken client (%s), forcing broadcast",
75 broken_vendors[i]);
76 packet->flags |= htons(BROADCAST_FLAG);
77 }
78 }
79 }
80
81
82 return bytes;
83}
84
85
86uint16_t checksum(void *addr, int count)
87{
88 /* Compute Internet Checksum for "count" bytes
89 * beginning at location "addr".
90 */
91 register int32_t sum = 0;
92 uint16_t *source = (uint16_t *) addr;
93
94 while (count > 1) {
95 /* This is the inner loop */
96 sum += *source++;
97 count -= 2;
98 }
99
100 /* Add left-over byte, if any */
101 if (count > 0) {
102 /* Make sure that the left-over byte is added correctly both
103 * with little and big endian hosts */
104 uint16_t tmp = 0;
105 *(uint8_t *) (&tmp) = * (uint8_t *) source;
106 sum += tmp;
107 }
108 /* Fold 32-bit sum to 16 bits */
109 while (sum >> 16)
110 sum = (sum & 0xffff) + (sum >> 16);
111
112 return ~sum;
113}
114
115
116/* Construct a ip/udp header for a packet, and specify the source and dest hardware address */
117int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
118 uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex)
119{
120 int fd;
121 int result;
122 struct sockaddr_ll dest;
123 struct udp_dhcp_packet packet;
124
125 if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
126 DEBUG(LOG_ERR, "socket call failed: %m");
127 return -1;
128 }
129
130 memset(&dest, 0, sizeof(dest));
131 memset(&packet, 0, sizeof(packet));
132
133 dest.sll_family = AF_PACKET;
134 dest.sll_protocol = htons(ETH_P_IP);
135 dest.sll_ifindex = ifindex;
136 dest.sll_halen = 6;
137 memcpy(dest.sll_addr, dest_arp, 6);
138 if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) {
139 DEBUG(LOG_ERR, "bind call failed: %m");
140 close(fd);
141 return -1;
142 }
143
144 packet.ip.protocol = IPPROTO_UDP;
145 packet.ip.saddr = source_ip;
146 packet.ip.daddr = dest_ip;
147 packet.udp.source = htons(source_port);
148 packet.udp.dest = htons(dest_port);
149 packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */
150 packet.ip.tot_len = packet.udp.len;
151 memcpy(&(packet.data), payload, sizeof(struct dhcpMessage));
152 packet.udp.check = checksum(&packet, sizeof(struct udp_dhcp_packet));
153
154 packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet));
155 packet.ip.ihl = sizeof(packet.ip) >> 2;
156 packet.ip.version = IPVERSION;
157 packet.ip.ttl = IPDEFTTL;
158 packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip));
159
160 result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0, (struct sockaddr *) &dest, sizeof(dest));
161 if (result <= 0) {
162 DEBUG(LOG_ERR, "write on socket failed: %m");
163 }
164 close(fd);
165 return result;
166}
167
168
169/* Let the kernel do all the work for packet generation */
170int kernel_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
171 uint32_t dest_ip, int dest_port)
172{
173 int n = 1;
174 int fd, result;
175 struct sockaddr_in client;
176
177 if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
178 return -1;
179
180 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1)
181 return -1;
182
183 memset(&client, 0, sizeof(client));
184 client.sin_family = AF_INET;
185 client.sin_port = htons(source_port);
186 client.sin_addr.s_addr = source_ip;
187
188 if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1)
189 return -1;
190
191 memset(&client, 0, sizeof(client));
192 client.sin_family = AF_INET;
193 client.sin_port = htons(dest_port);
194 client.sin_addr.s_addr = dest_ip;
195
196 if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1)
197 return -1;
198
199 result = write(fd, payload, sizeof(struct dhcpMessage));
200 close(fd);
201 return result;
202}
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 @@
1#ifndef _PACKET_H
2#define _PACKET_H
3
4#include <netinet/udp.h>
5#include <netinet/ip.h>
6
7struct dhcpMessage {
8 uint8_t op;
9 uint8_t htype;
10 uint8_t hlen;
11 uint8_t hops;
12 uint32_t xid;
13 uint16_t secs;
14 uint16_t flags;
15 uint32_t ciaddr;
16 uint32_t yiaddr;
17 uint32_t siaddr;
18 uint32_t giaddr;
19 uint8_t chaddr[16];
20 uint8_t sname[64];
21 uint8_t file[128];
22 uint32_t cookie;
23 uint8_t options[308]; /* 312 - cookie */
24};
25
26struct udp_dhcp_packet {
27 struct iphdr ip;
28 struct udphdr udp;
29 struct dhcpMessage data;
30};
31
32void init_header(struct dhcpMessage *packet, char type);
33int get_packet(struct dhcpMessage *packet, int fd);
34uint16_t checksum(void *addr, int count);
35int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
36 uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex);
37int kernel_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
38 uint32_t dest_ip, int dest_port);
39
40
41#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 @@
1/* pidfile.c
2 *
3 * Functions to assist in the writing and removing of pidfiles.
4 *
5 * Russ Dill <Russ.Dill@asu.edu> September 2001
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <fcntl.h>
25#include <unistd.h>
26#include <stdio.h>
27#include <stdlib.h>
28
29#include "pidfile.h"
30#include "common.h"
31
32static char *saved_pidfile;
33
34static void pidfile_delete(void)
35{
36 if (saved_pidfile) unlink(saved_pidfile);
37}
38
39
40int pidfile_acquire(const char *pidfile)
41{
42 int pid_fd;
43 if (!pidfile) return -1;
44
45 pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644);
46 if (pid_fd < 0) {
47 LOG(LOG_ERR, "Unable to open pidfile %s: %m\n", pidfile);
48 } else {
49 lockf(pid_fd, F_LOCK, 0);
50 if (!saved_pidfile)
51 atexit(pidfile_delete);
52 saved_pidfile = (char *) pidfile;
53 }
54
55 return pid_fd;
56}
57
58
59void pidfile_write_release(int pid_fd)
60{
61 FILE *out;
62
63 if (pid_fd < 0) return;
64
65 if ((out = fdopen(pid_fd, "w")) != NULL) {
66 fprintf(out, "%d\n", getpid());
67 fclose(out);
68 }
69 lockf(pid_fd, F_UNLCK, 0);
70 close(pid_fd);
71}
72
73
74
75
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 @@
1/* pidfile.h
2 *
3 * Functions to assist in the writing and removing of pidfiles.
4 *
5 * Russ Dill <Russ.Dill@asu.edu> September 2001
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22
23int pidfile_acquire(const char *pidfile);
24void pidfile_write_release(int pid_fd);
25
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 @@
1/* script.c
2 *
3 * Functions to call the DHCP client notification scripts
4 *
5 * Russ Dill <Russ.Dill@asu.edu> July 2001
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include <string.h>
23#include <unistd.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <sys/socket.h>
27#include <netinet/in.h>
28#include <arpa/inet.h>
29#include <sys/types.h>
30#include <sys/wait.h>
31
32#include "options.h"
33#include "dhcpd.h"
34#include "dhcpc.h"
35#include "common.h"
36
37/* get a rough idea of how long an option will be (rounding up...) */
38static const int max_option_length[] = {
39 [OPTION_IP] = sizeof("255.255.255.255 "),
40 [OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2,
41 [OPTION_STRING] = 1,
42 [OPTION_BOOLEAN] = sizeof("yes "),
43 [OPTION_U8] = sizeof("255 "),
44 [OPTION_U16] = sizeof("65535 "),
45 [OPTION_S16] = sizeof("-32768 "),
46 [OPTION_U32] = sizeof("4294967295 "),
47 [OPTION_S32] = sizeof("-2147483684 "),
48};
49
50
51static inline int upper_length(int length, int opt_index)
52{
53 return max_option_length[opt_index] *
54 (length / option_lengths[opt_index]);
55}
56
57
58static int sprintip(char *dest, char *pre, uint8_t *ip)
59{
60 return sprintf(dest, "%s%d.%d.%d.%d", pre, ip[0], ip[1], ip[2], ip[3]);
61}
62
63
64/* really simple implementation, just count the bits */
65static int mton(struct in_addr *mask)
66{
67 int i;
68 unsigned long bits = ntohl(mask->s_addr);
69 /* too bad one can't check the carry bit, etc in c bit
70 * shifting */
71 for (i = 0; i < 32 && !((bits >> i) & 1); i++);
72 return 32 - i;
73}
74
75
76/* Fill dest with the text of option 'option'. */
77static void fill_options(char *dest, uint8_t *option, struct dhcp_option *type_p)
78{
79 int type, optlen;
80 uint16_t val_u16;
81 int16_t val_s16;
82 uint32_t val_u32;
83 int32_t val_s32;
84 int len = option[OPT_LEN - 2];
85
86 dest += sprintf(dest, "%s=", type_p->name);
87
88 type = type_p->flags & TYPE_MASK;
89 optlen = option_lengths[type];
90 for(;;) {
91 switch (type) {
92 case OPTION_IP_PAIR:
93 dest += sprintip(dest, "", option);
94 *(dest++) = '/';
95 option += 4;
96 optlen = 4;
97 case OPTION_IP: /* Works regardless of host byte order. */
98 dest += sprintip(dest, "", option);
99 break;
100 case OPTION_BOOLEAN:
101 dest += sprintf(dest, *option ? "yes" : "no");
102 break;
103 case OPTION_U8:
104 dest += sprintf(dest, "%u", *option);
105 break;
106 case OPTION_U16:
107 memcpy(&val_u16, option, 2);
108 dest += sprintf(dest, "%u", ntohs(val_u16));
109 break;
110 case OPTION_S16:
111 memcpy(&val_s16, option, 2);
112 dest += sprintf(dest, "%d", ntohs(val_s16));
113 break;
114 case OPTION_U32:
115 memcpy(&val_u32, option, 4);
116 dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32));
117 break;
118 case OPTION_S32:
119 memcpy(&val_s32, option, 4);
120 dest += sprintf(dest, "%ld", (long) ntohl(val_s32));
121 break;
122 case OPTION_STRING:
123 memcpy(dest, option, len);
124 dest[len] = '\0';
125 return; /* Short circuit this case */
126 }
127 option += optlen;
128 len -= optlen;
129 if (len <= 0) break;
130 dest += sprintf(dest, " ");
131 }
132}
133
134
135/* put all the parameters into an environment */
136static char **fill_envp(struct dhcpMessage *packet)
137{
138 int num_options = 0;
139 int i, j;
140 char **envp;
141 uint8_t *temp;
142 struct in_addr subnet;
143 char over = 0;
144
145 if (packet == NULL)
146 num_options = 0;
147 else {
148 for (i = 0; dhcp_options[i].code; i++)
149 if (get_option(packet, dhcp_options[i].code)) {
150 num_options++;
151 if (dhcp_options[i].code == DHCP_SUBNET)
152 num_options++; /* for mton */
153 }
154 if (packet->siaddr) num_options++;
155 if ((temp = get_option(packet, DHCP_OPTION_OVER)))
156 over = *temp;
157 if (!(over & FILE_FIELD) && packet->file[0]) num_options++;
158 if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++;
159 }
160
161 envp = xcalloc(sizeof(char *), num_options + 5);
162 j = 0;
163 asprintf(&envp[j++], "interface=%s", client_config.interface);
164 asprintf(&envp[j++], "%s=%s", "PATH",
165 getenv("PATH") ? : "/bin:/usr/bin:/sbin:/usr/sbin");
166 asprintf(&envp[j++], "%s=%s", "HOME", getenv("HOME") ? : "/");
167
168 if (packet == NULL) return envp;
169
170 envp[j] = xmalloc(sizeof("ip=255.255.255.255"));
171 sprintip(envp[j++], "ip=", (uint8_t *) &packet->yiaddr);
172
173
174 for (i = 0; dhcp_options[i].code; i++) {
175 if (!(temp = get_option(packet, dhcp_options[i].code)))
176 continue;
177 envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2],
178 dhcp_options[i].flags & TYPE_MASK) + strlen(dhcp_options[i].name) + 2);
179 fill_options(envp[j++], temp, &dhcp_options[i]);
180
181 /* Fill in a subnet bits option for things like /24 */
182 if (dhcp_options[i].code == DHCP_SUBNET) {
183 memcpy(&subnet, temp, 4);
184 asprintf(&envp[j++], "mask=%d", mton(&subnet));
185 }
186 }
187 if (packet->siaddr) {
188 envp[j] = xmalloc(sizeof("siaddr=255.255.255.255"));
189 sprintip(envp[j++], "siaddr=", (uint8_t *) &packet->siaddr);
190 }
191 if (!(over & FILE_FIELD) && packet->file[0]) {
192 /* watch out for invalid packets */
193 packet->file[sizeof(packet->file) - 1] = '\0';
194 asprintf(&envp[j++], "boot_file=%s", packet->file);
195 }
196 if (!(over & SNAME_FIELD) && packet->sname[0]) {
197 /* watch out for invalid packets */
198 packet->sname[sizeof(packet->sname) - 1] = '\0';
199 asprintf(&envp[j++], "sname=%s", packet->sname);
200 }
201 return envp;
202}
203
204
205/* Call a script with a par file and env vars */
206void run_script(struct dhcpMessage *packet, const char *name)
207{
208 int pid;
209 char **envp, **curr;
210
211 if (client_config.script == NULL)
212 return;
213
214 DEBUG(LOG_INFO, "vforking and execle'ing %s", client_config.script);
215
216 envp = fill_envp(packet);
217 /* call script */
218 pid = vfork();
219 if (pid) {
220 waitpid(pid, NULL, 0);
221 for (curr = envp; *curr; curr++) free(*curr);
222 free(envp);
223 return;
224 } else if (pid == 0) {
225 /* close fd's? */
226
227 /* exec script */
228 execle(client_config.script, client_config.script,
229 name, NULL, envp);
230 LOG(LOG_ERR, "script %s failed: %m", client_config.script);
231 exit(1);
232 }
233}
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 @@
1#ifndef _SCRIPT_H
2#define _SCRIPT_H
3
4void run_script(struct dhcpMessage *packet, const char *name);
5
6#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 @@
1/* serverpacket.c
2 *
3 * Construct and send DHCP server packets
4 *
5 * Russ Dill <Russ.Dill@asu.edu> July 2001
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include <sys/socket.h>
23#include <netinet/in.h>
24#include <arpa/inet.h>
25#include <string.h>
26#include <time.h>
27
28#include "serverpacket.h"
29#include "dhcpd.h"
30#include "options.h"
31#include "common.h"
32#include "static_leases.h"
33
34/* send a packet to giaddr using the kernel ip stack */
35static int send_packet_to_relay(struct dhcpMessage *payload)
36{
37 DEBUG(LOG_INFO, "Forwarding packet to relay");
38
39 return kernel_packet(payload, server_config.server, SERVER_PORT,
40 payload->giaddr, SERVER_PORT);
41}
42
43
44/* send a packet to a specific arp address and ip address by creating our own ip packet */
45static int send_packet_to_client(struct dhcpMessage *payload, int force_broadcast)
46{
47 uint8_t *chaddr;
48 uint32_t ciaddr;
49
50 if (force_broadcast) {
51 DEBUG(LOG_INFO, "broadcasting packet to client (NAK)");
52 ciaddr = INADDR_BROADCAST;
53 chaddr = MAC_BCAST_ADDR;
54 } else if (payload->ciaddr) {
55 DEBUG(LOG_INFO, "unicasting packet to client ciaddr");
56 ciaddr = payload->ciaddr;
57 chaddr = payload->chaddr;
58 } else if (ntohs(payload->flags) & BROADCAST_FLAG) {
59 DEBUG(LOG_INFO, "broadcasting packet to client (requested)");
60 ciaddr = INADDR_BROADCAST;
61 chaddr = MAC_BCAST_ADDR;
62 } else {
63 DEBUG(LOG_INFO, "unicasting packet to client yiaddr");
64 ciaddr = payload->yiaddr;
65 chaddr = payload->chaddr;
66 }
67 return raw_packet(payload, server_config.server, SERVER_PORT,
68 ciaddr, CLIENT_PORT, chaddr, server_config.ifindex);
69}
70
71
72/* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */
73static int send_packet(struct dhcpMessage *payload, int force_broadcast)
74{
75 int ret;
76
77 if (payload->giaddr)
78 ret = send_packet_to_relay(payload);
79 else ret = send_packet_to_client(payload, force_broadcast);
80 return ret;
81}
82
83
84static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type)
85{
86 init_header(packet, type);
87 packet->xid = oldpacket->xid;
88 memcpy(packet->chaddr, oldpacket->chaddr, 16);
89 packet->flags = oldpacket->flags;
90 packet->giaddr = oldpacket->giaddr;
91 packet->ciaddr = oldpacket->ciaddr;
92 add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server);
93}
94
95
96/* add in the bootp options */
97static void add_bootp_options(struct dhcpMessage *packet)
98{
99 packet->siaddr = server_config.siaddr;
100 if (server_config.sname)
101 strncpy(packet->sname, server_config.sname, sizeof(packet->sname) - 1);
102 if (server_config.boot_file)
103 strncpy(packet->file, server_config.boot_file, sizeof(packet->file) - 1);
104}
105
106
107/* send a DHCP OFFER to a DHCP DISCOVER */
108int sendOffer(struct dhcpMessage *oldpacket)
109{
110 struct dhcpMessage packet;
111 struct dhcpOfferedAddr *lease = NULL;
112 uint32_t req_align, lease_time_align = server_config.lease;
113 uint8_t *req, *lease_time;
114 struct option_set *curr;
115 struct in_addr addr;
116
117 uint32_t static_lease_ip;
118
119 init_packet(&packet, oldpacket, DHCPOFFER);
120
121 static_lease_ip = getIpByMac(server_config.static_leases, oldpacket->chaddr);
122
123 /* ADDME: if static, short circuit */
124 if(!static_lease_ip)
125 {
126 /* the client is in our lease/offered table */
127 if ((lease = find_lease_by_chaddr(oldpacket->chaddr))) {
128 if (!lease_expired(lease))
129 lease_time_align = lease->expires - time(0);
130 packet.yiaddr = lease->yiaddr;
131
132 /* Or the client has a requested ip */
133 } else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP)) &&
134
135 /* Don't look here (ugly hackish thing to do) */
136 memcpy(&req_align, req, 4) &&
137
138 /* and the ip is in the lease range */
139 ntohl(req_align) >= ntohl(server_config.start) &&
140 ntohl(req_align) <= ntohl(server_config.end) &&
141
142 !static_lease_ip && /* Check that its not a static lease */
143 /* and is not already taken/offered */
144 ((!(lease = find_lease_by_yiaddr(req_align)) ||
145
146 /* or its taken, but expired */ /* ADDME: or maybe in here */
147 lease_expired(lease)))) {
148 packet.yiaddr = req_align; /* FIXME: oh my, is there a host using this IP? */
149
150 /* otherwise, find a free IP */
151 } else {
152 /* Is it a static lease? (No, because find_address skips static lease) */
153 packet.yiaddr = find_address(0);
154
155 /* try for an expired lease */
156 if (!packet.yiaddr) packet.yiaddr = find_address(1);
157 }
158
159 if(!packet.yiaddr) {
160 LOG(LOG_WARNING, "no IP addresses to give -- OFFER abandoned");
161 return -1;
162 }
163
164 if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) {
165 LOG(LOG_WARNING, "lease pool is full -- OFFER abandoned");
166 return -1;
167 }
168
169 if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) {
170 memcpy(&lease_time_align, lease_time, 4);
171 lease_time_align = ntohl(lease_time_align);
172 if (lease_time_align > server_config.lease)
173 lease_time_align = server_config.lease;
174 }
175
176 /* Make sure we aren't just using the lease time from the previous offer */
177 if (lease_time_align < server_config.min_lease)
178 lease_time_align = server_config.lease;
179 }
180 /* ADDME: end of short circuit */
181 else
182 {
183 /* It is a static lease... use it */
184 packet.yiaddr = static_lease_ip;
185 }
186
187 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
188
189 curr = server_config.options;
190 while (curr) {
191 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
192 add_option_string(packet.options, curr->data);
193 curr = curr->next;
194 }
195
196 add_bootp_options(&packet);
197
198 addr.s_addr = packet.yiaddr;
199 LOG(LOG_INFO, "sending OFFER of %s", inet_ntoa(addr));
200 return send_packet(&packet, 0);
201}
202
203
204int sendNAK(struct dhcpMessage *oldpacket)
205{
206 struct dhcpMessage packet;
207
208 init_packet(&packet, oldpacket, DHCPNAK);
209
210 DEBUG(LOG_INFO, "sending NAK");
211 return send_packet(&packet, 1);
212}
213
214
215int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr)
216{
217 struct dhcpMessage packet;
218 struct option_set *curr;
219 uint8_t *lease_time;
220 uint32_t lease_time_align = server_config.lease;
221 struct in_addr addr;
222
223 init_packet(&packet, oldpacket, DHCPACK);
224 packet.yiaddr = yiaddr;
225
226 if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) {
227 memcpy(&lease_time_align, lease_time, 4);
228 lease_time_align = ntohl(lease_time_align);
229 if (lease_time_align > server_config.lease)
230 lease_time_align = server_config.lease;
231 else if (lease_time_align < server_config.min_lease)
232 lease_time_align = server_config.lease;
233 }
234
235 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align));
236
237 curr = server_config.options;
238 while (curr) {
239 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
240 add_option_string(packet.options, curr->data);
241 curr = curr->next;
242 }
243
244 add_bootp_options(&packet);
245
246 addr.s_addr = packet.yiaddr;
247 LOG(LOG_INFO, "sending ACK to %s", inet_ntoa(addr));
248
249 if (send_packet(&packet, 0) < 0)
250 return -1;
251
252 add_lease(packet.chaddr, packet.yiaddr, lease_time_align);
253
254 return 0;
255}
256
257
258int send_inform(struct dhcpMessage *oldpacket)
259{
260 struct dhcpMessage packet;
261 struct option_set *curr;
262
263 init_packet(&packet, oldpacket, DHCPACK);
264
265 curr = server_config.options;
266 while (curr) {
267 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
268 add_option_string(packet.options, curr->data);
269 curr = curr->next;
270 }
271
272 add_bootp_options(&packet);
273
274 return send_packet(&packet, 0);
275}
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 @@
1#ifndef _SERVERPACKET_H
2#define _SERVERPACKET_H
3
4#include "packet.h"
5
6int sendOffer(struct dhcpMessage *oldpacket);
7int sendNAK(struct dhcpMessage *oldpacket);
8int sendACK(struct dhcpMessage *oldpacket, uint32_t yiaddr);
9int send_inform(struct dhcpMessage *oldpacket);
10
11
12#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 @@
1/* signalpipe.c
2 *
3 * Signal pipe infrastructure. A reliable way of delivering signals.
4 *
5 * Russ Dill <Russ.Dill@asu.edu> December 2003
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include <unistd.h>
23#include <signal.h>
24#include <sys/types.h>
25#include <sys/socket.h>
26#include <sys/select.h>
27
28
29#include "signalpipe.h"
30#include "common.h"
31
32static int signal_pipe[2];
33
34static void signal_handler(int sig)
35{
36 if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0)
37 DEBUG(LOG_ERR, "Could not send signal: %m");
38}
39
40
41/* Call this before doing anything else. Sets up the socket pair
42 * and installs the signal handler */
43void udhcp_sp_setup(void)
44{
45 socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe);
46 signal(SIGUSR1, signal_handler);
47 signal(SIGUSR2, signal_handler);
48 signal(SIGTERM, signal_handler);
49}
50
51
52/* Quick little function to setup the rfds. Will return the
53 * max_fd for use with select. Limited in that you can only pass
54 * one extra fd */
55int udhcp_sp_fd_set(fd_set *rfds, int extra_fd)
56{
57 FD_ZERO(rfds);
58 FD_SET(signal_pipe[0], rfds);
59 if (extra_fd >= 0) FD_SET(extra_fd, rfds);
60 return signal_pipe[0] > extra_fd ? signal_pipe[0] : extra_fd;
61}
62
63
64/* Read a signal from the signal pipe. Returns 0 if there is
65 * no signal, -1 on error (and sets errno appropriately), and
66 * your signal on success */
67int udhcp_sp_read(fd_set *rfds)
68{
69 int sig;
70
71 if (!FD_ISSET(signal_pipe[0], rfds))
72 return 0;
73
74 if (read(signal_pipe[0], &sig, sizeof(sig)) < 0)
75 return -1;
76
77 return sig;
78}
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 @@
1/* signalpipe.h
2 *
3 * Russ Dill <Russ.Dill@asu.edu> December 2003
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20void udhcp_sp_setup(void);
21int udhcp_sp_fd_set(fd_set *rfds, int extra_fd);
22int 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 @@
1/*
2 * socket.c -- DHCP server client/server socket creation
3 *
4 * udhcp client/server
5 * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
6 * Chris Trew <ctrew@moreton.com.au>
7 *
8 * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <sys/ioctl.h>
28#include <netinet/in.h>
29#include <unistd.h>
30#include <string.h>
31#include <arpa/inet.h>
32#include <net/if.h>
33#include <errno.h>
34#include <features.h>
35#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
36#include <netpacket/packet.h>
37#include <net/ethernet.h>
38#else
39#include <asm/types.h>
40#include <linux/if_packet.h>
41#include <linux/if_ether.h>
42#endif
43
44#include "socket.h"
45#include "common.h"
46
47int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp)
48{
49 int fd;
50 struct ifreq ifr;
51 struct sockaddr_in *our_ip;
52
53 memset(&ifr, 0, sizeof(struct ifreq));
54 if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) {
55 ifr.ifr_addr.sa_family = AF_INET;
56 strcpy(ifr.ifr_name, interface);
57
58 if (addr) {
59 if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
60 our_ip = (struct sockaddr_in *) &ifr.ifr_addr;
61 *addr = our_ip->sin_addr.s_addr;
62 DEBUG(LOG_INFO, "%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr));
63 } else {
64 LOG(LOG_ERR, "SIOCGIFADDR failed, is the interface up and configured?: %m");
65 return -1;
66 }
67 }
68
69 if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) {
70 DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex);
71 *ifindex = ifr.ifr_ifindex;
72 } else {
73 LOG(LOG_ERR, "SIOCGIFINDEX failed!: %m");
74 return -1;
75 }
76 if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) {
77 memcpy(arp, ifr.ifr_hwaddr.sa_data, 6);
78 DEBUG(LOG_INFO, "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x",
79 arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]);
80 } else {
81 LOG(LOG_ERR, "SIOCGIFHWADDR failed!: %m");
82 return -1;
83 }
84 } else {
85 LOG(LOG_ERR, "socket failed!: %m");
86 return -1;
87 }
88 close(fd);
89 return 0;
90}
91
92
93int listen_socket(uint32_t ip, int port, char *inf)
94{
95 struct ifreq interface;
96 int fd;
97 struct sockaddr_in addr;
98 int n = 1;
99
100 DEBUG(LOG_INFO, "Opening listen socket on 0x%08x:%d %s", ip, port, inf);
101 if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
102 DEBUG(LOG_ERR, "socket call failed: %m");
103 return -1;
104 }
105
106 memset(&addr, 0, sizeof(addr));
107 addr.sin_family = AF_INET;
108 addr.sin_port = htons(port);
109 addr.sin_addr.s_addr = ip;
110
111 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) {
112 close(fd);
113 return -1;
114 }
115 if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *) &n, sizeof(n)) == -1) {
116 close(fd);
117 return -1;
118 }
119
120 strncpy(interface.ifr_ifrn.ifrn_name, inf, IFNAMSIZ);
121 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,(char *)&interface, sizeof(interface)) < 0) {
122 close(fd);
123 return -1;
124 }
125
126 if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
127 close(fd);
128 return -1;
129 }
130
131 return fd;
132}
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 @@
1/* socket.h */
2#ifndef _SOCKET_H
3#define _SOCKET_H
4
5int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp);
6int listen_socket(uint32_t ip, int port, char *inf);
7
8#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 @@
1/*
2 * static_leases.c -- Couple of functions to assist with storing and
3 * retrieving data for static leases
4 *
5 * Wade Berrier <wberrier@myrealbox.com> September 2004
6 *
7 */
8
9
10#include <stdlib.h>
11#include <stdio.h>
12#include <string.h>
13
14#include "static_leases.h"
15#include "dhcpd.h"
16
17/* Takes the address of the pointer to the static_leases linked list,
18 * Address to a 6 byte mac address
19 * Address to a 4 byte ip address */
20int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip)
21{
22
23 struct static_lease *cur;
24 struct static_lease *new_static_lease;
25
26 /* Build new node */
27 new_static_lease = xmalloc(sizeof(struct static_lease));
28 new_static_lease->mac = mac;
29 new_static_lease->ip = ip;
30 new_static_lease->next = NULL;
31
32 /* If it's the first node to be added... */
33 if(*lease_struct == NULL)
34 {
35 *lease_struct = new_static_lease;
36 }
37 else
38 {
39 cur = *lease_struct;
40 while(cur->next != NULL)
41 {
42 cur = cur->next;
43 }
44
45 cur->next = new_static_lease;
46 }
47
48 return 1;
49
50}
51
52/* Check to see if a mac has an associated static lease */
53uint32_t getIpByMac(struct static_lease *lease_struct, void *arg)
54{
55 uint32_t return_ip;
56 struct static_lease *cur = lease_struct;
57 uint8_t *mac = arg;
58
59 return_ip = 0;
60
61 while(cur != NULL)
62 {
63 /* If the client has the correct mac */
64 if(memcmp(cur->mac, mac, 6) == 0)
65 {
66 return_ip = *(cur->ip);
67 }
68
69 cur = cur->next;
70 }
71
72 return return_ip;
73
74}
75
76/* Check to see if an ip is reserved as a static ip */
77uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip)
78{
79 struct static_lease *cur = lease_struct;
80
81 uint32_t return_val = 0;
82
83 while(cur != NULL)
84 {
85 /* If the client has the correct ip */
86 if(*cur->ip == ip)
87 return_val = 1;
88
89 cur = cur->next;
90 }
91
92 return return_val;
93
94}
95
96#ifdef UDHCP_DEBUG
97/* Print out static leases just to check what's going on */
98/* Takes the address of the pointer to the static_leases linked list */
99void printStaticLeases(struct static_lease **arg)
100{
101 /* Get a pointer to the linked list */
102 struct static_lease *cur = *arg;
103
104 while(cur != NULL)
105 {
106 /* printf("PrintStaticLeases: Lease mac Address: %x\n", cur->mac); */
107 printf("PrintStaticLeases: Lease mac Value: %x\n", *(cur->mac));
108 /* printf("PrintStaticLeases: Lease ip Address: %x\n", cur->ip); */
109 printf("PrintStaticLeases: Lease ip Value: %x\n", *(cur->ip));
110
111 cur = cur->next;
112 }
113
114
115}
116#endif
117
118
119
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 @@
1/* static_leases.h */
2#ifndef _STATIC_LEASES_H
3#define _STATIC_LEASES_H
4
5#include "dhcpd.h"
6
7/* Config file will pass static lease info to this function which will add it
8 * to a data structure that can be searched later */
9int addStaticLease(struct static_lease **lease_struct, uint8_t *mac, uint32_t *ip);
10
11/* Check to see if a mac has an associated static lease */
12uint32_t getIpByMac(struct static_lease *lease_struct, void *arg);
13
14/* Check to see if an ip is reserved as a static ip */
15uint32_t reservedIp(struct static_lease *lease_struct, uint32_t ip);
16
17#ifdef UDHCP_DEBUG
18/* Print out static leases just to check what's going on */
19void printStaticLeases(struct static_lease **lease_struct);
20#endif
21
22#endif
23
24
25
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 @@
1#ifndef _UDHCP_VERSION_H
2#define _UDHCP_VERSION_H
3
4#define VERSION "0.9.9-pre"
5
6#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * vconfig implementation for busybox
4 *
5 * Copyright (C) 2001 Manuel Novoa III <mjn3@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* BB_AUDIT SUSv3 N/A */
24
25#include <stdlib.h>
26#include <unistd.h>
27#include <fcntl.h>
28#include <sys/ioctl.h>
29#include <net/if.h>
30#include <string.h>
31#include <limits.h>
32#include "busybox.h"
33
34/* Stuff from linux/if_vlan.h, kernel version 2.4.23 */
35enum vlan_ioctl_cmds {
36 ADD_VLAN_CMD,
37 DEL_VLAN_CMD,
38 SET_VLAN_INGRESS_PRIORITY_CMD,
39 SET_VLAN_EGRESS_PRIORITY_CMD,
40 GET_VLAN_INGRESS_PRIORITY_CMD,
41 GET_VLAN_EGRESS_PRIORITY_CMD,
42 SET_VLAN_NAME_TYPE_CMD,
43 SET_VLAN_FLAG_CMD
44};
45enum vlan_name_types {
46 VLAN_NAME_TYPE_PLUS_VID, /* Name will look like: vlan0005 */
47 VLAN_NAME_TYPE_RAW_PLUS_VID, /* name will look like: eth1.0005 */
48 VLAN_NAME_TYPE_PLUS_VID_NO_PAD, /* Name will look like: vlan5 */
49 VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, /* Name will look like: eth0.5 */
50 VLAN_NAME_TYPE_HIGHEST
51};
52
53struct vlan_ioctl_args {
54 int cmd; /* Should be one of the vlan_ioctl_cmds enum above. */
55 char device1[24];
56
57 union {
58 char device2[24];
59 int VID;
60 unsigned int skb_priority;
61 unsigned int name_type;
62 unsigned int bind_type;
63 unsigned int flag; /* Matches vlan_dev_info flags */
64 } u;
65
66 short vlan_qos;
67};
68
69#define VLAN_GROUP_ARRAY_LEN 4096
70#define SIOCSIFVLAN 0x8983 /* Set 802.1Q VLAN options */
71
72/* On entry, table points to the length of the current string plus
73 * nul terminator plus data length for the subsequent entry. The
74 * return value is the last data entry for the matching string. */
75static const char *xfind_str(const char *table, const char *str)
76{
77 while (strcasecmp(str, table+1) != 0) {
78 if (!*(table += table[0])) {
79 bb_show_usage();
80 }
81 }
82 return table - 1;
83}
84
85static const char cmds[] = {
86 4, ADD_VLAN_CMD, 7,
87 'a', 'd', 'd', 0,
88 3, DEL_VLAN_CMD, 7,
89 'r', 'e', 'm', 0,
90 3, SET_VLAN_NAME_TYPE_CMD, 17,
91 's', 'e', 't', '_',
92 'n', 'a', 'm', 'e', '_',
93 't', 'y', 'p', 'e', 0,
94 4, SET_VLAN_FLAG_CMD, 12,
95 's', 'e', 't', '_',
96 'f', 'l', 'a', 'g', 0,
97 5, SET_VLAN_EGRESS_PRIORITY_CMD, 18,
98 's', 'e', 't', '_',
99 'e', 'g', 'r', 'e', 's', 's', '_',
100 'm', 'a', 'p', 0,
101 5, SET_VLAN_INGRESS_PRIORITY_CMD, 16,
102 's', 'e', 't', '_',
103 'i', 'n', 'g', 'r', 'e', 's', 's', '_',
104 'm', 'a', 'p', 0,
105};
106
107static const char name_types[] = {
108 VLAN_NAME_TYPE_PLUS_VID, 16,
109 'V', 'L', 'A', 'N',
110 '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
111 0,
112 VLAN_NAME_TYPE_PLUS_VID_NO_PAD, 22,
113 'V', 'L', 'A', 'N',
114 '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
115 '_', 'N', 'O', '_', 'P', 'A', 'D', 0,
116 VLAN_NAME_TYPE_RAW_PLUS_VID, 15,
117 'D', 'E', 'V',
118 '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
119 0,
120 VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, 20,
121 'D', 'E', 'V',
122 '_', 'P', 'L', 'U', 'S', '_', 'V', 'I', 'D',
123 '_', 'N', 'O', '_', 'P', 'A', 'D', 0,
124};
125
126static const char conf_file_name[] = "/proc/net/vlan/config";
127
128int vconfig_main(int argc, char **argv)
129{
130 struct vlan_ioctl_args ifr;
131 const char *p;
132 int fd;
133
134 if (argc < 3) {
135 bb_show_usage();
136 }
137
138 /* Don't bother closing the filedes. It will be closed on cleanup. */
139 if (open(conf_file_name, O_RDONLY) < 0) { /* Is 802.1q is present? */
140 bb_perror_msg_and_die("open %s", conf_file_name);
141 }
142
143 memset(&ifr, 0, sizeof(struct vlan_ioctl_args));
144
145 ++argv;
146 p = xfind_str(cmds+2, *argv);
147 ifr.cmd = *p;
148 if (argc != p[-1]) {
149 bb_show_usage();
150 }
151
152 if (ifr.cmd == SET_VLAN_NAME_TYPE_CMD) { /* set_name_type */
153 ifr.u.name_type = *xfind_str(name_types+1, argv[1]);
154 } else {
155 if (strlen(argv[1]) >= IF_NAMESIZE) {
156 bb_error_msg_and_die("if_name >= %d chars\n", IF_NAMESIZE);
157 }
158 strcpy(ifr.device1, argv[1]);
159 p = argv[2];
160
161 /* I suppose one could try to combine some of the function calls below,
162 * since ifr.u.flag, ifr.u.VID, and ifr.u.skb_priority are all same-sized
163 * (unsigned) int members of a unions. But because of the range checking,
164 * doing so wouldn't save that much space and would also make maintainence
165 * more of a pain. */
166 if (ifr.cmd == SET_VLAN_FLAG_CMD) { /* set_flag */
167 ifr.u.flag = bb_xgetularg10_bnd(p, 0, 1);
168 } else if (ifr.cmd == ADD_VLAN_CMD) { /* add */
169 ifr.u.VID = bb_xgetularg10_bnd(p, 0, VLAN_GROUP_ARRAY_LEN-1);
170 } else if (ifr.cmd != DEL_VLAN_CMD) { /* set_{egress|ingress}_map */
171 ifr.u.skb_priority = bb_xgetularg10_bnd(p, 0, ULONG_MAX);
172 ifr.vlan_qos = bb_xgetularg10_bnd(argv[3], 0, 7);
173 }
174 }
175
176 if (((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
177 || (ioctl(fd, SIOCSIFVLAN, &ifr) < 0)
178 ) {
179 bb_perror_msg_and_die("socket or ioctl error for %s", *argv);
180 }
181
182 return 0;
183}
184
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * wget - retrieve a file using HTTP or FTP
4 *
5 * Chip Rosenthal Covad Communications <chip@laserlink.net>
6 *
7 */
8
9#include <stdio.h>
10#include <errno.h>
11#include <stdlib.h>
12#include <unistd.h>
13#include <ctype.h>
14#include <string.h>
15#include <unistd.h>
16#include <signal.h>
17#include <sys/ioctl.h>
18
19#include <sys/time.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22#include <sys/socket.h>
23#include <netinet/in.h>
24#include <arpa/inet.h>
25#include <netdb.h>
26
27#ifndef _GNU_SOURCE
28#define _GNU_SOURCE
29#endif
30#include <getopt.h>
31
32#include "busybox.h"
33
34struct host_info {
35 char *host;
36 int port;
37 char *path;
38 int is_ftp;
39 char *user;
40};
41
42static void parse_url(char *url, struct host_info *h);
43static FILE *open_socket(struct sockaddr_in *s_in);
44static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc);
45static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf);
46
47/* Globals (can be accessed from signal handlers */
48static off_t filesize = 0; /* content-length of the file */
49static int chunked = 0; /* chunked transfer encoding */
50#ifdef CONFIG_FEATURE_WGET_STATUSBAR
51static void progressmeter(int flag);
52static char *curfile; /* Name of current file being transferred. */
53static struct timeval start; /* Time a transfer started. */
54static volatile unsigned long statbytes = 0; /* Number of bytes transferred so far. */
55/* For progressmeter() -- number of seconds before xfer considered "stalled" */
56static const int STALLTIME = 5;
57#endif
58
59static void close_and_delete_outfile(FILE* output, char *fname_out, int do_continue)
60{
61 if (output != stdout && do_continue==0) {
62 fclose(output);
63 unlink(fname_out);
64 }
65}
66
67/* Read NMEMB elements of SIZE bytes into PTR from STREAM. Returns the
68 * number of elements read, and a short count if an eof or non-interrupt
69 * error is encountered. */
70static size_t safe_fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
71{
72 size_t ret = 0;
73
74 do {
75 clearerr(stream);
76 ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream);
77 } while (ret < nmemb && ferror(stream) && errno == EINTR);
78
79 return ret;
80}
81
82/* Write NMEMB elements of SIZE bytes from PTR to STREAM. Returns the
83 * number of elements written, and a short count if an eof or non-interrupt
84 * error is encountered. */
85static size_t safe_fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream)
86{
87 size_t ret = 0;
88
89 do {
90 clearerr(stream);
91 ret += fwrite((char *)ptr + (ret * size), size, nmemb - ret, stream);
92 } while (ret < nmemb && ferror(stream) && errno == EINTR);
93
94 return ret;
95}
96
97/* Read a line or SIZE - 1 bytes into S, whichever is less, from STREAM.
98 * Returns S, or NULL if an eof or non-interrupt error is encountered. */
99static char *safe_fgets(char *s, int size, FILE *stream)
100{
101 char *ret;
102
103 do {
104 clearerr(stream);
105 ret = fgets(s, size, stream);
106 } while (ret == NULL && ferror(stream) && errno == EINTR);
107
108 return ret;
109}
110
111#define close_delete_and_die(s...) { \
112 close_and_delete_outfile(output, fname_out, do_continue); \
113 bb_error_msg_and_die(s); }
114
115
116#ifdef CONFIG_FEATURE_WGET_AUTHENTICATION
117/*
118 * Base64-encode character string
119 * oops... isn't something similar in uuencode.c?
120 * It would be better to use already existing code
121 */
122char *base64enc(unsigned char *p, char *buf, int len) {
123
124 char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
125 "0123456789+/";
126 char *s = buf;
127
128 while(*p) {
129 if (s >= buf+len-4)
130 bb_error_msg_and_die("buffer overflow");
131 *(s++) = al[(*p >> 2) & 0x3F];
132 *(s++) = al[((*p << 4) & 0x30) | ((*(p+1) >> 4) & 0x0F)];
133 *s = *(s+1) = '=';
134 *(s+2) = 0;
135 if (! *(++p)) break;
136 *(s++) = al[((*p << 2) & 0x3C) | ((*(p+1) >> 6) & 0x03)];
137 if (! *(++p)) break;
138 *(s++) = al[*(p++) & 0x3F];
139 }
140
141 return buf;
142}
143#endif
144
145#define WGET_OPT_CONTINUE 1
146#define WGET_OPT_QUIET 2
147#define WGET_OPT_PASSIVE 4
148#define WGET_OPT_OUTNAME 8
149#define WGET_OPT_HEADER 16
150#define WGET_OPT_PREFIX 32
151#define WGET_OPT_PROXY 64
152
153static const struct option wget_long_options[] = {
154 { "continue", 0, NULL, 'c' },
155 { "quiet", 0, NULL, 'q' },
156 { "passive-ftp", 0, NULL, 139 },
157 { "output-document", 1, NULL, 'O' },
158 { "header", 1, NULL, 131 },
159 { "directory-prefix",1, NULL, 'P' },
160 { "proxy", 1, NULL, 'Y' },
161 { 0, 0, 0, 0 }
162};
163
164int wget_main(int argc, char **argv)
165{
166 int n, try=5, status;
167 unsigned long opt;
168 int port;
169 char *proxy = 0;
170 char *dir_prefix=NULL;
171 char *s, buf[512];
172 struct stat sbuf;
173 char extra_headers[1024];
174 char *extra_headers_ptr = extra_headers;
175 int extra_headers_left = sizeof(extra_headers);
176 struct host_info server, target;
177 struct sockaddr_in s_in;
178 llist_t *headers_llist = NULL;
179
180 FILE *sfp = NULL; /* socket to web/ftp server */
181 FILE *dfp = NULL; /* socket to ftp server (data) */
182 char *fname_out = NULL; /* where to direct output (-O) */
183 int do_continue = 0; /* continue a prev transfer (-c) */
184 long beg_range = 0L; /* range at which continue begins */
185 int got_clen = 0; /* got content-length: from server */
186 FILE *output; /* socket to web server */
187 int quiet_flag = FALSE; /* Be verry, verry quiet... */
188 int use_proxy = 1; /* Use proxies if env vars are set */
189 char *proxy_flag = "on"; /* Use proxies if env vars are set */
190
191 /*
192 * Crack command line.
193 */
194 bb_opt_complementaly = "\203*";
195 bb_applet_long_options = wget_long_options;
196 opt = bb_getopt_ulflags(argc, argv, "cq\213O:\203:P:Y:", &fname_out, &headers_llist, &dir_prefix, &proxy_flag);
197 if (opt & WGET_OPT_CONTINUE) {
198 ++do_continue;
199 }
200 if (opt & WGET_OPT_QUIET) {
201 quiet_flag = TRUE;
202 }
203 if (strcmp(proxy_flag, "off") == 0) {
204 /* Use the proxy if necessary. */
205 use_proxy = 0;
206 }
207 if (opt & WGET_OPT_HEADER) {
208 while (headers_llist) {
209 int arglen = strlen(headers_llist->data);
210 if (extra_headers_left - arglen - 2 <= 0)
211 bb_error_msg_and_die("extra_headers buffer too small(need %i)", extra_headers_left - arglen);
212 strcpy(extra_headers_ptr, headers_llist->data);
213 extra_headers_ptr += arglen;
214 extra_headers_left -= ( arglen + 2 );
215 *extra_headers_ptr++ = '\r';
216 *extra_headers_ptr++ = '\n';
217 *(extra_headers_ptr + 1) = 0;
218 headers_llist = headers_llist->link;
219 }
220 }
221 if (argc - optind != 1)
222 bb_show_usage();
223
224 parse_url(argv[optind], &target);
225 server.host = target.host;
226 server.port = target.port;
227
228 /*
229 * Use the proxy if necessary.
230 */
231 if (use_proxy) {
232 proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
233 if (proxy && *proxy) {
234 parse_url(bb_xstrdup(proxy), &server);
235 } else {
236 use_proxy = 0;
237 }
238 }
239
240 /* Guess an output filename */
241 if (!fname_out) {
242 // Dirty hack. Needed because bb_get_last_path_component
243 // will destroy trailing / by storing '\0' in last byte!
244 if(target.path[strlen(target.path)-1]!='/') {
245 fname_out =
246#ifdef CONFIG_FEATURE_WGET_STATUSBAR
247 curfile =
248#endif
249 bb_get_last_path_component(target.path);
250 }
251 if (fname_out==NULL || strlen(fname_out)<1) {
252 fname_out =
253#ifdef CONFIG_FEATURE_WGET_STATUSBAR
254 curfile =
255#endif
256 "index.html";
257 }
258 if (dir_prefix != NULL)
259 fname_out = concat_path_file(dir_prefix, fname_out);
260#ifdef CONFIG_FEATURE_WGET_STATUSBAR
261 } else {
262 curfile = bb_get_last_path_component(fname_out);
263#endif
264 }
265 if (do_continue && !fname_out)
266 bb_error_msg_and_die("cannot specify continue (-c) without a filename (-O)");
267
268
269 /*
270 * Open the output file stream.
271 */
272 if (strcmp(fname_out, "-") == 0) {
273 output = stdout;
274 quiet_flag = TRUE;
275 } else {
276 output = bb_xfopen(fname_out, (do_continue ? "a" : "w"));
277 }
278
279 /*
280 * Determine where to start transfer.
281 */
282 if (do_continue) {
283 if (fstat(fileno(output), &sbuf) < 0)
284 bb_perror_msg_and_die("fstat()");
285 if (sbuf.st_size > 0)
286 beg_range = sbuf.st_size;
287 else
288 do_continue = 0;
289 }
290
291 /* We want to do exactly _one_ DNS lookup, since some
292 * sites (i.e. ftp.us.debian.org) use round-robin DNS
293 * and we want to connect to only one IP... */
294 bb_lookup_host(&s_in, server.host);
295 s_in.sin_port = server.port;
296 if (quiet_flag==FALSE) {
297 fprintf(stdout, "Connecting to %s[%s]:%d\n",
298 server.host, inet_ntoa(s_in.sin_addr), ntohs(server.port));
299 }
300
301 if (use_proxy || !target.is_ftp) {
302 /*
303 * HTTP session
304 */
305 do {
306 got_clen = chunked = 0;
307
308 if (! --try)
309 close_delete_and_die("too many redirections");
310
311 /*
312 * Open socket to http server
313 */
314 if (sfp) fclose(sfp);
315 sfp = open_socket(&s_in);
316
317 /*
318 * Send HTTP request.
319 */
320 if (use_proxy) {
321 const char *format = "GET %stp://%s:%d/%s HTTP/1.1\r\n";
322#ifdef CONFIG_FEATURE_WGET_IP6_LITERAL
323 if (strchr (target.host, ':'))
324 format = "GET %stp://[%s]:%d/%s HTTP/1.1\r\n";
325#endif
326 fprintf(sfp, format,
327 target.is_ftp ? "f" : "ht", target.host,
328 ntohs(target.port), target.path);
329 } else {
330 fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
331 }
332
333 fprintf(sfp, "Host: %s\r\nUser-Agent: Wget\r\n", target.host);
334
335#ifdef CONFIG_FEATURE_WGET_AUTHENTICATION
336 if (target.user) {
337 fprintf(sfp, "Authorization: Basic %s\r\n",
338 base64enc(target.user, buf, sizeof(buf)));
339 }
340 if (use_proxy && server.user) {
341 fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
342 base64enc(server.user, buf, sizeof(buf)));
343 }
344#endif
345
346 if (do_continue)
347 fprintf(sfp, "Range: bytes=%ld-\r\n", beg_range);
348 if(extra_headers_left < sizeof(extra_headers))
349 fputs(extra_headers,sfp);
350 fprintf(sfp,"Connection: close\r\n\r\n");
351
352 /*
353 * Retrieve HTTP response line and check for "200" status code.
354 */
355read_response:
356 if (fgets(buf, sizeof(buf), sfp) == NULL)
357 close_delete_and_die("no response from server");
358
359 for (s = buf ; *s != '\0' && !isspace(*s) ; ++s)
360 ;
361 for ( ; isspace(*s) ; ++s)
362 ;
363 switch (status = atoi(s)) {
364 case 0:
365 case 100:
366 while (gethdr(buf, sizeof(buf), sfp, &n) != NULL);
367 goto read_response;
368 case 200:
369 if (do_continue && output != stdout)
370 output = freopen(fname_out, "w", output);
371 do_continue = 0;
372 break;
373 case 300: /* redirection */
374 case 301:
375 case 302:
376 case 303:
377 break;
378 case 206:
379 if (do_continue)
380 break;
381 /*FALLTHRU*/
382 default:
383 chomp(buf);
384 close_delete_and_die("server returned error %d: %s", atoi(s), buf);
385 }
386
387 /*
388 * Retrieve HTTP headers.
389 */
390 while ((s = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) {
391 if (strcasecmp(buf, "content-length") == 0) {
392 unsigned long value;
393 if (safe_strtoul(s, &value)) {
394 close_delete_and_die("content-length %s is garbage", s);
395 }
396 filesize = value;
397 got_clen = 1;
398 continue;
399 }
400 if (strcasecmp(buf, "transfer-encoding") == 0) {
401 if (strcasecmp(s, "chunked") == 0) {
402 chunked = got_clen = 1;
403 } else {
404 close_delete_and_die("server wants to do %s transfer encoding", s);
405 }
406 }
407 if (strcasecmp(buf, "location") == 0) {
408 if (s[0] == '/')
409 target.path = bb_xstrdup(s+1);
410 else {
411 parse_url(bb_xstrdup(s), &target);
412 if (use_proxy == 0) {
413 server.host = target.host;
414 server.port = target.port;
415 }
416 bb_lookup_host(&s_in, server.host);
417 s_in.sin_port = server.port;
418 break;
419 }
420 }
421 }
422 } while(status >= 300);
423
424 dfp = sfp;
425 }
426 else
427 {
428 /*
429 * FTP session
430 */
431 if (! target.user)
432 target.user = bb_xstrdup("anonymous:busybox@");
433
434 sfp = open_socket(&s_in);
435 if (ftpcmd(NULL, NULL, sfp, buf) != 220)
436 close_delete_and_die("%s", buf+4);
437
438 /*
439 * Splitting username:password pair,
440 * trying to log in
441 */
442 s = strchr(target.user, ':');
443 if (s)
444 *(s++) = '\0';
445 switch(ftpcmd("USER ", target.user, sfp, buf)) {
446 case 230:
447 break;
448 case 331:
449 if (ftpcmd("PASS ", s, sfp, buf) == 230)
450 break;
451 /* FALLTHRU (failed login) */
452 default:
453 close_delete_and_die("ftp login: %s", buf+4);
454 }
455
456 ftpcmd("CDUP", NULL, sfp, buf);
457 ftpcmd("TYPE I", NULL, sfp, buf);
458
459 /*
460 * Querying file size
461 */
462 if (ftpcmd("SIZE /", target.path, sfp, buf) == 213) {
463 unsigned long value;
464 if (safe_strtoul(buf+4, &value)) {
465 close_delete_and_die("SIZE value is garbage");
466 }
467 filesize = value;
468 got_clen = 1;
469 }
470
471 /*
472 * Entering passive mode
473 */
474 if (ftpcmd("PASV", NULL, sfp, buf) != 227)
475 close_delete_and_die("PASV: %s", buf+4);
476 s = strrchr(buf, ',');
477 *s = 0;
478 port = atoi(s+1);
479 s = strrchr(buf, ',');
480 port += atoi(s+1) * 256;
481 s_in.sin_port = htons(port);
482 dfp = open_socket(&s_in);
483
484 if (do_continue) {
485 sprintf(buf, "REST %ld", beg_range);
486 if (ftpcmd(buf, NULL, sfp, buf) != 350) {
487 if (output != stdout)
488 output = freopen(fname_out, "w", output);
489 do_continue = 0;
490 } else
491 filesize -= beg_range;
492 }
493
494 if (ftpcmd("RETR /", target.path, sfp, buf) > 150)
495 close_delete_and_die("RETR: %s", buf+4);
496
497 }
498
499
500 /*
501 * Retrieve file
502 */
503 if (chunked) {
504 fgets(buf, sizeof(buf), dfp);
505 filesize = strtol(buf, (char **) NULL, 16);
506 }
507#ifdef CONFIG_FEATURE_WGET_STATUSBAR
508 if (quiet_flag==FALSE)
509 progressmeter(-1);
510#endif
511 do {
512 while ((filesize > 0 || !got_clen) && (n = safe_fread(buf, 1, ((chunked || got_clen) && (filesize < sizeof(buf)) ? filesize : sizeof(buf)), dfp)) > 0) {
513 if (safe_fwrite(buf, 1, n, output) != n) {
514 bb_perror_msg_and_die("write error");
515 }
516#ifdef CONFIG_FEATURE_WGET_STATUSBAR
517 statbytes+=n;
518#endif
519 if (got_clen) {
520 filesize -= n;
521 }
522 }
523
524 if (chunked) {
525 safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
526 safe_fgets(buf, sizeof(buf), dfp);
527 filesize = strtol(buf, (char **) NULL, 16);
528 if (filesize==0) {
529 chunked = 0; /* all done! */
530 }
531 }
532
533 if (n == 0 && ferror(dfp)) {
534 bb_perror_msg_and_die("network read error");
535 }
536 } while (chunked);
537#ifdef CONFIG_FEATURE_WGET_STATUSBAR
538 if (quiet_flag==FALSE)
539 progressmeter(1);
540#endif
541 if ((use_proxy == 0) && target.is_ftp) {
542 fclose(dfp);
543 if (ftpcmd(NULL, NULL, sfp, buf) != 226)
544 bb_error_msg_and_die("ftp error: %s", buf+4);
545 ftpcmd("QUIT", NULL, sfp, buf);
546 }
547 exit(EXIT_SUCCESS);
548}
549
550
551void parse_url(char *url, struct host_info *h)
552{
553 char *cp, *sp, *up, *pp;
554
555 if (strncmp(url, "http://", 7) == 0) {
556 h->port = bb_lookup_port("http", "tcp", 80);
557 h->host = url + 7;
558 h->is_ftp = 0;
559 } else if (strncmp(url, "ftp://", 6) == 0) {
560 h->port = bb_lookup_port("ftp", "tfp", 21);
561 h->host = url + 6;
562 h->is_ftp = 1;
563 } else
564 bb_error_msg_and_die("not an http or ftp url: %s", url);
565
566 sp = strchr(h->host, '/');
567 if (sp) {
568 *sp++ = '\0';
569 h->path = sp;
570 } else
571 h->path = bb_xstrdup("");
572
573 up = strrchr(h->host, '@');
574 if (up != NULL) {
575 h->user = h->host;
576 *up++ = '\0';
577 h->host = up;
578 } else
579 h->user = NULL;
580
581 pp = h->host;
582
583#ifdef CONFIG_FEATURE_WGET_IP6_LITERAL
584 if (h->host[0] == '[') {
585 char *ep;
586
587 ep = h->host + 1;
588 while (*ep == ':' || isxdigit (*ep))
589 ep++;
590 if (*ep == ']') {
591 h->host++;
592 *ep = '\0';
593 pp = ep + 1;
594 }
595 }
596#endif
597
598 cp = strchr(pp, ':');
599 if (cp != NULL) {
600 *cp++ = '\0';
601 h->port = htons(atoi(cp));
602 }
603}
604
605
606FILE *open_socket(struct sockaddr_in *s_in)
607{
608 FILE *fp;
609
610 fp = fdopen(xconnect(s_in), "r+");
611 if (fp == NULL)
612 bb_perror_msg_and_die("fdopen()");
613
614 return fp;
615}
616
617
618char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc)
619{
620 char *s, *hdrval;
621 int c;
622
623 *istrunc = 0;
624
625 /* retrieve header line */
626 if (fgets(buf, bufsiz, fp) == NULL)
627 return NULL;
628
629 /* see if we are at the end of the headers */
630 for (s = buf ; *s == '\r' ; ++s)
631 ;
632 if (s[0] == '\n')
633 return NULL;
634
635 /* convert the header name to lower case */
636 for (s = buf ; isalnum(*s) || *s == '-' ; ++s)
637 *s = tolower(*s);
638
639 /* verify we are at the end of the header name */
640 if (*s != ':')
641 bb_error_msg_and_die("bad header line: %s", buf);
642
643 /* locate the start of the header value */
644 for (*s++ = '\0' ; *s == ' ' || *s == '\t' ; ++s)
645 ;
646 hdrval = s;
647
648 /* locate the end of header */
649 while (*s != '\0' && *s != '\r' && *s != '\n')
650 ++s;
651
652 /* end of header found */
653 if (*s != '\0') {
654 *s = '\0';
655 return hdrval;
656 }
657
658 /* Rats! The buffer isn't big enough to hold the entire header value. */
659 while (c = getc(fp), c != EOF && c != '\n')
660 ;
661 *istrunc = 1;
662 return hdrval;
663}
664
665static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf)
666{
667 if (s1) {
668 if (!s2) s2="";
669 fprintf(fp, "%s%s\r\n", s1, s2);
670 fflush(fp);
671 }
672
673 do {
674 char *buf_ptr;
675
676 if (fgets(buf, 510, fp) == NULL) {
677 bb_perror_msg_and_die("fgets()");
678 }
679 buf_ptr = strstr(buf, "\r\n");
680 if (buf_ptr) {
681 *buf_ptr = '\0';
682 }
683 } while (! isdigit(buf[0]) || buf[3] != ' ');
684
685 return atoi(buf);
686}
687
688#ifdef CONFIG_FEATURE_WGET_STATUSBAR
689/* Stuff below is from BSD rcp util.c, as added to openshh.
690 * Original copyright notice is retained at the end of this file.
691 *
692 */
693
694
695static int
696getttywidth(void)
697{
698 int width=0;
699 get_terminal_width_height(0, &width, NULL);
700 return (width);
701}
702
703static void
704updateprogressmeter(int ignore)
705{
706 int save_errno = errno;
707
708 progressmeter(0);
709 errno = save_errno;
710}
711
712static void
713alarmtimer(int wait)
714{
715 struct itimerval itv;
716
717 itv.it_value.tv_sec = wait;
718 itv.it_value.tv_usec = 0;
719 itv.it_interval = itv.it_value;
720 setitimer(ITIMER_REAL, &itv, NULL);
721}
722
723
724static void
725progressmeter(int flag)
726{
727 static const char prefixes[] = " KMGTP";
728 static struct timeval lastupdate;
729 static off_t lastsize, totalsize;
730 struct timeval now, td, wait;
731 off_t cursize, abbrevsize;
732 double elapsed;
733 int ratio, barlength, i, remaining;
734 char buf[256];
735
736 if (flag == -1) {
737 (void) gettimeofday(&start, (struct timezone *) 0);
738 lastupdate = start;
739 lastsize = 0;
740 totalsize = filesize; /* as filesize changes.. */
741 }
742
743 (void) gettimeofday(&now, (struct timezone *) 0);
744 cursize = statbytes;
745 if (totalsize != 0 && !chunked) {
746 ratio = 100.0 * cursize / totalsize;
747 ratio = MAX(ratio, 0);
748 ratio = MIN(ratio, 100);
749 } else
750 ratio = 100;
751
752 snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio);
753
754 barlength = getttywidth() - 51;
755 if (barlength > 0) {
756 i = barlength * ratio / 100;
757 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
758 "|%.*s%*s|", i,
759 "*****************************************************************************"
760 "*****************************************************************************",
761 barlength - i, "");
762 }
763 i = 0;
764 abbrevsize = cursize;
765 while (abbrevsize >= 100000 && i < sizeof(prefixes)) {
766 i++;
767 abbrevsize >>= 10;
768 }
769 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5d %c%c ",
770 (int) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' :
771 'B');
772
773 timersub(&now, &lastupdate, &wait);
774 if (cursize > lastsize) {
775 lastupdate = now;
776 lastsize = cursize;
777 if (wait.tv_sec >= STALLTIME) {
778 start.tv_sec += wait.tv_sec;
779 start.tv_usec += wait.tv_usec;
780 }
781 wait.tv_sec = 0;
782 }
783 timersub(&now, &start, &td);
784 elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
785
786 if (wait.tv_sec >= STALLTIME) {
787 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
788 " - stalled -");
789 } else if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalsize || chunked) {
790 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
791 " --:-- ETA");
792 } else {
793 remaining = (int) (totalsize / (statbytes / elapsed) - elapsed);
794 i = remaining / 3600;
795 if (i)
796 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
797 "%2d:", i);
798 else
799 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
800 " ");
801 i = remaining % 3600;
802 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
803 "%02d:%02d ETA", i / 60, i % 60);
804 }
805 write(STDERR_FILENO, buf, strlen(buf));
806
807 if (flag == -1) {
808 struct sigaction sa;
809 sa.sa_handler = updateprogressmeter;
810 sigemptyset(&sa.sa_mask);
811 sa.sa_flags = SA_RESTART;
812 sigaction(SIGALRM, &sa, NULL);
813 alarmtimer(1);
814 } else if (flag == 1) {
815 alarmtimer(0);
816 statbytes = 0;
817 putc('\n', stderr);
818 }
819}
820#endif
821
822/* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff,
823 * much of which was blatantly stolen from openssh. */
824
825/*-
826 * Copyright (c) 1992, 1993
827 * The Regents of the University of California. All rights reserved.
828 *
829 * Redistribution and use in source and binary forms, with or without
830 * modification, are permitted provided that the following conditions
831 * are met:
832 * 1. Redistributions of source code must retain the above copyright
833 * notice, this list of conditions and the following disclaimer.
834 * 2. Redistributions in binary form must reproduce the above copyright
835 * notice, this list of conditions and the following disclaimer in the
836 * documentation and/or other materials provided with the distribution.
837 *
838 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
839 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
840 *
841 * 4. Neither the name of the University nor the names of its contributors
842 * may be used to endorse or promote products derived from this software
843 * without specific prior written permission.
844 *
845 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
846 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
847 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
848 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
849 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
850 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
851 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
852 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
853 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
854 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
855 * SUCH DAMAGE.
856 *
857 * $Id: wget.c,v 1.75 2004/10/08 08:27:40 andersen Exp $
858 */
859
860
861
862/*
863Local Variables:
864c-file-style: "linux"
865c-basic-offset: 4
866tab-width: 4
867End:
868*/
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 @@
1Index: coreutils/Config.in
2===================================================================
3RCS file: /var/cvs/busybox/coreutils/Config.in,v
4retrieving revision 1.24
5diff -u -r1.24 Config.in
6--- a/coreutils/Config.in 15 Mar 2004 08:28:19 -0000 1.24
7+++ b/coreutils/Config.in 31 Mar 2004 11:51:17 -0000
8@@ -59,6 +59,21 @@
9 cmp is used to compare two files and returns the result
10 to standard output.
11
12+config CONFIG_FEATURE_CMP_SKIP
13+ bool " Enable optional arguments SKIP1 and SKIP2"
14+ default n
15+ depends on CONFIG_CMP
16+ help
17+ SKIP1 and SKIP2 specify how many bytes to ignore
18+ at the start of each file.
19+
20+config CONFIG_FEATURE_CMP_LIMIT
21+ bool " Enable limit of inputs"
22+ default n
23+ depends on CONFIG_CMP
24+ help
25+ Enable cmp option (-n).
26+
27 config CONFIG_CP
28 bool "cp"
29 default n
30Index: coreutils/cmp.c
31===================================================================
32RCS file: /var/cvs/busybox/coreutils/cmp.c,v
33retrieving revision 1.9
34diff -u -r1.9 cmp.c
35--- a/coreutils/cmp.c 19 Mar 2003 09:11:32 -0000 1.9
36+++ b/coreutils/cmp.c 31 Mar 2004 11:51:17 -0000
37@@ -39,6 +39,12 @@
38 #include <unistd.h>
39 #include "busybox.h"
40
41+#ifdef CONFIG_FEATURE_CMP_SKIP
42+#define MAX_OPTIONAL_ARGS 3
43+#else
44+#define MAX_OPTIONAL_ARGS 1
45+#endif
46+
47 static FILE *cmp_xfopen_input(const char *filename)
48 {
49 FILE *fp;
50@@ -58,12 +64,57 @@
51 static const char fmt_l_opt[] = "%.0s%.0s%d %3o %3o\n"; /* nicer gnu format */
52 #endif
53
54-static const char opt_chars[] = "sl";
55+#ifdef CONFIG_FEATURE_CMP_LIMIT
56+#define OPTCHR_LIMIT "n:"
57+#define OPTARG_LIMIT ,&limit_str
58+#else
59+#define OPTCHR_LIMIT
60+#define OPTARG_LIMIT
61+#endif
62+
63+static const char opt_chars[] = "sl" OPTCHR_LIMIT;
64
65 enum {
66 OPT_s = 1,
67- OPT_l = 2
68+ OPT_l = 2,
69+ OPT_n = 4
70+};
71+
72+#ifdef CONFIG_LFS
73+#define SUFFIX_STRUCT suffix_mult64
74+#define PARSE_FUNC bb_xgetllarg10_sfx
75+#else
76+#define SUFFIX_STRUCT suffix_mult
77+#define PARSE_FUNC bb_xgetlarg10_sfx
78+#endif
79+
80+#if defined(CONFIG_FEATURE_CMP_SKIP) || defined(CONFIG_FEATURE_CMP_LIMIT)
81+static const struct SUFFIX_STRUCT suffixes[] = {
82+ { "k", 1UL << 10 },
83+ { "M", 1UL << 20 },
84+ { "G", 1UL << 30 },
85+#ifdef CONFIG_LFS
86+ { "T", 1ULL << 40 },
87+ { "P", 1ULL << 50 },
88+ { "E", 1ULL << 60 },
89+#endif
90+ { NULL, 0 }
91 };
92+#endif
93+
94+#ifdef CONFIG_FEATURE_CMP_SKIP
95+static void skip_input(FILE *fp, off_t skip, const char *filename)
96+{
97+ if (skip > 0 && fseeko(fp, skip, SEEK_CUR) != 0) {
98+ while (skip-- > 0) {
99+ if (getc(fp) == EOF) {
100+ bb_xferror(fp, filename);
101+ break;
102+ }
103+ }
104+ }
105+}
106+#endif
107
108 int cmp_main(int argc, char **argv)
109 {
110@@ -73,12 +124,26 @@
111 int c1, c2, char_pos, line_pos;
112 int opt_flags;
113 int exit_val = 0;
114+#ifdef CONFIG_FEATURE_CMP_SKIP
115+ off_t skip1 = 0, skip2 = 0;
116+#endif
117+#ifdef CONFIG_FEATURE_CMP_LIMIT
118+ off_t limit = -1;
119+ char *limit_str;
120+#endif
121
122 bb_default_error_retval = 2; /* 1 is returned if files are different. */
123
124- opt_flags = bb_getopt_ulflags(argc, argv, opt_chars);
125+ opt_flags = bb_getopt_ulflags(argc, argv, opt_chars OPTARG_LIMIT);
126
127- if ((opt_flags == 3) || (((unsigned int)(--argc - optind)) > 1)) {
128+#ifdef CONFIG_FEATURE_CMP_LIMIT
129+ if (opt_flags & OPT_n) {
130+ limit = PARSE_FUNC(limit_str, suffixes);
131+ opt_flags &= 3;
132+ }
133+#endif
134+
135+ if ((opt_flags == 3) || (((unsigned int)(--argc - optind)) > MAX_OPTIONAL_ARGS)) {
136 bb_show_usage();
137 }
138
139@@ -87,6 +152,13 @@
140 filename2 = "-";
141 if (*++argv) {
142 filename2 = *argv;
143+#ifdef CONFIG_FEATURE_CMP_SKIP
144+ if (*++argv) {
145+ skip1 = PARSE_FUNC(*argv, suffixes);
146+ if (*++argv)
147+ skip2 = PARSE_FUNC(*argv, suffixes);
148+ }
149+#endif
150 }
151 fp2 = cmp_xfopen_input(filename2);
152
153@@ -98,6 +170,11 @@
154 return 0;
155 }
156
157+#ifdef CONFIG_FEATURE_CMP_SKIP
158+ skip_input(fp1, skip1, filename1);
159+ skip_input(fp2, skip2, filename2);
160+#endif
161+
162 fmt = fmt_differ;
163 if (opt_flags == OPT_l) {
164 fmt = fmt_l_opt;
165@@ -106,6 +183,10 @@
166 char_pos = 0;
167 line_pos = 1;
168 do {
169+#ifdef CONFIG_FEATURE_CMP_LIMIT
170+ if (limit-- == 0)
171+ break;
172+#endif
173 c1 = getc(fp1);
174 c2 = getc(fp2);
175 ++char_pos;
176Index: include/usage.h
177===================================================================
178RCS file: /var/cvs/busybox/include/usage.h,v
179retrieving revision 1.198
180diff -u -r1.198 usage.h
181--- a/include/usage.h 29 Mar 2004 08:20:08 -0000 1.198
182+++ b/include/usage.h 31 Mar 2004 11:51:17 -0000
183@@ -186,14 +186,29 @@
184 #define clear_full_usage \
185 "Clear screen."
186
187+#ifdef CONFIG_FEATURE_CMP_SKIP
188+#define USAGE_CMP_SKIP(a) a
189+#else
190+#define USAGE_CMP_SKIP(a)
191+#endif
192+
193+#ifdef CONFIG_FEATURE_CMP_LIMIT
194+#define USAGE_CMP_LIMIT(a) a
195+#else
196+#define USAGE_CMP_LIMIT(a)
197+#endif
198+
199 #define cmp_trivial_usage \
200- "[OPTION]... FILE1 [FILE2]"
201+ "[OPTION]... FILE1 [FILE2" USAGE_CMP_SKIP(" [SKIP1 [SKIP2]]") "]"
202 #define cmp_full_usage \
203- "Compare files.\n\n" \
204+ "Compare files.\n" \
205+ USAGE_CMP_SKIP("SKIP1 and SKIP2 are the number of bytes to skip in each file.\n") \
206+ "\n" \
207 "Options:\n" \
208- "\t-l\tWrite the byte numbers (decimal) and values (octal)\n" \
209- "\t\t for all differing bytes.\n" \
210- "\t-s\tquiet mode - do not print"
211+ "\t-l\t\tWrite the byte numbers (decimal) and values (octal)\n" \
212+ "\t\t\t for all differing bytes.\n" \
213+ USAGE_CMP_LIMIT("\t-n LIMIT\tCompare at most LIMIT bytes.\n") \
214+ "\t-s\t\tquiet mode - do not print"
215
216 #define cp_trivial_usage \
217 "[OPTION]... SOURCE DEST"
218Index: include/libbb.h
219===================================================================
220RCS file: /var/cvs/busybox/include/libbb.h,v
221retrieving revision 1.129
222diff -u -r1.129 libbb.h
223--- a/include/libbb.h 15 Mar 2004 08:28:38 -0000 1.129
224+++ b/include/libbb.h 31 Mar 2004 11:51:17 -0000
225@@ -217,6 +217,21 @@
226 const struct suffix_mult *suffixes);
227 extern long bb_xgetlarg10_sfx(const char *arg, const struct suffix_mult *suffixes);
228
229+struct suffix_mult64 {
230+ const char *suffix;
231+ unsigned long long mult;
232+};
233+
234+extern unsigned long long bb_xgetullarg_bnd_sfx(const char *arg, int base,
235+ unsigned long long lower,
236+ unsigned long long upper,
237+ const struct suffix_mult64 *suffixes);
238+
239+extern long long bb_xgetllarg_bnd_sfx(const char *arg, int base,
240+ long long lower,
241+ long long upper,
242+ const struct suffix_mult64 *suffixes);
243+extern long long bb_xgetllarg10_sfx(const char *arg, const struct suffix_mult64 *suffixes);
244
245 //#warning pitchable now?
246 extern unsigned long bb_xparse_number(const char *numstr,
247Index: libbb/Makefile.in
248===================================================================
249RCS file: /var/cvs/busybox/libbb/Makefile.in,v
250retrieving revision 1.34
251diff -u -r1.34 Makefile.in
252--- a/libbb/Makefile.in 6 Mar 2004 22:11:45 -0000 1.34
253+++ b/libbb/Makefile.in 31 Mar 2004 11:51:17 -0000
254@@ -70,7 +70,8 @@
255
256 LIBBB_MSRC3:=$(LIBBB_DIR)xgetularg.c
257 LIBBB_MOBJ3:=xgetularg_bnd_sfx.o xgetlarg_bnd_sfx.o getlarg10_sfx.o \
258- xgetularg_bnd.o xgetularg10_bnd.o xgetularg10.o
259+ xgetularg_bnd.o xgetularg10_bnd.o xgetularg10.o \
260+ xgetullarg_bnd_sfx.o xgetllarg_bnd_sfx.o xgetllarg10_sfx.o
261
262 LIBBB_MSRC4:=$(LIBBB_DIR)/safe_strtol.c
263 LIBBB_MOBJ4:=safe_strtoi.o safe_strtod.o safe_strtol.o safe_strtoul.o
264Index: libbb/xgetularg.c
265===================================================================
266RCS file: /var/cvs/busybox/libbb/xgetularg.c,v
267retrieving revision 1.2
268diff -u -r1.2 xgetularg.c
269--- a/libbb/xgetularg.c 15 Mar 2004 08:28:44 -0000 1.2
270+++ b/libbb/xgetularg.c 31 Mar 2004 11:51:17 -0000
271@@ -158,3 +158,106 @@
272 return bb_xgetularg10_bnd(arg, 0, ULONG_MAX);
273 }
274 #endif
275+
276+#ifdef L_xgetullarg_bnd_sfx
277+extern
278+unsigned long long bb_xgetullarg_bnd_sfx(const char *arg, int base,
279+ unsigned long long lower,
280+ unsigned long long upper,
281+ const struct suffix_mult64 *suffixes)
282+{
283+ unsigned long long r;
284+ int old_errno;
285+ char *e;
286+
287+ assert(arg);
288+
289+ /* Disallow '-' and any leading whitespace. Speed isn't critical here
290+ * since we're parsing commandline args. So make sure we get the
291+ * actual isspace function rather than a larger macro implementaion. */
292+ if ((*arg == '-') || (isspace)(*arg)) {
293+ bb_show_usage();
294+ }
295+
296+ /* Since this is a lib function, we're not allowed to reset errno to 0.
297+ * Doing so could break an app that is deferring checking of errno.
298+ * So, save the old value so that we can restore it if successful. */
299+ old_errno = errno;
300+ errno = 0;
301+ r = strtoull(arg, &e, base);
302+ /* Do the initial validity check. Note: The standards do not
303+ * guarantee that errno is set if no digits were found. So we
304+ * must test for this explicitly. */
305+ if (errno || (arg == e)) { /* error or no digits */
306+ bb_show_usage();
307+ }
308+ errno = old_errno; /* Ok. So restore errno. */
309+
310+ /* Do optional suffix parsing. Allow 'empty' suffix tables.
311+ * Note that we also all nul suffixes with associated multipliers,
312+ * to allow for scaling of the arg by some default multiplier. */
313+
314+ if (suffixes) {
315+ while (suffixes->suffix) {
316+ if (strcmp(suffixes->suffix, e) == 0) {
317+ if (ULONG_LONG_MAX / suffixes->mult < r) { /* Overflow! */
318+ bb_show_usage();
319+ }
320+ ++e;
321+ r *= suffixes->mult;
322+ break;
323+ }
324+ ++suffixes;
325+ }
326+ }
327+
328+ /* Finally, check for illegal trailing chars and range limits. */
329+ /* Note: although we allow leading space (via stroul), trailing space
330+ * is an error. It would be easy enough to allow though if desired. */
331+ if (*e || (r < lower) || (r > upper)) {
332+ bb_show_usage();
333+ }
334+
335+ return r;
336+}
337+#endif
338+
339+#ifdef L_xgetllarg_bnd_sfx
340+extern
341+long long bb_xgetllarg_bnd_sfx(const char *arg, int base,
342+ long long lower,
343+ long long upper,
344+ const struct suffix_mult64 *suffixes)
345+{
346+ unsigned long long u = LONG_LONG_MAX;
347+ long long r;
348+ const char *p = arg;
349+
350+ if ((*p == '-') && (p[1] != '+')) {
351+ ++p;
352+#if LONG_LONG_MAX == (-(LONG_LONG_MIN + 1))
353+ ++u; /* two's complement */
354+#endif
355+ }
356+
357+ r = bb_xgetullarg_bnd_sfx(p, base, 0, u, suffixes);
358+
359+ if (*arg == '-') {
360+ r = -r;
361+ }
362+
363+ if ((r < lower) || (r > upper)) {
364+ bb_show_usage();
365+ }
366+
367+ return r;
368+}
369+#endif
370+
371+#ifdef L_xgetllarg10_sfx
372+extern
373+long long bb_xgetllarg10_sfx(const char *arg, const struct suffix_mult64 *suffixes)
374+{
375+ return bb_xgetllarg_bnd_sfx(arg, 10, LONG_LONG_MIN, LONG_LONG_MAX, suffixes);
376+}
377+#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 @@
1This patch adds support of ibs= and obs= to dd.
2----
3Hideki IWAMOTO h-iwamoto@kit.hi-ho.ne.jp
4
5
6--- a/include/usage.h 29 Mar 2004 08:20:08 -0000 1.198
7+++ b/include/usage.h 4 Apr 2004 07:15:21 -0000
8@@ -309,13 +309,15 @@
9 "64\n"
10
11 #define dd_trivial_usage \
12- "[if=FILE] [of=FILE] [bs=N] [count=N] [skip=N]\n" \
13- "\t [seek=N] [conv=notrunc|noerror|sync]"
14+ "[if=FILE] [of=FILE] [ibs=N] [obs=N] [bs=N] [count=N]\n" \
15+ "\t [skip=N] [seek=N] [conv=notrunc|noerror|sync]"
16 #define dd_full_usage \
17 "Copy a file, converting and formatting according to options\n\n" \
18 "\tif=FILE\t\tread from FILE instead of stdin\n" \
19 "\tof=FILE\t\twrite to FILE instead of stdout\n" \
20- "\tbs=N\t\tread and write N bytes at a time\n" \
21+ "\tibs=N\t\tread N bytes at a time\n" \
22+ "\tobs=N\t\twrite N bytes at a time\n" \
23+ "\tbs=N\t\tforce ibs=N and obs=N\n" \
24 "\tcount=N\t\tcopy only N input blocks\n" \
25 "\tskip=N\t\tskip N input blocks\n" \
26 "\tseek=N\t\tskip N output blocks\n" \
27@@ -323,6 +325,8 @@
28 "\tconv=noerror\tcontinue after read errors\n" \
29 "\tconv=sync\tpad blocks with zeros\n" \
30 "\n" \
31+ "If the bs= expr operand is not specified, the input is processed and collected\n" \
32+ "into full-sized output blocks until the end of the input is reached.\n" \
33 "Numbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024),\n" \
34 "MD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)."
35 #define dd_example_usage \
36--- a/coreutils/dd.c 30 Jan 2004 22:24:32 -0000 1.55
37+++ b/coreutils/dd.c 4 Apr 2004 07:15:21 -0000
38@@ -30,6 +30,10 @@
39 #include <fcntl.h>
40 #include "busybox.h"
41
42+#define C_NOERROR 0x0001
43+#define C_TRUNC 0x0002
44+#define C_SYNC 0x0004
45+#define C_TWOBUFS 0x0008
46
47 static const struct suffix_mult dd_suffixes[] = {
48 { "c", 1 },
49@@ -51,13 +55,13 @@
50 size_t in_full = 0;
51 size_t in_part = 0;
52 size_t count = -1;
53- size_t bs = 512;
54+ size_t ibs = 512;
55+ size_t obs = 512;
56+ size_t oc = 0;
57 ssize_t n;
58 off_t seek = 0;
59 off_t skip = 0;
60- int sync_flag = FALSE;
61- int noerror = FALSE;
62- int trunc_flag = TRUE;
63+ unsigned int dd_flags = C_TWOBUFS | C_TRUNC;
64 int oflag;
65 int ifd;
66 int ofd;
67@@ -65,11 +69,18 @@
68 const char *infile = NULL;
69 const char *outfile = NULL;
70 char *buf;
71+ char *ibuf;
72+ char *obuf;
73
74 for (i = 1; i < argc; i++) {
75- if (strncmp("bs=", argv[i], 3) == 0)
76- bs = bb_xparse_number(argv[i]+3, dd_suffixes);
77- else if (strncmp("count=", argv[i], 6) == 0)
78+ if (strncmp("ibs=", argv[i], 4) == 0)
79+ ibs = bb_xparse_number(argv[i]+4, dd_suffixes);
80+ else if (strncmp("obs=", argv[i], 4) == 0)
81+ obs = bb_xparse_number(argv[i]+4, dd_suffixes);
82+ else if (strncmp("bs=", argv[i], 3) == 0) {
83+ ibs = obs = bb_xparse_number(argv[i]+3, dd_suffixes);
84+ dd_flags &= ~C_TWOBUFS;
85+ } else if (strncmp("count=", argv[i], 6) == 0)
86 count = bb_xparse_number(argv[i]+6, dd_suffixes);
87 else if (strncmp("seek=", argv[i], 5) == 0)
88 seek = bb_xparse_number(argv[i]+5, dd_suffixes);
89@@ -83,13 +94,13 @@
90 buf = argv[i]+5;
91 while (1) {
92 if (strncmp("notrunc", buf, 7) == 0) {
93- trunc_flag = FALSE;
94+ dd_flags &= ~C_TRUNC;
95 buf += 7;
96 } else if (strncmp("sync", buf, 4) == 0) {
97- sync_flag = TRUE;
98+ dd_flags |= C_SYNC;
99 buf += 4;
100 } else if (strncmp("noerror", buf, 7) == 0) {
101- noerror = TRUE;
102+ dd_flags |= C_NOERROR;
103 buf += 7;
104 } else {
105 bb_error_msg_and_die("invalid conversion `%s'", argv[i]+5);
106@@ -103,7 +114,12 @@
107 bb_show_usage();
108 }
109
110- buf = xmalloc(bs);
111+ ibuf = xmalloc(ibs);
112+
113+ if (dd_flags & C_TWOBUFS)
114+ obuf = xmalloc(obs);
115+ else
116+ obuf = ibuf;
117
118 if (infile != NULL) {
119 ifd = bb_xopen(infile, O_RDONLY);
120@@ -115,7 +131,7 @@
121 if (outfile != NULL) {
122 oflag = O_WRONLY | O_CREAT;
123
124- if (!seek && trunc_flag) {
125+ if (!seek && (dd_flags & C_TRUNC)) {
126 oflag |= O_TRUNC;
127 }
128
129@@ -123,8 +139,8 @@
130 bb_perror_msg_and_die("%s", outfile);
131 }
132
133- if (seek && trunc_flag) {
134- if (ftruncate(ofd, seek * bs) < 0) {
135+ if (seek && (dd_flags & C_TRUNC)) {
136+ if (ftruncate(ofd, seek * obs) < 0) {
137 struct stat st;
138
139 if (fstat (ofd, &st) < 0 || S_ISREG (st.st_mode) ||
140@@ -139,52 +155,88 @@
141 }
142
143 if (skip) {
144- if (lseek(ifd, skip * bs, SEEK_CUR) < 0) {
145- bb_perror_msg_and_die("%s", infile);
146+ if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) {
147+ while (skip-- > 0) {
148+ n = safe_read(ifd, ibuf, ibs);
149+ if (n < 0)
150+ bb_perror_msg_and_die("%s", infile);
151+ if (n == 0)
152+ break;
153+ }
154 }
155 }
156
157 if (seek) {
158- if (lseek(ofd, seek * bs, SEEK_CUR) < 0) {
159+ if (lseek(ofd, seek * obs, SEEK_CUR) < 0) {
160 bb_perror_msg_and_die("%s", outfile);
161 }
162 }
163
164 while (in_full + in_part != count) {
165- if (noerror) {
166+ if (dd_flags & C_NOERROR) {
167 /* Pre-zero the buffer when doing the noerror thing */
168- memset(buf, '\0', bs);
169+ memset(ibuf, '\0', ibs);
170+ }
171+
172+ n = safe_read(ifd, ibuf, ibs);
173+ if (n == 0) {
174+ break;
175 }
176- n = safe_read(ifd, buf, bs);
177 if (n < 0) {
178- if (noerror) {
179- n = bs;
180+ if (dd_flags & C_NOERROR) {
181+ n = ibs;
182 bb_perror_msg("%s", infile);
183 } else {
184 bb_perror_msg_and_die("%s", infile);
185 }
186 }
187- if (n == 0) {
188- break;
189- }
190- if (n == bs) {
191+ if (n == ibs) {
192 in_full++;
193 } else {
194 in_part++;
195+ if (dd_flags & C_SYNC) {
196+ memset(ibuf + n, '\0', ibs - n);
197+ n = ibs;
198+ }
199 }
200- if (sync_flag) {
201- memset(buf + n, '\0', bs - n);
202- n = bs;
203+
204+ if (dd_flags & C_TWOBUFS) {
205+ size_t d;
206+ char *tmp = ibuf;
207+
208+ while (n) {
209+ d = obs - oc;
210+ if (d > n)
211+ d = n;
212+ memcpy(obuf + oc, tmp, d);
213+ n -= d;
214+ tmp += d;
215+ oc += d;
216+ if (oc == obs) {
217+ if (bb_full_write(ofd, obuf, obs) < 0) {
218+ bb_perror_msg_and_die("%s", outfile);
219+ }
220+ out_full++;
221+ oc = 0;
222+ }
223+ }
224+ } else {
225+ if (bb_full_write(ofd, ibuf, n) < 0) {
226+ bb_perror_msg_and_die("%s", outfile);
227+ }
228+ if (n == ibs) {
229+ out_full++;
230+ } else {
231+ out_part++;
232+ }
233 }
234- n = bb_full_write(ofd, buf, n);
235- if (n < 0) {
236+ }
237+
238+ if (oc) {
239+ if (bb_full_write(ofd, obuf, oc) < 0) {
240 bb_perror_msg_and_die("%s", outfile);
241 }
242- if (n == bs) {
243- out_full++;
244- } else {
245- out_part++;
246- }
247+ out_part++;
248 }
249
250 if (close (ifd) < 0) {
251
252
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 @@
1Index: AUTHORS
2===================================================================
3RCS file: /var/cvs/busybox/AUTHORS,v
4retrieving revision 1.40
5diff -u -r1.40 AUTHORS
6--- a/AUTHORS 9 Oct 2003 21:19:21 -0000 1.40
7+++ b/AUTHORS 5 Mar 2004 07:23:17 -0000
8@@ -8,6 +8,9 @@
9
10 -----------
11
12+Peter Willis <psyphreak@phreaker.net>
13+ eject
14+
15 Emanuele Aina <emanuele.aina@tiscali.it>
16 run-parts
17
18Index: coreutils/Config.in
19===================================================================
20RCS file: /var/cvs/busybox/coreutils/Config.in,v
21retrieving revision 1.23
22diff -u -r1.23 Config.in
23--- a/coreutils/Config.in 5 Mar 2004 06:47:25 -0000 1.23
24+++ b/coreutils/Config.in 5 Mar 2004 07:23:18 -0000
25@@ -164,6 +164,13 @@
26 a command; without options it displays the current
27 environment.
28
29+config CONFIG_EJECT
30+ bool "eject"
31+ default n
32+ help
33+ ejects a cdrom drive.
34+ defaults to /dev/cdrom
35+
36 config CONFIG_EXPR
37 bool "expr"
38 default n
39Index: coreutils/Makefile.in
40===================================================================
41RCS file: /var/cvs/busybox/coreutils/Makefile.in,v
42retrieving revision 1.8
43diff -u -r1.8 Makefile.in
44--- a/coreutils/Makefile.in 27 Jan 2004 09:22:20 -0000 1.8
45+++ b/coreutils/Makefile.in 5 Mar 2004 07:23:18 -0000
46@@ -41,6 +41,7 @@
47 COREUTILS-$(CONFIG_DU) += du.o
48 COREUTILS-$(CONFIG_ECHO) += echo.o
49 COREUTILS-$(CONFIG_ENV) += env.o
50+COREUTILS-$(CONFIG_EJECT) += eject.o
51 COREUTILS-$(CONFIG_EXPR) += expr.o
52 COREUTILS-$(CONFIG_FALSE) += false.o
53 COREUTILS-$(CONFIG_FOLD) += fold.o
54Index: coreutils/eject.c
55===================================================================
56RCS file: coreutils/eject.c
57diff -N coreutils/eject.c
58--- /dev/null 1 Jan 1970 00:00:00 -0000
59+++ b/coreutils/eject.c 5 Mar 2004 07:23:21 -0000
60@@ -0,0 +1,66 @@
61+/*
62+ * eject implementation for busybox
63+ *
64+ * Copyright (C) 2004 Peter Willis <psyphreak@phreaker.net>
65+ *
66+ * This program is free software; you can redistribute it and/or modify
67+ * it under the terms of the GNU General Public License as published by
68+ * the Free Software Foundation; either version 2 of the License, or
69+ * (at your option) any later version.
70+ *
71+ * This program is distributed in the hope that it will be useful,
72+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
73+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
74+ * General Public License for more details.
75+ *
76+ * You should have received a copy of the GNU General Public License
77+ * along with this program; if not, write to the Free Software
78+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
79+ *
80+ */
81+
82+/*
83+ * This is a simple hack of eject based on something Erik posted in #uclibc.
84+ * Most of the dirty work blatantly ripped off from cat.c =)
85+ */
86+
87+#include <stdio.h>
88+#include <string.h>
89+#include <sys/types.h>
90+#include <sys/stat.h>
91+#include <fcntl.h>
92+#include <sys/ioctl.h>
93+#include "busybox.h"
94+#include <linux/cdrom.h> // needs to be after busybox.h or compile problems arise
95+
96+#define DEFAULT_CDROM "/dev/cdrom"
97+
98+extern int eject_main(int argc, char **argv)
99+{
100+ int fd;
101+ int flag = CDROMEJECT;
102+ int i = 1;
103+ char *device = NULL;
104+
105+ /*
106+ * i'm too lazy to learn bb_getopt_ulflags and this is obscenely large
107+ * for just some argument parsing so mjn3 can clean it up later.
108+ * sorry, but PlumpOS 7.0-pre2 needs this asap :-/
109+ */
110+ while (++i <= argc) {
111+ if ( (! strncmp(argv[i-1],"-t",2)) || (! strncmp(argv[i-1],"--trayclose",11)) ) {
112+ flag = CDROMCLOSETRAY;
113+ } else {
114+ device = argv[i-1];
115+ }
116+ }
117+ if ( (fd = open(device == NULL ? DEFAULT_CDROM : device, O_RDONLY | O_NONBLOCK) ) < 0 ) {
118+ perror("eject: Can't open device");
119+ return(EXIT_FAILURE);
120+ }
121+ if (ioctl(fd, flag)) {
122+ perror("eject: Can't eject cdrom");
123+ return(EXIT_FAILURE);
124+ }
125+ return EXIT_SUCCESS;
126+}
127Index: include/applets.h
128===================================================================
129RCS file: /var/cvs/busybox/include/applets.h,v
130retrieving revision 1.111
131diff -u -r1.111 applets.h
132--- a/include/applets.h 27 Jan 2004 09:22:20 -0000 1.111
133+++ b/include/applets.h 5 Mar 2004 07:23:21 -0000
134@@ -178,6 +178,9 @@
135 #if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS)
136 APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
137 #endif
138+#ifdef CONFIG_EJECT
139+ APPLET(eject, eject_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
140+#endif
141 #ifdef CONFIG_ENV
142 APPLET(env, env_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
143 #endif
144Index: include/usage.h
145===================================================================
146RCS file: /var/cvs/busybox/include/usage.h,v
147retrieving revision 1.191
148diff -u -r1.191 usage.h
149--- a/include/usage.h 25 Feb 2004 10:35:55 -0000 1.191
150+++ b/include/usage.h 5 Mar 2004 07:23:29 -0000
151@@ -537,6 +537,13 @@
152 "\t-, -i\tstart with an empty environment\n" \
153 "\t-u\tremove variable from the environment\n"
154
155+#define eject_trivial_usage \
156+ "[-t] [FILE]"
157+#define eject_full_usage \
158+ "Ejects the specified FILE or /dev/cdrom if FILE is unspecified.\n\n" \
159+ "Options:\n" \
160+ "\t-t, --trayclose \tclose tray\n"
161+
162 #define expr_trivial_usage \
163 "EXPRESSION"
164 #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 @@
1Index: include/usage.h
2===================================================================
3RCS file: /var/cvs/busybox/include/usage.h,v
4retrieving revision 1.211
5diff -u -r1.211 usage.h
6--- a/include/usage.h 26 May 2004 22:09:37 -0000 1.211
7+++ b/include/usage.h 5 Jun 2004 07:51:26 -0000
8@@ -1536,6 +1536,7 @@
9 #define lsmod_full_usage \
10 "List the currently loaded kernel modules."
11
12+#ifdef CONFIG_FEATURE_MAKEDEVS_LEAF
13 #define makedevs_trivial_usage \
14 "NAME TYPE MAJOR MINOR FIRST LAST [s]"
15 #define makedevs_full_usage \
16@@ -1555,6 +1556,18 @@
17 "[creates ttyS2-ttyS63]\n" \
18 "# makedevs /dev/hda b 3 0 0 8 s\n" \
19 "[creates hda,hda1-hda8]\n"
20+#endif
21+
22+#ifdef CONFIG_FEATURE_MAKEDEVS_TABLE
23+#define makedevs_trivial_usage \
24+ "[-r rootdir] [device_table]"
25+#define makedevs_full_usage \
26+ "Creates a batch of special files as specified in a device table\n" \
27+ "The device table has one line per device group, each group is of\n" \
28+ "the format\n" \
29+ "\ttype mode user group major minor start increment count\n" \
30+ "a '-' may be used for blank entries\n"
31+#endif
32
33 #ifdef CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
34 #define USAGE_MD5_SHA1_SUM_CHECK(a) a
35Index: miscutils/Config.in
36===================================================================
37RCS file: /var/cvs/busybox/miscutils/Config.in,v
38retrieving revision 1.14
39diff -u -r1.14 Config.in
40--- a/miscutils/Config.in 15 Mar 2004 08:28:46 -0000 1.14
41+++ b/miscutils/Config.in 5 Jun 2004 07:51:26 -0000
42@@ -143,10 +143,32 @@
43 bool "makedevs"
44 default n
45 help
46- 'makedevs' is a utility used and created by the Linux Router Project.
47- It creates a large number of device special files (/dev devices)
48- rather quickly, and can be considerably faster then running mknod a
49- zillion times.
50+ 'makedevs' is a utility used to create a batch of devices with
51+ one command.
52+ .
53+ There are two choices for command line behaviour, the interface
54+ as used by LEAF/Linux Router Project, or a device table file.
55+ .
56+ 'leaf' is traditionally what busybox follows, it allows multiple
57+ devices of a particluar type to be created per command.
58+ e.g. /dev/hda[0-9]
59+ Device properties are passed as command line arguments.
60+ .
61+ 'table' reads device properties from a file or stdin, allowing
62+ a batch of unrelated devices to be makde with one command.
63+ User/group names are allowed as an alternative to uid/gid.
64+
65+choice
66+ prompt "Choose makedevs behaviour"
67+ default CONFIG_FEATURE_MAKDEVS_TABLE
68+
69+config CONFIG_FEATURE_MAKEDEVS_LEAF
70+ bool "leaf"
71+
72+config CONFIG_FEATURE_MAKEDEVS_TABLE
73+ bool "table"
74+
75+endchoice
76
77 config CONFIG_MT
78 bool "mt"
79Index: miscutils/makedevs.c
80===================================================================
81RCS file: /var/cvs/busybox/miscutils/makedevs.c,v
82retrieving revision 1.16
83diff -u -r1.16 makedevs.c
84--- a/miscutils/makedevs.c 15 Mar 2004 08:28:46 -0000 1.16
85+++ b/miscutils/makedevs.c 5 Jun 2004 07:51:26 -0000
86@@ -1,20 +1,27 @@
87 /* vi: set sw=4 ts=4: */
88-/*
89- * public domain -- Dave 'Kill a Cop' Cinege <dcinege@psychosis.com>
90- *
91- * makedevs
92- * Make ranges of device files quickly.
93- * known bugs: can't deal with alpha ranges
94- */
95
96+#include <sys/types.h>
97+
98+#include <fcntl.h>
99+#include <getopt.h>
100 #include <stdio.h>
101 #include <stdlib.h>
102 #include <string.h>
103-#include <fcntl.h>
104+#include <time.h>
105 #include <unistd.h>
106-#include <sys/types.h>
107+
108 #include "busybox.h"
109
110+#ifdef CONFIG_FEATURE_MAKEDEVS_LEAF
111+
112+/*
113+ * public domain -- Dave 'Kill a Cop' Cinege <dcinege@psychosis.com>
114+ *
115+ * makedevs
116+ * Make ranges of device files quickly.
117+ * known bugs: can't deal with alpha ranges
118+ */
119+
120 int makedevs_main(int argc, char **argv)
121 {
122 mode_t mode;
123@@ -69,24 +76,153 @@
124 return 0;
125 }
126
127+#elif defined CONFIG_FEATURE_MAKEDEVS_TABLE
128+
129 /*
130-And this is what this program replaces. The shell is too slow!
131+ * This program is free software; you can redistribute it and/or modify
132+ * it under the terms of the GNU General Public License version 2 as
133+ * published by the Free Software Foundation.
134+ *
135+ * This program is distributed in the hope that it will be useful,
136+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
137+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
138+ * GNU Library General Public License for more details.
139+ *
140+ * You should have received a copy of the GNU General Public License
141+ * along with this program; if not, write to the Free Software
142+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
143+ *
144+ */
145+
146+static const struct option makedevs_long_options[] = {
147+ {"root", 1, NULL, 'r'},
148+ {0, 0, 0, 0}
149+};
150
151-makedev () {
152-local basedev=$1; local S=$2; local E=$3
153-local major=$4; local Sminor=$5; local type=$6
154-local sbase=$7
155-
156- if [ ! "$sbase" = "" ]; then
157- mknod "$basedev" $type $major $Sminor
158- S=`expr $S + 1`
159- Sminor=`expr $Sminor + 1`
160- fi
161-
162- while [ $S -le $E ]; do
163- mknod "$basedev$S" $type $major $Sminor
164- S=`expr $S + 1`
165- Sminor=`expr $Sminor + 1`
166- done
167+extern int makedevs_main(int argc, char **argv)
168+{
169+ FILE *table;
170+ int opt;
171+ char *rootdir = "./";
172+ char *line;
173+ int ret = EXIT_SUCCESS;
174+
175+ bb_opt_complementaly = "d~r";
176+ bb_applet_long_options = makedevs_long_options;
177+ opt = bb_getopt_ulflags(argc, argv, "d:r:", &rootdir, &rootdir);
178+
179+ if (optind + 1 == argc) {
180+ table = bb_xfopen(argv[optind], "r");
181+ } else {
182+ table = stdin;
183+ }
184+
185+ if (chdir(rootdir) == -1) {
186+ bb_perror_msg_and_die("Couldnt chdor to %s", rootdir);
187+ }
188+
189+ umask(0);
190+
191+ while ((line = bb_get_chomped_line_from_file(table))) {
192+ char type;
193+ unsigned int mode = 0755;
194+ unsigned int major = 0;
195+ unsigned int minor = 0;
196+ unsigned int count = 0;
197+ unsigned int increment = 0;
198+ unsigned int start = 0;
199+ char name[41];
200+ char user[41];
201+ char group[41];
202+ char *full_name;
203+ uid_t uid;
204+ gid_t gid;
205+
206+ if ((2 > sscanf(line, "%40s %c %o %40s %40s %u %u %u %u %u", name,
207+ &type, &mode, user, group, &major,
208+ &minor, &start, &increment, &count)) ||
209+ ((major | minor | start | count | increment) > 255)) {
210+ bb_error_msg("Ignoring invalid line\n%s\n", line);
211+ ret = EXIT_FAILURE;
212+ continue;
213+ }
214+ if (name[0] == '#') {
215+ continue;
216+ }
217+ if (group) {
218+ gid = get_ug_id(group, my_getgrnam);
219+ } else {
220+ gid = getgid();
221+ }
222+ if (user) {
223+ uid = get_ug_id(user, my_getpwnam);
224+ } else {
225+ uid = getuid();
226+ }
227+ full_name = concat_path_file(rootdir, name);
228+
229+ if (type == 'd') {
230+ bb_make_directory(full_name, mode | S_IFDIR, 0);
231+ if (chown(full_name, uid, gid) == -1) {
232+ bb_perror_msg("chown failed for %s", full_name);
233+ ret = EXIT_FAILURE;
234+ goto loop;
235+ }
236+ } else {
237+ dev_t rdev;
238+
239+ if (type == 'p') {
240+ mode |= S_IFIFO;
241+ }
242+ else if (type == 'c') {
243+ mode |= S_IFCHR;
244+ }
245+ else if (type == 'b') {
246+ mode |= S_IFBLK;
247+ } else {
248+ bb_error_msg("Unsupported file type %c", type);
249+ ret = EXIT_FAILURE;
250+ goto loop;
251+ }
252+
253+ if (count > 0) {
254+ int i;
255+ char *full_name_inc;
256+
257+ full_name_inc = xmalloc(strlen(full_name) + 4);
258+ for (i = start; i < count; i++) {
259+ sprintf(full_name_inc, "%s%d", full_name, i);
260+ rdev = (major << 8) + minor + (i * increment - start);
261+ if (mknod(full_name_inc, mode, rdev) == -1) {
262+ bb_perror_msg("Couldnt create node %s", full_name_inc);
263+ ret = EXIT_FAILURE;
264+ }
265+ else if (chown(full_name_inc, uid, gid) == -1) {
266+ bb_perror_msg("chown failed for %s", full_name_inc);
267+ ret = EXIT_FAILURE;
268+ }
269+ }
270+ free(full_name_inc);
271+ } else {
272+ rdev = (major << 8) + minor;
273+ if (mknod(full_name, mode, rdev) == -1) {
274+ bb_perror_msg("Couldnt create node %s", full_name);
275+ ret = EXIT_FAILURE;
276+ }
277+ else if (chown(full_name, uid, gid) == -1) {
278+ bb_perror_msg("chown failed for %s", full_name);
279+ ret = EXIT_FAILURE;
280+ }
281+ }
282+ }
283+loop:
284+ free(line);
285+ free(full_name);
286+ }
287+ fclose(table);
288+
289+ return 0;
290 }
291-*/
292+#else
293+# error makdedevs configuration error, either leaf or table must be selected
294+#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 @@
1diff -ur busybox/archival/Config.in busybox/archival/Config.in
2--- busybox/archival/Config.in Sun May 23 09:15:37 2004
3+++ busybox/archival/Config.in Sun May 23 09:15:58 2004
4@@ -127,6 +127,14 @@
5 help
6 Converts an RPM file into a CPIO archive.
7
8+config CONFIG_FEATURE_RPM2CPIO_BZIP2
9+ bool " Support bzip2 decompression"
10+ default n
11+ depends on CONFIG_RPM2CPIO
12+ help
13+ If you enable this option you'll be able to extract
14+ rpms compressed with bzip2.
15+
16 config CONFIG_RPM
17 bool "rpm"
18 default n
19diff -ur busybox/archival/libunarchive/Makefile.in busybox/archival/libunarchive/Makefile.in
20--- busybox/archival/libunarchive/Makefile.in Sun May 23 09:15:04 2004
21+++ busybox/archival/libunarchive/Makefile.in Sun May 23 09:16:42 2004
22@@ -65,6 +65,7 @@
23 LIBUNARCHIVE-$(CONFIG_GUNZIP) += $(GUNZIP_FILES)
24 LIBUNARCHIVE-$(CONFIG_FEATURE_GUNZIP_UNCOMPRESS) += decompress_uncompress.o
25 LIBUNARCHIVE-$(CONFIG_RPM2CPIO) += $(GUNZIP_FILES) get_header_cpio.o
26+LIBUNARCHIVE-$(CONFIG_FEATURE_RPM2CPIO_BZIP2) += decompress_bunzip2.o
27 LIBUNARCHIVE-$(CONFIG_RPM) += $(GUNZIP_FILES) get_header_cpio.o
28 LIBUNARCHIVE-$(CONFIG_TAR) += get_header_tar.o
29 LIBUNARCHIVE-$(CONFIG_FEATURE_TAR_BZIP2) += decompress_bunzip2.o get_header_tar_bz2.o
30diff -ur busybox/archival/rpm2cpio.c busybox/archival/rpm2cpio.c
31--- busybox/archival/rpm2cpio.c Sun May 23 09:15:04 2004
32+++ busybox/archival/rpm2cpio.c Sun May 23 09:19:03 2004
33@@ -91,14 +91,26 @@
34 skip_header(rpm_fd);
35
36 bb_xread_all(rpm_fd, &magic, 2);
37- if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
38- bb_error_msg_and_die("Invalid gzip magic");
39+ if ((magic[0] == 0x1f) || (magic[1] == 0x8b)) {
40+ check_header_gzip(rpm_fd);
41+ if (inflate_gunzip(rpm_fd, fileno(stdout)) != 0)
42+ bb_error_msg("Error inflating (gzip)");
43 }
44
45- check_header_gzip(rpm_fd);
46- if (inflate_gunzip(rpm_fd, STDOUT_FILENO) != 0) {
47- bb_error_msg("Error inflating");
48+ if ((magic[0] == 'B') && (magic[1] == 'Z')) {
49+#ifdef CONFIG_FEATURE_RPM2CPIO_BZIP2
50+ /* return to position before magic (eek..!) */
51+ lseek(rpm_fd, -2, SEEK_CUR);
52+ if(uncompressStream(rpm_fd, fileno(stdout)) != 0)
53+ bb_error_msg("Error inflating (bzip2)");
54+#else
55+ bb_error_msg_and_die("bzip2 not supported");
56+#endif
57 }
58+
59+ else
60+ bb_error_msg_and_die("not gzip or bzip2 compressed");
61+
62
63 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 @@
1Index: AUTHORS
2===================================================================
3RCS file: /var/cvs/busybox/AUTHORS,v
4retrieving revision 1.40
5diff -u -r1.40 AUTHORS
6--- a/AUTHORS 9 Oct 2003 21:19:21 -0000 1.40
7+++ b/AUTHORS 5 Mar 2004 15:45:47 -0000
8@@ -92,6 +92,9 @@
9 Original author of BusyBox in 1995, 1996. Some of his code can
10 still be found hiding here and there...
11
12+John Powers <jpp@ti.com>
13+ Added multicast option (rfc2090) and timeout option (rfc2349) to tftp.
14+
15 Tim Riker <Tim@Rikers.org>
16 bug fixes, member of fan club
17
18Index: include/usage.h
19===================================================================
20RCS file: /var/cvs/busybox/include/usage.h,v
21retrieving revision 1.191
22diff -u -r1.191 usage.h
23--- a/include/usage.h 25 Feb 2004 10:35:55 -0000 1.191
24+++ b/include/usage.h 5 Mar 2004 15:45:59 -0000
25@@ -2492,6 +2492,21 @@
26 #else
27 #define USAGE_TFTP_BS(a)
28 #endif
29+#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
30+ #define USAGE_TFTP_TIMEOUT(a) a
31+#else
32+ #define USAGE_TFTP_TIMEOUT(a)
33+#endif
34+#ifdef CONFIG_FEATURE_TFTP_MULTICAST
35+ #define USAGE_TFTP_MULTICAST(a) a
36+#else
37+ #define USAGE_TFTP_MULTICAST(a)
38+#endif
39+#ifdef CONFIG_FEATURE_TFTP_DEBUG
40+ #define USAGE_TFTP_DEBUG(a) a
41+#else
42+ #define USAGE_TFTP_DEBUG(a)
43+#endif
44
45 #define tftp_trivial_usage \
46 "[OPTION]... HOST [PORT]"
47@@ -2508,6 +2523,16 @@
48 ) \
49 USAGE_TFTP_BS( \
50 "\t-b SIZE\tTransfer blocks of SIZE octets.\n" \
51+ ) \
52+ USAGE_TFTP_TIMEOUT( \
53+ "\t-T SEC\tClient timeout SEC seconds (default: 5).\n" \
54+ "\t-t SEC\tServer timeout SEC seconds\n" \
55+ ) \
56+ USAGE_TFTP_MULTICAST( \
57+ "\t-m\tMulticast get file.\n" \
58+ ) \
59+ USAGE_TFTP_DEBUG( \
60+ "\t-D\tPrint debug messages.\n" \
61 )
62 #define time_trivial_usage \
63 "[OPTION]... COMMAND [ARGS...]"
64Index: networking/Config.in
65===================================================================
66RCS file: /var/cvs/busybox/networking/Config.in,v
67retrieving revision 1.27
68diff -u -r1.27 Config.in
69--- a/networking/Config.in 22 Feb 2004 12:25:47 -0000 1.27
70+++ b/networking/Config.in 5 Mar 2004 15:45:59 -0000
71@@ -522,6 +522,13 @@
72 Add support for the GET command within the TFTP client. This allows
73 a client to retrieve a file from a TFTP server.
74
75+config CONFIG_FEATURE_TFTP_MULTICAST
76+ bool " Enable \"multicast\" option"
77+ default n
78+ depends on CONFIG_FEATURE_TFTP_GET
79+ help
80+ Allow the client to receive multicast file transfers.
81+
82 config CONFIG_FEATURE_TFTP_PUT
83 bool " Enable \"put\" command"
84 default y
85@@ -531,12 +538,19 @@
86 a client to transfer a file to a TFTP server.
87
88 config CONFIG_FEATURE_TFTP_BLOCKSIZE
89- bool " Enable \"blocksize\" command"
90+ bool " Enable \"blksize\" option"
91 default n
92 depends on CONFIG_TFTP
93 help
94 Allow the client to specify the desired block size for transfers.
95
96+config CONFIG_FEATURE_TFTP_TIMEOUT
97+ bool " Enable \"timeout\" option"
98+ default n
99+ depends on CONFIG_TFTP
100+ help
101+ Allow the client to negotiate timeout option with server.
102+
103 config CONFIG_FEATURE_TFTP_DEBUG
104 bool " Enable debug"
105 default n
106Index: networking/tftp.c
107===================================================================
108RCS file: /var/cvs/busybox/networking/tftp.c,v
109retrieving revision 1.25
110diff -u -r1.25 tftp.c
111--- a/networking/tftp.c 5 Mar 2004 13:04:39 -0000 1.25
112+++ b/networking/tftp.c 5 Mar 2004 15:46:00 -0000
113@@ -1,11 +1,26 @@
114+/* vi: set sw=4 ts=4: */
115 /* ------------------------------------------------------------------------- */
116 /* tftp.c */
117+/* Copyright (c) 2003, 2004 Texas Instruments */
118+/* */
119+/* This package is free software; you can redistribute it and/or */
120+/* modify it under the terms of the license found in the file */
121+/* named COPYING that should have accompanied this file. */
122+/* */
123+/* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */
124+/* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */
125+/* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */
126 /* */
127 /* A simple tftp client for busybox. */
128 /* Tries to follow RFC1350. */
129 /* Only "octet" mode supported. */
130 /* Optional blocksize negotiation (RFC2347 + RFC2348) */
131 /* */
132+/* New features added at Texas Instruments, October 2003 */
133+/* Author: John Powers */
134+/* Multicast option: rfc2090 */
135+/* Timeout option: rfc2349 */
136+/* */
137 /* Copyright (C) 2001 Magnus Damm <damm@opensource.se> */
138 /* */
139 /* Parts of the code based on: */
140@@ -46,8 +61,20 @@
141
142 #include "busybox.h"
143
144+#if defined(CONFIG_FEATURE_TFTP_BLOCKSIZE) || defined(CONFIG_FEATURE_TFTP_MULTICAST) || defined(CONFIG_FEATURE_TFTP_TIMEOUT)
145+ #define TFTP_OPTIONS
146+#endif
147+
148 //#define CONFIG_FEATURE_TFTP_DEBUG
149
150+#ifdef CONFIG_FEATURE_TFTP_DEBUG
151+ static void printtime(void);
152+ #define dprintf(fmt...) if (debug) {printtime(); printf(fmt);}
153+ int debug = 0;
154+#else
155+ #define dprintf(fmt...)
156+#endif
157+
158 #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
159 #define TFTP_TIMEOUT 5 /* seconds */
160
161@@ -68,12 +95,24 @@
162 "Illegal TFTP operation",
163 "Unknown transfer ID",
164 "File already exists",
165- "No such user"
166+ "No such user",
167+#ifdef TFTP_OPTIONS
168+ "Unsupported option",
169+#endif
170 };
171
172 const int tftp_cmd_get = 1;
173 const int tftp_cmd_put = 2;
174
175+
176+struct tftp_option {
177+ int multicast;
178+ int blksize;
179+ int client_timeout;
180+ int server_timeout;
181+};
182+
183+
184 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
185
186 static int tftp_blocksize_check(int blocksize, int bufsize)
187@@ -93,16 +132,158 @@
188 return blocksize;
189 }
190
191+#endif
192+
193+#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
194+
195+static int
196+tftp_timeout_check(int timeout)
197+{
198+ /* Check if timeout seconds is valid:
199+ * RFC2349 says between 1 and 255.
200+ */
201+
202+ if (timeout < 1 || timeout > 255) {
203+ bb_error_msg("bad timeout value");
204+ return 0;
205+ }
206+ return timeout;
207+}
208+
209+#endif
210+
211+#ifdef CONFIG_FEATURE_TFTP_MULTICAST
212+static int
213+tftp_multicast_check(const char *opt, char **phost, unsigned short *pport, int *pactive)
214+{
215+ /* Option string contains comma delimited addr,port,active.
216+ * addr = multicast IP address
217+ * port = port number
218+ * active = 1 if active client
219+ * 0 if passive client
220+ *
221+ * Addr and port will be empty fields when the server notifies a
222+ * passive client that it is now the active client.
223+ *
224+ * The host address string must be freed by the caller. Neither host
225+ * nor port will be set/changed if the input fields are empty.
226+ *
227+ * If any tokenization errors occur in the opt string, the host
228+ * address string is automatically freed.
229+ *
230+ * Return 0 if any tokenization error, 1 if all parameters are good.
231+ */
232+
233+ char *token = NULL;
234+ char *parse_buf = NULL;
235+ char *tokenv = NULL;
236+ char *host = NULL;
237+ int port;
238+ int active;
239+
240+ parse_buf = bb_xstrdup(opt);
241+
242+ dprintf("multicast option=%s\n", opt);
243+
244+ /* IP address */
245+ if ((token = strtok_r(parse_buf, ",", &tokenv)) == NULL) {
246+ dprintf("tftp_multicast_check: cannot parse IP address from %s\n", parse_buf);
247+ free(parse_buf);
248+ return 0;
249+ }
250+ if (strlen(token) > 0)
251+ *phost = host = bb_xstrdup(token);
252+
253+ /* Port */
254+ if ((token = strtok_r(NULL, ",", &tokenv)) == NULL) {
255+ dprintf("tftp_multicast_check: cannot parse port number from %s\n", tokenv);
256+ goto token_error;
257+ }
258+ if (strlen(token) > 0) {
259+ port = atoi(token);
260+ if (port < 0 || port > 0xFFFF) {
261+ dprintf("tftp_multicast_check: bad port number (%d)\n", port);
262+ goto token_error;
263+ }
264+ *pport = htons(port);
265+ }
266+
267+ /* Active/passive */
268+ if ((token = strtok_r(NULL, ",", &tokenv)) == NULL) {
269+ dprintf("tftp_multicast_check: cannot parse active/passive from %s\n", tokenv);
270+ goto token_error;
271+ }
272+ active = atoi(token);
273+ if (active != 0 && active != 1) {
274+ dprintf("tftp_multicast_check: bad active/passive flag (%d)\n", active);
275+ goto token_error;
276+ }
277+ *pactive = active;
278+
279+ free(parse_buf);
280+ return 1;
281+
282+token_error:
283+ free(parse_buf);
284+ if (host != NULL)
285+ free(host);
286+ *phost = NULL;
287+ return 0;
288+
289+}
290+
291+#define VECTOR_QUANTUM_WIDTH 8
292+#define VECTOR_QUANTUM_ALL_ONES ((1<<VECTOR_QUANTUM_WIDTH)-1)
293+
294+static void inline
295+bit_set(int bit, unsigned char *vector)
296+{
297+ int offset = bit / VECTOR_QUANTUM_WIDTH;
298+ int mask = 1 << (bit % VECTOR_QUANTUM_WIDTH);
299+ vector[offset] |= mask;
300+}
301+
302+static int inline
303+bit_isset(int bit, const unsigned char *vector)
304+{
305+ int offset = bit / VECTOR_QUANTUM_WIDTH;
306+ int mask = 1 << (bit % VECTOR_QUANTUM_WIDTH);
307+ return vector[offset] & mask ? 1 : 0;
308+}
309+
310+static int inline
311+bit_lmz(const unsigned char *vector)
312+{
313+ /* Return number of left-most zero in bit vector */
314+ const unsigned char *vp = vector;
315+ int i;
316+ unsigned char velem;
317+
318+ while (*vp == VECTOR_QUANTUM_ALL_ONES)
319+ vp++;
320+ velem = *vp;
321+ for (i = 0; i < VECTOR_QUANTUM_WIDTH; i++) {
322+ if ((velem & (1 << i)) == 0)
323+ break;
324+ }
325+ dprintf("bit_lmz: block=%d\n", (vp - vector)*VECTOR_QUANTUM_WIDTH + i);
326+ return (vp - vector)*VECTOR_QUANTUM_WIDTH + i;
327+}
328+
329+#endif
330+
331+
332+
333+#ifdef TFTP_OPTIONS
334+
335 static char *tftp_option_get(char *buf, int len, char *option)
336 {
337- int opt_val = 0;
338+ int opt_val = 0;
339 int opt_found = 0;
340 int k;
341-
342- while (len > 0) {
343
344+ while (len > 0) {
345 /* Make sure the options are terminated correctly */
346-
347 for (k = 0; k < len; k++) {
348 if (buf[k] == '\0') {
349 break;
350@@ -117,9 +298,8 @@
351 if (strcasecmp(buf, option) == 0) {
352 opt_found = 1;
353 }
354- }
355- else {
356- if (opt_found) {
357+ } else {
358+ if (opt_found) {
359 return buf;
360 }
361 }
362@@ -138,7 +318,8 @@
363 #endif
364
365 static inline int tftp(const int cmd, const struct hostent *host,
366- const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize)
367+ const char *remotefile, int localfd, const unsigned short port,
368+ struct tftp_option *option)
369 {
370 const int cmd_get = cmd & tftp_cmd_get;
371 const int cmd_put = cmd & tftp_cmd_put;
372@@ -155,18 +336,29 @@
373 int len;
374 int opcode = 0;
375 int finished = 0;
376- int timeout = bb_tftp_num_retries;
377+ int retry = bb_tftp_num_retries;
378 unsigned short block_nr = 1;
379
380-#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
381- int want_option_ack = 0;
382+#ifdef CONFIG_FEATURE_TFTP_MULTICAST
383+ struct hostent *mchost;
384+ struct sockaddr_in mcsa;
385+ char *mchostname;
386+ unsigned short mcport;
387+ unsigned char *mcblockmap = NULL;
388+ int master_client = 1;
389+ int mcfd = -1;
390+ int mcmaxblock = 0x10000;
391+ int ack_oack = 0;
392+#else
393+ #define master_client 1
394+ #define ack_oack 0
395 #endif
396
397 /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
398 * size varies meaning BUFFERS_GO_ON_STACK would fail */
399- char *buf=xmalloc(tftp_bufsize + 4);
400+ char *buf=xmalloc(option->blksize + 4);
401
402- tftp_bufsize += 4;
403+ int tftp_bufsize = option->blksize + 4;
404
405 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
406 bb_perror_msg("socket");
407@@ -183,15 +375,21 @@
408 memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
409 sizeof(sa.sin_addr));
410
411- /* build opcode */
412-
413- if (cmd_get) {
414- opcode = TFTP_RRQ;
415+#ifdef CONFIG_FEATURE_TFTP_MULTICAST
416+ if (option->multicast) {
417+ const int bmsize = 0x10000 / VECTOR_QUANTUM_WIDTH;
418+ if ((mcfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
419+ bb_perror_msg("multicast socket");
420+ return EXIT_FAILURE;
421+ }
422+ mcblockmap = xmalloc(bmsize+1);
423+ memset(mcblockmap, 0, bmsize+1);
424 }
425+#endif
426
427- if (cmd_put) {
428- opcode = TFTP_WRQ;
429- }
430+ /* build opcode */
431+
432+ opcode = cmd_get ? TFTP_RRQ : TFTP_WRQ;
433
434 while (1) {
435
436@@ -203,7 +401,7 @@
437
438 cp += 2;
439
440- /* add filename and mode */
441+ /* First packet of file transfer includes file name, mode, and options */
442
443 if ((cmd_get && (opcode == TFTP_RRQ)) ||
444 (cmd_put && (opcode == TFTP_WRQ))) {
445@@ -223,7 +421,7 @@
446 }
447
448 if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
449- bb_error_msg("too long remote-filename");
450+ bb_error_msg("too long: remote filename");
451 break;
452 }
453
454@@ -238,8 +436,8 @@
455
456 if (len != TFTP_BLOCKSIZE_DEFAULT) {
457
458- if ((&buf[tftp_bufsize - 1] - cp) < 15) {
459- bb_error_msg("too long remote-filename");
460+ if ((&buf[tftp_bufsize - 1] - cp) < 15) {
461+ bb_error_msg("buffer too small for blksize option");
462 break;
463 }
464
465@@ -249,16 +447,65 @@
466 cp += 8;
467
468 cp += snprintf(cp, 6, "%d", len) + 1;
469+ }
470+#endif
471+
472+
473+
474+#ifdef CONFIG_FEATURE_TFTP_MULTICAST
475+
476+ if (option->multicast) {
477+ if ((&buf[tftp_bufsize - 1] - cp) < 12) {
478+ bb_error_msg("buffer too small for multicast option");
479+ break;
480+ }
481+
482+ /* add "multicast" option */
483
484- want_option_ack = 1;
485+ memcpy(cp, "multicast\0", 11);
486+ cp += 11;
487+
488+ option->multicast = 0; /* turn back on when server accepts option */
489+ ack_oack = 1; /* acknowledge OACK */
490 }
491+
492 #endif
493+
494+#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
495+
496+ if (option->server_timeout != TFTP_TIMEOUT) {
497+ if ((&buf[tftp_bufsize - 1] - cp) < 12) {
498+ bb_error_msg("buffer too small for timeout option");
499+ break;
500+ }
501+
502+ /* add "timeout" option */
503+
504+ memcpy(cp, "timeout", 8);
505+ cp += 8;
506+
507+ cp += snprintf(cp, 4, "%d", option->server_timeout) + 1;
508+ }
509+#endif
510+
511 }
512
513 /* add ack and data */
514
515- if ((cmd_get && (opcode == TFTP_ACK)) ||
516- (cmd_put && (opcode == TFTP_DATA))) {
517+#ifdef CONFIG_FEATURE_TFTP_MULTICAST
518+ else if (option->multicast && opcode == TFTP_ACK) {
519+ if (master_client || ack_oack) {
520+ int blocknum = bit_lmz(mcblockmap);
521+ *((unsigned short *) cp) = htons(blocknum);
522+ cp += 2;
523+ if (blocknum >= mcmaxblock)
524+ finished = 1;
525+ dprintf("ack block %d/%d %s\n", blocknum, mcmaxblock, finished? "finished": "");
526+ }
527+ }
528+#endif
529+ else if ((cmd_get && opcode == TFTP_ACK) ||
530+ (cmd_put && opcode == TFTP_DATA)) {
531
532 *((unsigned short *) cp) = htons(block_nr);
533
534@@ -275,7 +522,7 @@
535 }
536
537 if (len != (tftp_bufsize - 4)) {
538- finished++;
539+ finished = 1;
540 }
541
542 cp += len;
543@@ -283,82 +530,119 @@
544 }
545
546
547- /* send packet */
548+ /* send packet and receive reply */
549
550
551- timeout = bb_tftp_num_retries; /* re-initialize */
552+ retry = bb_tftp_num_retries; /* re-initialize */
553 do {
554-
555+ int selectrc;
556 len = cp - buf;
557
558-#ifdef CONFIG_FEATURE_TFTP_DEBUG
559- fprintf(stderr, "sending %u bytes\n", len);
560- for (cp = buf; cp < &buf[len]; cp++)
561- fprintf(stderr, "%02x ", (unsigned char)*cp);
562- fprintf(stderr, "\n");
563-#endif
564- if (sendto(socketfd, buf, len, 0,
565- (struct sockaddr *) &sa, sizeof(sa)) < 0) {
566- bb_perror_msg("send");
567- len = -1;
568- break;
569- }
570-
571+ /* send packet */
572+ if ((len > 2) && (! option->multicast || master_client || ack_oack)) {
573
574- if (finished && (opcode == TFTP_ACK)) {
575- break;
576+#ifdef CONFIG_FEATURE_TFTP_DEBUG
577+ dprintf("sending %u bytes\n", len);
578+ for (cp = buf; cp < &buf[len]; cp++)
579+ if (debug)
580+ printf("%02x ", *(unsigned char *)cp);
581+ if (debug)
582+ printf("\n");
583+#endif
584+#ifdef CONFIG_FEATURE_TFTP_MULTICAST
585+ ack_oack = 0;
586+#endif
587+ if (sendto(socketfd, buf, len, 0, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
588+ bb_perror_msg("send");
589+ len = -1;
590+ break;
591+ }
592+ if (finished && opcode == TFTP_ACK) {
593+ break;
594+ }
595 }
596
597- /* receive packet */
598+ /* receive reply packet */
599
600 memset(&from, 0, sizeof(from));
601 fromlen = sizeof(from);
602
603- tv.tv_sec = TFTP_TIMEOUT;
604+ tv.tv_sec = option->client_timeout;
605 tv.tv_usec = 0;
606
607 FD_ZERO(&rfds);
608 FD_SET(socketfd, &rfds);
609+ dprintf("set to receive from socketfd (%d)\n", socketfd);
610+#ifdef CONFIG_FEATURE_TFTP_MULTICAST
611+ if (option->multicast) {
612+ FD_SET(mcfd, &rfds);
613+ dprintf("set to receive from mcfd (%d)\n", mcfd);
614+ }
615+#endif
616
617- switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
618- case 1:
619- len = recvfrom(socketfd, buf, tftp_bufsize, 0,
620- (struct sockaddr *) &from, &fromlen);
621-
622- if (len < 0) {
623- bb_perror_msg("recvfrom");
624- break;
625+ dprintf("select\n");
626+ selectrc = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
627+ if (selectrc > 0) {
628+ /* A packet was received */
629+ if (FD_ISSET(socketfd, &rfds)) { /* Unicast packet */
630+ dprintf("from socketfd\n");
631+ len = recvfrom(socketfd, buf, tftp_bufsize, 0, (struct sockaddr *) &from, &fromlen);
632+
633+ if (len < 0) {
634+ bb_perror_msg("recvfrom");
635+ } else {
636+ if (sa.sin_port == port) {
637+ sa.sin_port = from.sin_port;
638+ }
639+ if (sa.sin_port == from.sin_port) {
640+ retry = 0;
641+ } else {
642+ /* bad packet */
643+ /* discard the packet - treat as timeout */
644+ retry = bb_tftp_num_retries;
645+ bb_error_msg("timeout");
646+ }
647+ }
648 }
649
650- timeout = 0;
651-
652- if (sa.sin_port == port) {
653- sa.sin_port = from.sin_port;
654+#ifdef CONFIG_FEATURE_TFTP_MULTICAST
655+ else if (option->multicast && FD_ISSET(mcfd, &rfds)) { /* Multicast packet */
656+ dprintf("from mcfd\n");
657+ len = recvfrom(mcfd, buf, tftp_bufsize, 0, (struct sockaddr *) &from, &fromlen);
658+ if (len < 0) {
659+ bb_perror_msg("multicast recvfrom");
660+ } else {
661+ if (mcsa.sin_port == mcport) {
662+ mcsa.sin_port = from.sin_port;
663+ }
664+ if (mcsa.sin_port == from.sin_port) {
665+ retry = 0;
666+ } else {
667+ retry = bb_tftp_num_retries;
668+ bb_error_msg("multicast timeout");
669+ }
670+ }
671 }
672- if (sa.sin_port == from.sin_port) {
673- break;
674- }
675-
676- /* fall-through for bad packets! */
677- /* discard the packet - treat as timeout */
678- timeout = bb_tftp_num_retries;
679+#endif
680
681- case 0:
682+ } else if (selectrc == 0) {
683+ /* Time out */
684+ dprintf("timeout\n");
685 bb_error_msg("timeout");
686
687- timeout--;
688- if (timeout == 0) {
689+ retry--;
690+ if (retry == 0) {
691 len = -1;
692 bb_error_msg("last timeout");
693 }
694- break;
695-
696- default:
697+ } else {
698+ /* Error condition */
699+ dprintf("error\n");
700 bb_perror_msg("select");
701 len = -1;
702 }
703
704- } while (timeout && (len >= 0));
705+ } while (retry && len >= 0);
706
707 if ((finished) || (len < 0)) {
708 break;
709@@ -370,9 +654,8 @@
710 opcode = ntohs(*((unsigned short *) buf));
711 tmp = ntohs(*((unsigned short *) &buf[2]));
712
713-#ifdef CONFIG_FEATURE_TFTP_DEBUG
714- fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
715-#endif
716+ dprintf("received %d bytes: %04x %04x\n", len, opcode, tmp);
717+ dprintf("master_client=%d\n", master_client);
718
719 if (opcode == TFTP_ERROR) {
720 char *msg = NULL;
721@@ -393,55 +676,116 @@
722 break;
723 }
724
725-#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
726- if (want_option_ack) {
727+#ifdef TFTP_OPTIONS
728
729- want_option_ack = 0;
730+ if (opcode == TFTP_OACK) {
731
732- if (opcode == TFTP_OACK) {
733+ /* server seems to support options */
734
735- /* server seems to support options */
736+ char *res;
737+
738+ block_nr = 0; /* acknowledge option packet with block number 0 */
739+ opcode = cmd_put ? TFTP_DATA : TFTP_ACK;
740
741- char *res;
742
743- res = tftp_option_get(&buf[2], len-2,
744- "blksize");
745+#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
746+ res = tftp_option_get(&buf[2], len-2, "blksize");
747
748- if (res) {
749- int blksize = atoi(res);
750-
751- if (tftp_blocksize_check(blksize,
752- tftp_bufsize - 4)) {
753+ if (res) {
754+ int blksize = atoi(res);
755
756- if (cmd_put) {
757- opcode = TFTP_DATA;
758- }
759- else {
760- opcode = TFTP_ACK;
761- }
762-#ifdef CONFIG_FEATURE_TFTP_DEBUG
763- fprintf(stderr, "using blksize %u\n", blksize);
764+ if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
765+ dprintf("using blksize %d\n", blksize);
766+ tftp_bufsize = blksize + 4;
767+ free(buf);
768+ buf = xmalloc(tftp_bufsize);
769+ } else {
770+ bb_error_msg("bad blksize %d", blksize);
771+ break;
772+ }
773+ }
774 #endif
775- tftp_bufsize = blksize + 4;
776- block_nr = 0;
777- continue;
778- }
779- }
780- /* FIXME:
781- * we should send ERROR 8 */
782- bb_error_msg("bad server option");
783- break;
784- }
785
786- bb_error_msg("warning: blksize not supported by server"
787- " - reverting to 512");
788
789- tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
790+#ifdef CONFIG_FEATURE_TFTP_MULTICAST
791+ res = tftp_option_get(&buf[2], len-2, "multicast");
792+
793+ if (res) {
794+ ack_oack = 1;
795+ if (tftp_multicast_check(res, &mchostname, &mcport, &master_client)) {
796+ struct ip_mreq mreq;
797+ struct in_addr mcaddr;
798+
799+ dprintf("using multicast\n");
800+
801+ mchost = xgethostbyname(mchostname);
802+ if (mchost) {
803+ memcpy(&mcaddr, mchost->h_addr, mchost->h_length);
804+ if (! IN_MULTICAST(ntohl(mcaddr.s_addr))) {
805+ bb_error_msg("bad multicast address: %s", mchostname);
806+ break;
807+ }
808+ } else {
809+ bb_error_msg("bad multicast address: %s", mchostname);
810+ break;
811+ }
812+
813+ memset(&mcsa, 0, sizeof(mcsa));
814+ mcsa.sin_family = AF_INET;
815+ mcsa.sin_addr.s_addr = htonl(INADDR_ANY);
816+ mcsa.sin_port = mcport;
817+
818+ bind(mcfd, (struct sockaddr *)&mcsa, sizeof(mcsa));
819+
820+ mreq.imr_multiaddr.s_addr = mcaddr.s_addr;
821+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
822+
823+ if (setsockopt(mcfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
824+ {
825+ bb_error_msg("setsockopt");
826+ break;
827+ }
828+
829+ option->multicast = 1;
830+ } else {
831+ bb_error_msg("bad multicast option value: %s", res);
832+ break;
833+ }
834+ }
835+#endif
836+
837 }
838+ else
839 #endif
840
841 if (cmd_get && (opcode == TFTP_DATA)) {
842
843+#ifdef CONFIG_FEATURE_TFTP_MULTICAST
844+ if (option->multicast) {
845+ int bn = tmp - 1;
846+ /* Do I need this block? */
847+ if (! bit_isset(bn, mcblockmap)) {
848+ lseek(localfd, bn*(tftp_bufsize-4), SEEK_SET);
849+ len = write(localfd, &buf[4], len-4);
850+ if (len < 0) {
851+ bb_perror_msg("write");
852+ break;
853+ }
854+ bit_set(bn, mcblockmap);
855+ if (len != (tftp_bufsize-4)) {
856+ mcmaxblock = tmp;
857+ dprintf("mcmaxblock=%d, (len(%d) != tftp_bufsize-4(%d))\n", mcmaxblock, len, tftp_bufsize-4);
858+ }
859+ opcode = TFTP_ACK;
860+ }
861+ /* Do not acknowledge block if I already have a copy of the block. A situation can arise when the server
862+ * and client timeout nearly simultaneously. The server retransmits the block at the same time the client
863+ * re-requests the block. From then on out, each block is transmitted twice--not a good use of bandwidth.
864+ */
865+ }
866+ else
867+#endif
868+
869 if (tmp == block_nr) {
870
871 len = write(localfd, &buf[4], len - 4);
872@@ -452,15 +796,14 @@
873 }
874
875 if (len != (tftp_bufsize - 4)) {
876- finished++;
877+ finished = 1;
878 }
879
880 opcode = TFTP_ACK;
881- continue;
882 }
883 }
884
885- if (cmd_put && (opcode == TFTP_ACK)) {
886+ else if (cmd_put && opcode == TFTP_ACK) {
887
888 if (tmp == (unsigned short)(block_nr - 1)) {
889 if (finished) {
890@@ -468,15 +811,19 @@
891 }
892
893 opcode = TFTP_DATA;
894- continue;
895 }
896 }
897 }
898
899 #ifdef CONFIG_FEATURE_CLEAN_UP
900 close(socketfd);
901+ free(buf);
902+
903+#ifdef CONFIG_FEATURE_TFTP_MULTICAST
904+ if (mcblockmap != NULL)
905+ free(mcblockmap);
906+#endif
907
908- free(buf);
909 #endif
910
911 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
912@@ -487,13 +834,18 @@
913 struct hostent *host = NULL;
914 char *localfile = NULL;
915 char *remotefile = NULL;
916- int port;
917+ unsigned short port;
918 int cmd = 0;
919 int fd = -1;
920 int flags = 0;
921 int opt;
922 int result;
923- int blocksize = TFTP_BLOCKSIZE_DEFAULT;
924+ struct tftp_option option = {
925+ .multicast = 0,
926+ .blksize = TFTP_BLOCKSIZE_DEFAULT,
927+ .client_timeout = TFTP_TIMEOUT,
928+ .server_timeout = TFTP_TIMEOUT,
929+ };
930
931 /* figure out what to pass to getopt */
932
933@@ -515,13 +867,45 @@
934 #define PUT
935 #endif
936
937- while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) {
938+#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
939+#define TO "T:t:"
940+#else
941+#define TO
942+#endif
943+
944+#ifdef CONFIG_FEATURE_TFTP_MULTICAST
945+#define MC "m"
946+#else
947+#define MC
948+#endif
949+
950+#ifdef CONFIG_FEATURE_TFTP_DEBUG
951+#define DB "D"
952+#else
953+#define DB
954+#endif
955+
956+ while ((opt = getopt(argc, argv, BS GET PUT TO MC DB "l:r:")) != -1) {
957 switch (opt) {
958 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
959 case 'b':
960- blocksize = atoi(optarg);
961- if (!tftp_blocksize_check(blocksize, 0)) {
962- return EXIT_FAILURE;
963+ option.blksize = atoi(optarg);
964+ if (!tftp_blocksize_check(option.blksize, 0)) {
965+ return EXIT_FAILURE;
966+ }
967+ break;
968+#endif
969+#ifdef CONFIG_FEATURE_TFTP_TIMEOUT
970+ case 'T':
971+ option.client_timeout = atoi(optarg);
972+ if (!tftp_timeout_check(option.client_timeout)) {
973+ return EXIT_FAILURE;
974+ }
975+ break;
976+ case 't':
977+ option.server_timeout = atoi(optarg);
978+ if (!tftp_timeout_check(option.server_timeout)) {
979+ return EXIT_FAILURE;
980 }
981 break;
982 #endif
983@@ -537,18 +921,34 @@
984 flags = O_RDONLY;
985 break;
986 #endif
987+#ifdef CONFIG_FEATURE_TFTP_MULTICAST
988+ case 'm':
989+ option.multicast = 1; /* receive multicast file */
990+ break;
991+#endif
992+#ifdef CONFIG_FEATURE_TFTP_DEBUG
993+ case 'D':
994+ debug = 1;
995+ break;
996+#endif
997 case 'l':
998 localfile = bb_xstrdup(optarg);
999 break;
1000 case 'r':
1001 remotefile = bb_xstrdup(optarg);
1002 break;
1003+ default:
1004+ bb_show_usage();
1005 }
1006 }
1007
1008 if ((cmd == 0) || (optind == argc)) {
1009 bb_show_usage();
1010 }
1011+ if (cmd == tftp_cmd_put && option.multicast) {
1012+ fprintf(stderr, "Multicast (-m) invalid option with put (-p) command\n");
1013+ exit(EXIT_FAILURE);
1014+ }
1015 if(localfile && strcmp(localfile, "-") == 0) {
1016 fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
1017 }
1018@@ -566,14 +966,12 @@
1019 host = xgethostbyname(argv[optind]);
1020 port = bb_lookup_port(argv[optind + 1], "udp", 69);
1021
1022-#ifdef CONFIG_FEATURE_TFTP_DEBUG
1023- fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
1024+ dprintf("using server \"%s\", remotefile \"%s\", "
1025 "localfile \"%s\".\n",
1026 inet_ntoa(*((struct in_addr *) host->h_addr)),
1027 remotefile, localfile);
1028-#endif
1029
1030- result = tftp(cmd, host, remotefile, fd, port, blocksize);
1031+ result = tftp(cmd, host, remotefile, fd, port, &option);
1032
1033 #ifdef CONFIG_FEATURE_CLEAN_UP
1034 if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
1035@@ -582,3 +980,18 @@
1036 #endif
1037 return(result);
1038 }
1039+
1040+
1041+#ifdef CONFIG_FEATURE_TFTP_DEBUG
1042+
1043+#include <sys/time.h>
1044+
1045+static void
1046+printtime(void)
1047+{
1048+ struct timeval tv;
1049+ gettimeofday(&tv, NULL);
1050+ printf("%11lu.%06lu ", tv.tv_sec, tv.tv_usec);
1051+}
1052+
1053+#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 @@
1diff -purN busybox.ori/include/libbb.h busybox/include/libbb.h
2--- busybox.ori/include/libbb.h 2004-03-21 14:39:35.000000000 +0100
3+++ busybox-1.0/include/libbb.h 2004-03-21 14:45:35.000000000 +0100
4@@ -447,6 +447,7 @@ typedef struct {
5 int ppid;
6 #ifdef FEATURE_CPU_USAGE_PERCENTAGE
7 unsigned pcpu;
8+ unsigned pscpu;
9 unsigned long stime, utime;
10 #endif
11 char *cmd;
12diff -purN busybox.ori/procps/top.c busybox/procps/top.c
13--- busybox.ori/procps/top.c 2004-03-21 14:40:09.000000000 +0100
14+++ busybox-1.0/procps/top.c 2004-03-21 17:27:52.961951448 +0100
15@@ -289,6 +289,15 @@ static void do_stats(void)
16 i = 999;
17 cur->pcpu = i;
18
19+ /*
20+ * Calculate percent of system time from cpu time
21+ */
22+ if (systime != 0) {
23+ cur->pscpu = 100 * total_time / systime;
24+ } else {
25+ cur->pscpu = 0;
26+ }
27+
28 }
29
30 /*
31@@ -393,7 +402,7 @@ static void display_status(int count, in
32
33 #ifdef FEATURE_CPU_USAGE_PERCENTAGE
34 /* what info of the processes is shown */
35- printf("\n\e[7m PID USER STATUS RSS PPID %%CPU %%MEM COMMAND\e[0m\n");
36+ printf("\n\e[7m PID USER STATUS RSS PPID %%CPU %%SCPU %%MEM COMMAND\e[0m\n");
37 #else
38 printf("\n\e[7m PID USER STATUS RSS PPID %%MEM COMMAND\e[0m\n");
39 #endif
40@@ -410,9 +419,9 @@ static void display_status(int count, in
41 else
42 sprintf(rss_str_buf, "%7ld", s->rss);
43 #ifdef FEATURE_CPU_USAGE_PERCENTAGE
44- printf("%5d %-8s %s %s %5d %2d.%d %2u.%u ",
45+ printf("%5d %-8s %s %s %5d %2d.%d %2d.%d %2u.%u ",
46 s->pid, s->user, s->state, rss_str_buf, s->ppid,
47- s->pcpu/10, s->pcpu%10, pmem/10, pmem%10);
48+ s->pcpu/10, s->pcpu%10,s->pscpu/10, s->pscpu%10, pmem/10, pmem%10);
49 #else
50 printf("%5d %-8s %s %s %5d %2u.%u ",
51 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 @@
1Index: include/usage.h
2===================================================================
3RCS file: /var/cvs/busybox/include/usage.h,v
4retrieving revision 1.191
5diff -u -r1.191 usage.h
6--- a/include/usage.h 25 Feb 2004 10:35:55 -0000 1.191
7+++ b/include/usage.h 5 Mar 2004 14:32:45 -0000
8@@ -2606,6 +2606,7 @@
9 "\t-p,\t--pidfile=file\tStore process ID of daemon in file\n" \
10 "\t-q,\t--quit\tQuit after obtaining lease\n" \
11 "\t-r,\t--request=IP\tIP address to request (default: none)\n" \
12+ "\t-R,\t--require=NAME\tAdd NAME to request\n" \
13 "\t-s,\t--script=file\tRun file at dhcp events (default: /usr/share/udhcpc/default.script)\n" \
14 "\t-v,\t--version\tDisplay version"
15
16Index: networking/udhcp/README.udhcpc
17===================================================================
18RCS file: /var/cvs/busybox/networking/udhcp/README.udhcpc,v
19retrieving revision 1.3
20diff -u -r1.3 README.udhcpc
21--- a/networking/udhcp/README.udhcpc 11 Dec 2002 21:12:44 -0000 1.3
22+++ b/networking/udhcp/README.udhcpc 5 Mar 2004 14:32:46 -0000
23@@ -22,6 +22,7 @@
24 -p, --pidfile=file Store process ID of daemon in file
25 -q, --quit Quit after obtaining lease
26 -r, --request=IP IP address to request (default: none)
27+-R, --require=NAME Add NAME to request
28 -s, --script=file Run file at dhcp events (default:
29 /usr/share/udhcpc/default.script)
30 -v, --version Display version
31@@ -101,6 +102,8 @@
32
33 additional options are easily added in options.c.
34
35+By default, only a few basic items are requested. To request additional
36+items use the -R option. Example: "-R rootpath"
37
38 note on udhcpc's random seed
39 ---------------------------
40Index: networking/udhcp/dhcpc.c
41===================================================================
42RCS file: /var/cvs/busybox/networking/udhcp/dhcpc.c,v
43retrieving revision 1.16
44diff -u -r1.16 dhcpc.c
45--- a/networking/udhcp/dhcpc.c 30 Jan 2004 23:45:12 -0000 1.16
46+++ b/networking/udhcp/dhcpc.c 5 Mar 2004 14:32:46 -0000
47@@ -88,6 +88,7 @@
48 " -p, --pidfile=file Store process ID of daemon in file\n"
49 " -q, --quit Quit after obtaining lease\n"
50 " -r, --request=IP IP address to request (default: none)\n"
51+" -R, --require=NAME Add NAME to the request\n"
52 " -s, --script=file Run file at dhcp events (default:\n"
53 " " DEFAULT_SCRIPT ")\n"
54 " -v, --version Display version\n"
55@@ -203,6 +204,7 @@
56 {"pidfile", required_argument, 0, 'p'},
57 {"quit", no_argument, 0, 'q'},
58 {"request", required_argument, 0, 'r'},
59+ {"require", required_argument, 0, 'R'},
60 {"script", required_argument, 0, 's'},
61 {"version", no_argument, 0, 'v'},
62 {0, 0, 0, 0}
63@@ -211,7 +213,7 @@
64 /* get options */
65 while (1) {
66 int option_index = 0;
67- c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:s:v", arg_options, &option_index);
68+ c = getopt_long(argc, argv, "c:fbH:h:i:np:qr:R:s:v", arg_options, &option_index);
69 if (c == -1) break;
70
71 switch (c) {
72@@ -254,6 +256,11 @@
73 case 'r':
74 requested_ip = inet_addr(optarg);
75 break;
76+ case 'R':
77+ if (require_option(optarg)) {
78+ fprintf(stderr,"WARNING: %s unknown/not-supported (Ignored)\n", optarg );
79+ }
80+ break;
81 case 's':
82 client_config.script = optarg;
83 break;
84Index: networking/udhcp/options.c
85===================================================================
86RCS file: /var/cvs/busybox/networking/udhcp/options.c,v
87retrieving revision 1.7
88diff -u -r1.7 options.c
89--- a/networking/udhcp/options.c 30 Jan 2004 23:45:12 -0000 1.7
90+++ b/networking/udhcp/options.c 5 Mar 2004 14:32:46 -0000
91@@ -57,7 +57,19 @@
92 [OPTION_S32] = 4
93 };
94
95-
96+/* find and mark requested item as required */
97+int require_option(char *name)
98+{
99+ int i;
100+ for (i = 0; dhcp_options[i].code; i++) {
101+ if (strcmp(name, dhcp_options[i].name) == 0 ){
102+ dhcp_options[i].flags |= OPTION_REQ;
103+ return 0;
104+ }
105+ }
106+ return 1;
107+}
108+
109 /* get an option with bounds checking (warning, not aligned). */
110 uint8_t *get_option(struct dhcpMessage *packet, int code)
111 {
112Index: networking/udhcp/options.h
113===================================================================
114RCS file: /var/cvs/busybox/networking/udhcp/options.h,v
115retrieving revision 1.5
116diff -u -r1.5 options.h
117--- a/networking/udhcp/options.h 30 Jan 2004 23:45:12 -0000 1.5
118+++ b/networking/udhcp/options.h 5 Mar 2004 14:32:46 -0000
119@@ -30,6 +30,7 @@
120 extern struct dhcp_option dhcp_options[];
121 extern int option_lengths[];
122
123+int require_option(char *name);
124 uint8_t *get_option(struct dhcpMessage *packet, int code);
125 int end_option(uint8_t *optionptr);
126 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 @@
1Index: include/usage.h
2===================================================================
3RCS file: /var/cvs/busybox/include/usage.h,v
4retrieving revision 1.191
5diff -u -r1.191 usage.h
6--- a/include/usage.h 25 Feb 2004 10:35:55 -0000 1.191
7+++ b/include/usage.h 5 Mar 2004 13:20:11 -0000
8@@ -2606,7 +2606,8 @@
9 "\t-p,\t--pidfile=file\tStore process ID of daemon in file\n" \
10 "\t-q,\t--quit\tQuit after obtaining lease\n" \
11 "\t-r,\t--request=IP\tIP address to request (default: none)\n" \
12- "\t-s,\t--script=file\tRun file at dhcp events (default: /usr/share/udhcpc/default.script)\n" \
13+ "\t-s,\t--script=file\tRun file at dhcp events (default: " \
14+ CONFIG_UDHCPC_SCRIPT_PATH ")\n" \
15 "\t-v,\t--version\tDisplay version"
16
17 #define udhcpd_trivial_usage \
18Index: networking/udhcp/AUTHORS
19===================================================================
20RCS file: /var/cvs/busybox/networking/udhcp/AUTHORS,v
21retrieving revision 1.3
22diff -u -r1.3 AUTHORS
23--- a/networking/udhcp/AUTHORS 18 Dec 2003 22:25:38 -0000 1.3
24+++ b/networking/udhcp/AUTHORS 5 Mar 2004 13:20:11 -0000
25@@ -10,5 +10,5 @@
26 Moreton Bay (http://www.moretonbay.com/)
27 Vladimir Oleynik <dzo@simtrea.ru> Size optimizations
28
29-
30+Tony J. White <tjw@tjw.org> additional busybox build options
31
32Index: networking/udhcp/Config.in
33===================================================================
34RCS file: /var/cvs/busybox/networking/udhcp/Config.in,v
35retrieving revision 1.5
36diff -u -r1.5 Config.in
37--- a/networking/udhcp/Config.in 22 Oct 2003 09:58:38 -0000 1.5
38+++ b/networking/udhcp/Config.in 5 Mar 2004 13:20:11 -0000
39@@ -58,5 +58,62 @@
40
41 See http://udhcp.busybox.net for further details.
42
43+menu "udhcpd Configuration Options"
44+ depends on CONFIG_UDHCPD
45+
46+config CONFIG_UDHCPD_CONF_PATH
47+ string "Path to default udhcpd.conf"
48+ default "/etc/udhcpd.conf"
49+ depends on CONFIG_UDHCPD
50+ help
51+ The full path to udhcpd's default configuration file.
52+ (default is: /etc/udhcpd.conf)
53+
54+config CONFIG_UDHCPD_LEASE_PATH
55+ string "Path to default udhcpd.leases"
56+ default "/var/lib/misc/udhcpd.leases"
57+ depends on CONFIG_UDHCPD
58+ help
59+ The full path to udhcpd's default leases file.
60+ (default is: /var/lib/misc/udhcpd.leases)
61+
62+config CONFIG_UDHCPD_PID_PATH
63+ string "Path to default udhcpd PID file"
64+ default "/var/run/udhcpd.pid"
65+ depends on CONFIG_UDHCPD
66+ help
67+ The full path to udhcpd's default pid file.
68+ (default is: /var/run/udhcpd.pid)
69+
70+endmenu
71+
72+menu "udhcpc Configuration Options"
73+ depends on CONFIG_UDHCPC
74+
75+config CONFIG_UDHCPC_SCRIPT_PATH
76+ string "Path to default udhcpc event script"
77+ depends on CONFIG_UDHCPC
78+ help
79+ The full path to udhcpc's default event script file.
80+ (default is: /usr/share/udhcpc/default.script OR
81+ /share/udhcpc/default.script if CONFIG_INSTALL_NO_USR is set)
82+
83+ When udhcpc is started it executes this script to take care
84+ of system tasks after it completes DHCP communication. Such
85+ tasks include putting network interfaces up or down, setting
86+ DNS info, adding routing information, etc.
87+
88+if CONFIG_INSTALL_NO_USR
89+config CONFIG_UDHCPC_SCRIPT_PATH
90+ default "/share/udhcpc/default.script"
91+endif
92+
93+if !CONFIG_INSTALL_NO_USR
94+config CONFIG_UDHCPC_SCRIPT_PATH
95+ default "/usr/share/udhcpc/default.script"
96+endif
97+
98+endmenu
99+
100 endmenu
101
102Index: networking/udhcp/README
103===================================================================
104RCS file: /var/cvs/busybox/networking/udhcp/README,v
105retrieving revision 1.3
106diff -u -r1.3 README
107--- a/networking/udhcp/README 18 Dec 2003 22:25:38 -0000 1.3
108+++ b/networking/udhcp/README 5 Mar 2004 13:20:11 -0000
109@@ -9,27 +9,42 @@
110 compile time options
111 -------------------
112
113-The Makefile contains three of the compile time options:
114+The following options can be adjusted when configuring busybox:
115
116- UDHCP_DEBUG: If UDHCP_DEBUG is defined, udhcpd will output extra
117- debugging output, compile with -g, and not fork to the background when
118- run.
119- UDHCP_SYSLOG: If UDHCP_SYSLOG is defined, udhcpd will log all its
120- messages syslog, otherwise, it will attempt to log them to stdout.
121-
122- COMBINED_BINARY: If COMBINED_BINARY is define, one binary, udhcpd,
123- is created. If called as udhcpd, the dhcp server will be started.
124- If called as udhcpc, the dhcp client will be started.
125-
126-dhcpd.h contains the other three compile time options:
127-
128- LEASE_TIME: The default lease time if not specified in the config
129- file.
130+ CONFIG_FEATURE_UDHCP_DEBUG:
131+ If this is defined, udhcpd will output extra debugging output,
132+ compile with -g, and not fork to the background when run.
133
134- LEASES_FILE: The default file for storing leases.
135-
136- DHCPD_CONFIG_FILE: The defualt config file to use.
137+ CONFIG_FEATURE_UDHCP_SYSLOG:
138+ If this is defined, udhcpd will log all its messages syslog,
139+ otherwise, it will attempt to log them to stdout.
140+
141+ CONFIG_UDHCPD_CONF_PATH:
142+ The full path to udhcpd's default configuration file.
143+
144+ CONFIG_UDHCPD_LEASE_PATH:
145+ The full path to udhcpd's default leases file.
146+
147+ CONFIG_UDHCPD_PID_PATH:
148+ The full path to udhcpd's default pid file.
149+
150+ CONFIG_UDHCPC_SCRIPT_PATH:
151+ The full path to udhcpc's default event script file.
152+ (default is: /usr/share/udhcpc/default.script)
153+
154+ When udhcpc is started it executes this script to take care
155+ of system tasks after it completes DHCP communication. Such
156+ tasks include putting network interfaces up or down, setting
157+ DNS info, adding routing information, etc.
158+
159+
160+dhcpd.h contains the another compile time option:
161
162+ LEASE_TIME:
163+ The default lease time if not specified in the config file.
164+ This option can also be changed at runtime with the 'lease'
165+ configuration option.
166+
167 options.c contains a set of dhcp options for the client:
168
169 name[10]: The name of the option as it will appear in scripts
170Index: networking/udhcp/README.udhcpc
171===================================================================
172RCS file: /var/cvs/busybox/networking/udhcp/README.udhcpc,v
173retrieving revision 1.3
174diff -u -r1.3 README.udhcpc
175--- a/networking/udhcp/README.udhcpc 11 Dec 2002 21:12:44 -0000 1.3
176+++ b/networking/udhcp/README.udhcpc 5 Mar 2004 13:20:11 -0000
177@@ -23,7 +23,8 @@
178 -q, --quit Quit after obtaining lease
179 -r, --request=IP IP address to request (default: none)
180 -s, --script=file Run file at dhcp events (default:
181- /usr/share/udhcpc/default.script)
182+ /usr/share/udhcpc/default.script or
183+ CONFIG_UDHCPC_SCRIPT_PATH at build time)
184 -v, --version Display version
185
186
187Index: networking/udhcp/README.udhcpd
188===================================================================
189RCS file: /var/cvs/busybox/networking/udhcp/README.udhcpd,v
190retrieving revision 1.1
191diff -u -r1.1 README.udhcpd
192--- a/networking/udhcp/README.udhcpd 31 Oct 2002 19:21:27 -0000 1.1
193+++ b/networking/udhcp/README.udhcpd 5 Mar 2004 13:20:11 -0000
194@@ -50,10 +50,14 @@
195
196 compile time options
197 -------------------
198+
199+During busybox configuration, you can change the default paths for
200+udhcpd.conf, udhcpd.leases, and udhcpd.pid files. See README for
201+more details.
202
203-dhcpd.h contains the other two compile time options:
204+dhcpd.h contains the compile time option:
205
206 LEASE_TIME: The default lease time if not specified in the config
207 file.
208+
209
210- DHCPD_CONFIG_FILE: The defualt config file to use.
211Index: networking/udhcp/dhcpc.h
212===================================================================
213RCS file: /var/cvs/busybox/networking/udhcp/dhcpc.h,v
214retrieving revision 1.4
215diff -u -r1.4 dhcpc.h
216--- a/networking/udhcp/dhcpc.h 30 Jan 2004 23:45:12 -0000 1.4
217+++ b/networking/udhcp/dhcpc.h 5 Mar 2004 13:20:11 -0000
218@@ -2,7 +2,11 @@
219 #ifndef _DHCPC_H
220 #define _DHCPC_H
221
222-#define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script"
223+#ifdef CONFIG_UDHCPC_SCRIPT_PATH
224+ #define DEFAULT_SCRIPT CONFIG_UDHCPC_SCRIPT_PATH
225+#else
226+ #define DEFAULT_SCRIPT "/usr/share/udhcpc/default.script"
227+#endif
228
229 /* allow libbb_udhcp.h to redefine DEFAULT_SCRIPT */
230 #include "libbb_udhcp.h"
231Index: networking/udhcp/dhcpd.c
232===================================================================
233RCS file: /var/cvs/busybox/networking/udhcp/dhcpd.c,v
234retrieving revision 1.5
235diff -u -r1.5 dhcpd.c
236--- a/networking/udhcp/dhcpd.c 30 Jan 2004 23:45:12 -0000 1.5
237+++ b/networking/udhcp/dhcpd.c 5 Mar 2004 13:20:11 -0000
238@@ -70,6 +70,13 @@
239 struct dhcpOfferedAddr *lease;
240 int max_sock;
241 unsigned long num_ips;
242+ int daemonize = 1;
243+
244+ while (strcmp(argv[1],"-f")==0 || strcmp(argv[1],"--foreground")==0) {
245+ daemonize = 0;
246+ argv++;
247+ argc--;
248+ }
249
250 memset(&server_config, 0, sizeof(struct server_config_t));
251 read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]);
252@@ -99,9 +106,8 @@
253 &server_config.server, server_config.arp) < 0)
254 return 1;
255
256-#ifndef UDHCP_DEBUG
257- background(server_config.pidfile); /* hold lock during fork. */
258-#endif
259+ if(daemonize)
260+ background(server_config.pidfile); /* hold lock during fork. */
261
262 /* Setup the signal pipe */
263 udhcp_sp_setup();
264Index: networking/udhcp/dhcpd.h
265===================================================================
266RCS file: /var/cvs/busybox/networking/udhcp/dhcpd.h,v
267retrieving revision 1.5
268diff -u -r1.5 dhcpd.h
269--- a/networking/udhcp/dhcpd.h 30 Jan 2004 23:45:12 -0000 1.5
270+++ b/networking/udhcp/dhcpd.h 5 Mar 2004 13:20:12 -0000
271@@ -15,11 +15,25 @@
272
273 /* the period of time the client is allowed to use that address */
274 #define LEASE_TIME (60*60*24*10) /* 10 days of seconds */
275-#define LEASES_FILE "/var/lib/misc/udhcpd.leases"
276+
277+#ifdef CONFIG_UDHCPD_LEASE_PATH
278+ #define LEASES_FILE CONFIG_UDHCPD_LEASE_PATH
279+#else
280+ #define LEASES_FILE "/var/lib/misc/udhcpd.leases"
281+#endif
282
283 /* where to find the DHCP server configuration file */
284-#define DHCPD_CONF_FILE "/etc/udhcpd.conf"
285+#ifdef CONFIG_UDHCPD_CONF_PATH
286+ #define DHCPD_CONF_FILE CONFIG_UDHCPD_CONF_PATH
287+#else
288+ #define DHCPD_CONF_FILE "/etc/udhcpd.conf"
289+#endif
290
291+#ifdef CONFIG_UDHCPD_PID_PATH
292+ #define DHCPD_PID_FILE CONFIG_UDHCPD_PID_PATH
293+#else
294+ #define DHCPD_PID_FILE "/var/run/udhcpd.pid"
295+#endif
296 /*****************************************************************/
297 /* Do not modify below here unless you know what you are doing!! */
298 /*****************************************************************/
299Index: networking/udhcp/files.c
300===================================================================
301RCS file: /var/cvs/busybox/networking/udhcp/files.c,v
302retrieving revision 1.13
303diff -u -r1.13 files.c
304--- a/networking/udhcp/files.c 30 Jan 2004 23:45:12 -0000 1.13
305+++ b/networking/udhcp/files.c 5 Mar 2004 13:20:13 -0000
306@@ -166,7 +166,7 @@
307 {"offer_time", read_u32, &(server_config.offer_time), "60"},
308 {"min_lease", read_u32, &(server_config.min_lease), "60"},
309 {"lease_file", read_str, &(server_config.lease_file), LEASES_FILE},
310- {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"},
311+ {"pidfile", read_str, &(server_config.pidfile), DHCPD_PID_FILE},
312 {"notify_file", read_str, &(server_config.notify_file), ""},
313 {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"},
314 {"sname", read_str, &(server_config.sname), ""},
315Index: networking/udhcp/libbb_udhcp.h
316===================================================================
317RCS file: /var/cvs/busybox/networking/udhcp/libbb_udhcp.h,v
318retrieving revision 1.5
319diff -u -r1.5 libbb_udhcp.h
320--- a/networking/udhcp/libbb_udhcp.h 18 Dec 2003 22:25:38 -0000 1.5
321+++ b/networking/udhcp/libbb_udhcp.h 5 Mar 2004 13:20:13 -0000
322@@ -3,11 +3,6 @@
323 /* bit of a hack, do this no matter what the order of the includes.
324 * (for busybox) */
325
326-#ifdef CONFIG_INSTALL_NO_USR
327-#undef DEFUALT_SCRIPT
328-#define DEFAULT_SCRIPT "/share/udhcpc/default.script"
329-#endif
330-
331 #ifndef _LIBBB_UDHCP_H
332 #define _LIBBB_UDHCP_H
333
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 @@
1Index: ./networking/udhcp/dhcpd.c
2===================================================================
3RCS file: /var/cvs/busybox/networking/udhcp/dhcpd.c,v
4retrieving revision 1.5
5diff -u -r1.5 dhcpd.c
6--- a/./networking/udhcp/dhcpd.c 30 Jan 2004 23:45:12 -0000 1.5
7+++ b/./networking/udhcp/dhcpd.c 5 Mar 2004 13:09:05 -0000
8@@ -70,6 +70,13 @@
9 struct dhcpOfferedAddr *lease;
10 int max_sock;
11 unsigned long num_ips;
12+ int daemonize = 1;
13+
14+ while (strcmp(argv[1],"-f")==0 || strcmp(argv[1],"--foreground")==0) {
15+ daemonize = 0;
16+ argv++;
17+ argc--;
18+ }
19
20 memset(&server_config, 0, sizeof(struct server_config_t));
21 read_config(argc < 2 ? DHCPD_CONF_FILE : argv[1]);
22@@ -99,9 +106,8 @@
23 &server_config.server, server_config.arp) < 0)
24 return 1;
25
26-#ifndef UDHCP_DEBUG
27- background(server_config.pidfile); /* hold lock during fork. */
28-#endif
29+ if(daemonize)
30+ background(server_config.pidfile); /* hold lock during fork. */
31
32 /* Setup the signal pipe */
33 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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Process Utilities"
7
8config CONFIG_FREE
9 bool "free"
10 default n
11 help
12 free displays the total amount of free and used physical and swap
13 memory in the system, as well as the buffers used by the kernel.
14 The shared memory column should be ignored; it is obsolete.
15
16config CONFIG_KILL
17 bool "kill"
18 default n
19 help
20 The command kill sends the specified signal to the specified
21 process or process group. If no signal is specified, the TERM
22 signal is sent.
23
24config CONFIG_KILLALL
25 bool "killall"
26 default n
27 depends on CONFIG_KILL
28 help
29 killall sends a signal to all processes running any of the
30 specified commands. If no signal name is specified, SIGTERM is
31 sent.
32
33config CONFIG_PIDOF
34 bool "pidof"
35 default n
36 help
37 Pidof finds the process id's (pids) of the named programs. It prints
38 those id's on the standard output.
39
40config CONFIG_PS
41 bool "ps"
42 default n
43 help
44 ps gives a snapshot of the current processes.
45
46config CONFIG_RENICE
47 bool "renice"
48 default n
49 help
50 Renice alters the scheduling priority of one or more running
51 processes.
52
53config CONFIG_TOP
54 bool "top"
55 default n
56 help
57 The top program provides a dynamic real-time view of a running
58 system.
59
60config FEATURE_CPU_USAGE_PERCENTAGE
61 bool " Support showing CPU usage percentage (add 2k bytes)"
62 default y
63 depends on CONFIG_TOP
64 help
65 Make top display CPU usage.
66
67config CONFIG_UPTIME
68 bool "uptime"
69 default n
70 help
71 uptime gives a one line display of the current time, how long
72 the system has been running, how many users are currently logged
73 on, and the system load averages for the past 1, 5, and 15 minutes.
74
75config CONFIG_SYSCTL
76 bool "sysctl"
77 default n
78 help
79 sysctl - configure kernel parameters at runtime
80
81endmenu
82
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/procps
23PROCPS_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20PROCPS_AR:=procps.a
21ifndef $(PROCPS_DIR)
22PROCPS_DIR:=$(top_builddir)/procps/
23endif
24srcdir=$(top_srcdir)/procps
25
26PROCPS-y:=
27PROCPS-$(CONFIG_FREE) += free.o
28PROCPS-$(CONFIG_KILL) += kill.o
29PROCPS-$(CONFIG_PIDOF) += pidof.o
30PROCPS-$(CONFIG_PS) += ps.o
31PROCPS-$(CONFIG_RENICE) += renice.o
32PROCPS-$(CONFIG_SYSCTL) += sysctl.o
33PROCPS-$(CONFIG_TOP) += top.o
34PROCPS-$(CONFIG_UPTIME) += uptime.o
35
36libraries-y+=$(PROCPS_DIR)$(PROCPS_AR)
37
38$(PROCPS_DIR)$(PROCPS_AR): $(patsubst %,$(PROCPS_DIR)%, $(PROCPS-y))
39 $(AR) -ro $@ $(patsubst %,$(PROCPS_DIR)%, $(PROCPS-y))
40
41$(PROCPS_DIR)%.o: $(srcdir)/%.c
42 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
43
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini free implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* getopt not needed */
24
25#include <stdio.h>
26#include <errno.h>
27#include <stdlib.h>
28#include "busybox.h"
29
30extern int free_main(int argc, char **argv)
31{
32 struct sysinfo info;
33 sysinfo(&info);
34
35 /* Kernels prior to 2.4.x will return info.mem_unit==0, so cope... */
36 if (info.mem_unit==0) {
37 info.mem_unit=1;
38 }
39 if ( info.mem_unit == 1 ) {
40 info.mem_unit=1024;
41
42 /* TODO: Make all this stuff not overflow when mem >= 4 Gib */
43 info.totalram/=info.mem_unit;
44 info.freeram/=info.mem_unit;
45#ifndef __uClinux__
46 info.totalswap/=info.mem_unit;
47 info.freeswap/=info.mem_unit;
48#endif
49 info.sharedram/=info.mem_unit;
50 info.bufferram/=info.mem_unit;
51 } else {
52 info.mem_unit/=1024;
53 /* TODO: Make all this stuff not overflow when mem >= 4 Gib */
54 info.totalram*=info.mem_unit;
55 info.freeram*=info.mem_unit;
56#ifndef __uClinux__
57 info.totalswap*=info.mem_unit;
58 info.freeswap*=info.mem_unit;
59#endif
60 info.sharedram*=info.mem_unit;
61 info.bufferram*=info.mem_unit;
62 }
63
64 if (argc > 1 && **(argv + 1) == '-')
65 bb_show_usage();
66
67 printf("%6s%13s%13s%13s%13s%13s\n", "", "total", "used", "free",
68 "shared", "buffers");
69
70 printf("%6s%13ld%13ld%13ld%13ld%13ld\n", "Mem:", info.totalram,
71 info.totalram-info.freeram, info.freeram,
72 info.sharedram, info.bufferram);
73
74#ifndef __uClinux__
75 printf("%6s%13ld%13ld%13ld\n", "Swap:", info.totalswap,
76 info.totalswap-info.freeswap, info.freeswap);
77
78 printf("%6s%13ld%13ld%13ld\n", "Total:", info.totalram+info.totalswap,
79 (info.totalram-info.freeram)+(info.totalswap-info.freeswap),
80 info.freeram+info.freeswap);
81#endif
82 return EXIT_SUCCESS;
83}
84
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini kill/killall implementation for busybox
4 *
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <errno.h>
28#include <unistd.h>
29#include <signal.h>
30#include <ctype.h>
31#include <string.h>
32#include <unistd.h>
33#include "busybox.h"
34
35#define KILL 0
36#define KILLALL 1
37
38extern int kill_main(int argc, char **argv)
39{
40 int whichApp, signo = SIGTERM;
41 const char *name;
42 int errors = 0;
43
44#ifdef CONFIG_KILLALL
45 int quiet=0;
46 /* Figure out what we are trying to do here */
47 whichApp = (strcmp(bb_applet_name, "killall") == 0)? KILLALL : KILL;
48#else
49 whichApp = KILL;
50#endif
51
52 /* Parse any options */
53 if (argc < 2)
54 bb_show_usage();
55
56 if(argv[1][0] != '-'){
57 argv++;
58 argc--;
59 goto do_it_now;
60 }
61
62 /* The -l option, which prints out signal names. */
63 if(argv[1][1]=='l' && argv[1][2]=='\0'){
64 if(argc==2) {
65 /* Print the whole signal list */
66 int col = 0;
67 for(signo=1; signo < NSIG; signo++) {
68 name = u_signal_names(0, &signo, 1);
69 if(name==NULL) /* unnamed */
70 continue;
71 col += printf("%2d) %-16s", signo, name);
72 if (col > 60) {
73 printf("\n");
74 col = 0;
75 }
76 }
77 printf("\n");
78
79 } else {
80 for(argv++; *argv; argv++) {
81 name = u_signal_names(*argv, &signo, -1);
82 if(name!=NULL)
83 printf("%s\n", name);
84 }
85 }
86 /* If they specified -l, were all done */
87 return EXIT_SUCCESS;
88 }
89
90#ifdef CONFIG_KILLALL
91 /* The -q quiet option */
92 if(argv[1][1]=='q' && argv[1][2]=='\0'){
93 quiet++;
94 argv++;
95 argc--;
96 if(argc<2 || argv[1][0] != '-'){
97 goto do_it_now;
98 }
99 }
100#endif
101
102 if(!u_signal_names(argv[1]+1, &signo, 0))
103 bb_error_msg_and_die( "bad signal name '%s'", argv[1]+1);
104 argv+=2;
105 argc-=2;
106
107do_it_now:
108
109 if (whichApp == KILL) {
110 /* Looks like they want to do a kill. Do that */
111 while (--argc >= 0) {
112 int pid;
113
114 if (!isdigit(**argv))
115 bb_error_msg_and_die( "Bad PID '%s'", *argv);
116 pid = strtol(*argv, NULL, 0);
117 if (kill(pid, signo) != 0) {
118 bb_perror_msg( "Could not kill pid '%d'", pid);
119 errors++;
120 }
121 argv++;
122 }
123
124 }
125#ifdef CONFIG_KILLALL
126 else {
127 pid_t myPid=getpid();
128 /* Looks like they want to do a killall. Do that */
129 while (--argc >= 0) {
130 long* pidList;
131
132 pidList = find_pid_by_name(*argv);
133 if (!pidList || *pidList<=0) {
134 errors++;
135 if (quiet==0)
136 bb_error_msg( "%s: no process killed", *argv);
137 } else {
138 long *pl;
139
140 for(pl = pidList; *pl !=0 ; pl++) {
141 if (*pl==myPid)
142 continue;
143 if (kill(*pl, signo) != 0) {
144 errors++;
145 if (quiet==0)
146 bb_perror_msg( "Could not kill pid '%ld'", *pl);
147 }
148 }
149 }
150 free(pidList);
151 argv++;
152 }
153 }
154#endif
155 return errors;
156}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * pidof implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <errno.h>
27#include <unistd.h>
28#include <signal.h>
29#include <ctype.h>
30#include <string.h>
31#include <unistd.h>
32#include "busybox.h"
33
34
35extern int pidof_main(int argc, char **argv)
36{
37 int opt, n = 0;
38 int single_flag = 0;
39 int fail = 1;
40
41 /* do normal option parsing */
42 while ((opt = getopt(argc, argv, "s")) > 0) {
43 switch (opt) {
44 case 's':
45 single_flag = 1;
46 break;
47 default:
48 bb_show_usage();
49 }
50 }
51
52 /* Looks like everything is set to go. */
53 while(optind < argc) {
54 long *pidList;
55 long *pl;
56
57 pidList = find_pid_by_name(argv[optind]);
58 for(pl = pidList; *pl > 0; pl++) {
59 printf("%s%ld", (n++ ? " " : ""), *pl);
60 fail = 0;
61 if (single_flag)
62 break;
63 }
64 free(pidList);
65 optind++;
66
67 }
68 printf("\n");
69
70 return fail ? EXIT_FAILURE : EXIT_SUCCESS;
71}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini ps implementation(s) for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <unistd.h>
25#include <dirent.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <ctype.h>
29#include <string.h>
30#include <termios.h>
31#include <sys/ioctl.h>
32#include "busybox.h"
33#ifdef CONFIG_SELINUX
34#include <fs_secure.h>
35#include <ss.h>
36#include <flask_util.h> /* for is_flask_enabled() */
37#endif
38
39static const int TERMINAL_WIDTH = 79; /* not 80 in case terminal has linefold bug */
40
41
42
43extern int ps_main(int argc, char **argv)
44{
45 procps_status_t * p;
46 int i, len;
47 int terminal_width = TERMINAL_WIDTH;
48
49#ifdef CONFIG_SELINUX
50 int use_selinux = 0;
51 security_id_t sid;
52 if(is_flask_enabled() && argv[1] && !strcmp(argv[1], "-c") )
53 use_selinux = 1;
54#endif
55
56 get_terminal_width_height(0, &terminal_width, NULL);
57 /* Go one less... */
58 terminal_width--;
59
60#ifdef CONFIG_SELINUX
61 if(use_selinux)
62 printf(" PID Context Stat Command\n");
63 else
64#endif
65 printf(" PID Uid VmSize Stat Command\n");
66#ifdef CONFIG_SELINUX
67 while ((p = procps_scan(1, use_selinux, &sid)) != 0) {
68#else
69 while ((p = procps_scan(1)) != 0) {
70#endif
71 char *namecmd = p->cmd;
72
73#ifdef CONFIG_SELINUX
74 if(use_selinux)
75 {
76 char sbuf[128];
77 len = sizeof(sbuf);
78 if(security_sid_to_context(sid, (security_context_t)&sbuf, &len))
79 strcpy(sbuf, "unknown");
80
81 len = printf("%5d %-32s %s ", p->pid, sbuf, p->state);
82 }
83 else
84#endif
85 if(p->rss == 0)
86 len = printf("%5d %-8s %s ", p->pid, p->user, p->state);
87 else
88 len = printf("%5d %-8s %6ld %s ", p->pid, p->user, p->rss, p->state);
89 i = terminal_width-len;
90
91 if(namecmd != 0 && namecmd[0] != 0) {
92 if(i < 0)
93 i = 0;
94 if(strlen(namecmd) > i)
95 namecmd[i] = 0;
96 printf("%s\n", namecmd);
97 } else {
98 namecmd = p->short_cmd;
99 if(i < 2)
100 i = 2;
101 if(strlen(namecmd) > (i-2))
102 namecmd[i-2] = 0;
103 printf("[%s]\n", namecmd);
104 }
105 free(p->cmd);
106 }
107 return EXIT_SUCCESS;
108}
109
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 @@
1/*
2 * Mini renice implementation for busybox
3 *
4 *
5 * Copyright (C) 2000 Dave 'Kill a Cop' Cinege <dcinege@psychosis.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <stdio.h>
24#include <errno.h>
25#include <stdlib.h>
26#include <sys/time.h>
27#include <sys/resource.h>
28#include "busybox.h"
29
30
31extern int renice_main(int argc, char **argv)
32{
33 int prio, status = EXIT_SUCCESS;
34
35 if (argc < 3) bb_show_usage();
36
37 prio = atoi(*++argv);
38 if (prio > 20) prio = 20;
39 if (prio < -20) prio = -20;
40
41 while (*++argv) {
42 int ps = atoi(*argv);
43 int oldp = getpriority(PRIO_PROCESS, ps);
44
45 if (setpriority(PRIO_PROCESS, ps, prio) == 0) {
46 printf("%d: old priority %d, new priority %d\n", ps, oldp, prio );
47 } else {
48 bb_perror_msg("%d: setpriority", ps);
49 status = EXIT_FAILURE;
50 }
51 }
52
53 return status;
54}
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 @@
1
2/*
3 * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters
4 *
5 *
6 * "Copyright 1999 George Staikos
7 * This file may be used subject to the terms and conditions of the
8 * GNU General Public License Version 2, or any later version
9 * at your option, as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details."
14 *
15 * Changelog:
16 * v1.01:
17 * - added -p <preload> to preload values from a file
18 * v1.01.1
19 * - busybox applet aware by <solar@gentoo.org>
20 *
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <unistd.h>
26#include <sys/stat.h>
27#include <sys/types.h>
28#include <dirent.h>
29#include <string.h>
30#include <errno.h>
31#include <fcntl.h>
32#include "busybox.h"
33
34/*
35 * Function Prototypes
36 */
37static int sysctl_read_setting(const char *setting, int output);
38static int sysctl_write_setting(const char *setting, int output);
39static int sysctl_preload_file(const char *filename, int output);
40static int sysctl_display_all(const char *path, int output, int show_table);
41
42/*
43 * Globals...
44 */
45static const char PROC_PATH[] = "/proc/sys/";
46static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf";
47
48/* error messages */
49static const char ERR_UNKNOWN_PARAMETER[] = "error: Unknown parameter '%s'\n";
50static const char ERR_MALFORMED_SETTING[] = "error: Malformed setting '%s'\n";
51static const char ERR_NO_EQUALS[] =
52 "error: '%s' must be of the form name=value\n";
53static const char ERR_INVALID_KEY[] = "error: '%s' is an unknown key\n";
54static const char ERR_UNKNOWN_WRITING[] =
55 "error: unknown error %d setting key '%s'\n";
56static const char ERR_UNKNOWN_READING[] =
57 "error: unknown error %d reading key '%s'\n";
58static const char ERR_PERMISSION_DENIED[] =
59 "error: permission denied on key '%s'\n";
60static const char ERR_OPENING_DIR[] = "error: unable to open directory '%s'\n";
61static const char ERR_PRELOAD_FILE[] =
62 "error: unable to open preload file '%s'\n";
63static const char WARN_BAD_LINE[] =
64 "warning: %s(%d): invalid syntax, continuing...\n";
65
66
67static void dwrite_str(int fd, const char *buf)
68{
69 write(fd, buf, strlen(buf));
70}
71
72/*
73 * sysctl_main()...
74 */
75int sysctl_main(int argc, char **argv)
76{
77 int retval = 0;
78 int output = 1;
79 int write_mode = 0;
80 int switches_allowed = 1;
81
82 if (argc < 2)
83 bb_show_usage();
84
85 argv++;
86
87 for (; argv && *argv && **argv; argv++) {
88 if (switches_allowed && **argv == '-') { /* we have a switch */
89 switch ((*argv)[1]) {
90 case 'n':
91 output = 0;
92 break;
93 case 'w':
94 write_mode = 1;
95 switches_allowed = 0;
96 break;
97 case 'p':
98 argv++;
99 return
100 sysctl_preload_file(((argv && *argv
101 && **argv) ? *argv :
102 DEFAULT_PRELOAD), output);
103 case 'a':
104 case 'A':
105 switches_allowed = 0;
106 return sysctl_display_all(PROC_PATH, output,
107 ((*argv)[1] == 'a') ? 0 : 1);
108 case 'h':
109 case '?':
110 bb_show_usage();
111 default:
112 bb_error_msg(ERR_UNKNOWN_PARAMETER, *argv);
113 bb_show_usage();
114 }
115 } else {
116 switches_allowed = 0;
117 if (write_mode)
118 retval = sysctl_write_setting(*argv, output);
119 else
120 sysctl_read_setting(*argv, output);
121 }
122 }
123 return retval;
124} /* end sysctl_main() */
125
126
127
128/*
129 * sysctl_preload_file
130 * preload the sysctl's from a conf file
131 * - we parse the file and then reform it (strip out whitespace)
132 */
133#define PRELOAD_BUF 256
134
135int sysctl_preload_file(const char *filename, int output)
136{
137 int lineno = 0;
138 char oneline[PRELOAD_BUF];
139 char buffer[PRELOAD_BUF];
140 char *name, *value, *ptr;
141 FILE *fp = NULL;
142
143 if (!filename || ((fp = fopen(filename, "r")) == NULL)) {
144 bb_error_msg_and_die(ERR_PRELOAD_FILE, filename);
145 }
146
147 while (fgets(oneline, sizeof(oneline) - 1, fp)) {
148 oneline[sizeof(oneline) - 1] = 0;
149 lineno++;
150 trim(oneline);
151 ptr = (char *) oneline;
152
153 if (*ptr == '#' || *ptr == ';')
154 continue;
155
156 if (bb_strlen(ptr) < 2)
157 continue;
158
159 name = strtok(ptr, "=");
160 if (!name || !*name) {
161 bb_error_msg(WARN_BAD_LINE, filename, lineno);
162 continue;
163 }
164
165 trim(name);
166
167 value = strtok(NULL, "\n\r");
168 if (!value || !*value) {
169 bb_error_msg(WARN_BAD_LINE, filename, lineno);
170 continue;
171 }
172
173 while ((*value == ' ' || *value == '\t') && *value != 0)
174 value++;
175 strcpy(buffer, name);
176 strcat(buffer, "=");
177 strcat(buffer, value);
178 sysctl_write_setting(buffer, output);
179 }
180 fclose(fp);
181 return 0;
182} /* end sysctl_preload_file() */
183
184
185/*
186 * Write a single sysctl setting
187 */
188int sysctl_write_setting(const char *setting, int output)
189{
190 int retval = 0;
191 const char *name = setting;
192 const char *value;
193 const char *equals;
194 char *tmpname, *outname, *cptr;
195 int fd = -1;
196
197 if (!name) /* probably dont' want to display this err */
198 return 0;
199
200 if (!(equals = strchr(setting, '='))) {
201 bb_error_msg(ERR_NO_EQUALS, setting);
202 return -1;
203 }
204
205 value = equals + sizeof(char); /* point to the value in name=value */
206
207 if (!*name || !*value || name == equals) {
208 bb_error_msg(ERR_MALFORMED_SETTING, setting);
209 return -2;
210 }
211
212 bb_xasprintf(&tmpname, "%s%.*s", PROC_PATH, (equals - name), name);
213 outname = bb_xstrdup(tmpname + strlen(PROC_PATH));
214
215 while ((cptr = strchr(tmpname, '.')) != NULL)
216 *cptr = '/';
217
218 while ((cptr = strchr(outname, '/')) != NULL)
219 *cptr = '.';
220
221 if ((fd = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
222 switch (errno) {
223 case ENOENT:
224 bb_error_msg(ERR_INVALID_KEY, outname);
225 break;
226 case EACCES:
227 bb_perror_msg(ERR_PERMISSION_DENIED, outname);
228 break;
229 default:
230 bb_error_msg(ERR_UNKNOWN_WRITING, errno, outname);
231 break;
232 }
233 retval = -1;
234 } else {
235 dwrite_str(fd, value);
236 close(fd);
237 if (output) {
238 dwrite_str(STDOUT_FILENO, outname);
239 dwrite_str(STDOUT_FILENO, " = ");
240 }
241 dwrite_str(STDOUT_FILENO, value);
242 dwrite_str(STDOUT_FILENO, "\n");
243 }
244
245 /* cleanup */
246 free(tmpname);
247 free(outname);
248 return retval;
249} /* end sysctl_write_setting() */
250
251
252/*
253 * Read a sysctl setting
254 *
255 */
256int sysctl_read_setting(const char *setting, int output)
257{
258 int retval = 0;
259 char *tmpname, *outname, *cptr;
260 char inbuf[1025];
261 const char *name = setting;
262 FILE *fp;
263
264 if (!setting || !*setting)
265 bb_error_msg(ERR_INVALID_KEY, setting);
266
267 tmpname = concat_path_file(PROC_PATH, name);
268 outname = bb_xstrdup(tmpname + strlen(PROC_PATH));
269
270 while ((cptr = strchr(tmpname, '.')) != NULL)
271 *cptr = '/';
272 while ((cptr = strchr(outname, '/')) != NULL)
273 *cptr = '.';
274
275 if ((fp = fopen(tmpname, "r")) == NULL) {
276 switch (errno) {
277 case ENOENT:
278 bb_error_msg(ERR_INVALID_KEY, outname);
279 break;
280 case EACCES:
281 bb_error_msg(ERR_PERMISSION_DENIED, outname);
282 break;
283 default:
284 bb_error_msg(ERR_UNKNOWN_READING, errno, outname);
285 break;
286 }
287 retval = -1;
288 } else {
289 while (fgets(inbuf, sizeof(inbuf) - 1, fp)) {
290 if (output) {
291 dwrite_str(STDOUT_FILENO, outname);
292 dwrite_str(STDOUT_FILENO, " = ");
293 }
294 dwrite_str(STDOUT_FILENO, inbuf);
295 }
296 fclose(fp);
297 }
298
299 free(tmpname);
300 free(outname);
301 return retval;
302} /* end sysctl_read_setting() */
303
304
305
306/*
307 * Display all the sysctl settings
308 *
309 */
310int sysctl_display_all(const char *path, int output, int show_table)
311{
312 int retval = 0;
313 int retval2;
314 DIR *dp;
315 struct dirent *de;
316 char *tmpdir;
317 struct stat ts;
318
319 if (!(dp = opendir(path))) {
320 bb_perror_msg(ERR_OPENING_DIR, path);
321 retval = -1;
322 } else {
323 while ((de = readdir(dp)) != NULL) {
324 tmpdir = concat_subpath_file(path, de->d_name);
325 if(tmpdir == NULL)
326 continue;
327 if ((retval2 = stat(tmpdir, &ts)) != 0)
328 bb_perror_msg(tmpdir);
329 else {
330 if (S_ISDIR(ts.st_mode)) {
331 sysctl_display_all(tmpdir, output, show_table);
332 } else
333 retval |=
334 sysctl_read_setting(tmpdir + bb_strlen(PROC_PATH),
335 output);
336
337 }
338 free(tmpdir);
339 } /* end while */
340 closedir(dp);
341 }
342
343 return retval;
344} /* end sysctl_display_all() */
345
346#ifdef STANDALONE_SYSCTL
347int main(int argc, char **argv)
348{
349 return sysctl_main(argc, argv);
350}
351const char *bb_applet_name = "sysctl";
352#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 @@
1/*
2 * A tiny 'top' utility.
3 *
4 * This is written specifically for the linux /proc/<PID>/stat(m)
5 * files format.
6
7 * This reads the PIDs of all processes and their status and shows
8 * the status of processes (first ones that fit to screen) at given
9 * intervals.
10 *
11 * NOTES:
12 * - At startup this changes to /proc, all the reads are then
13 * relative to that.
14 *
15 * (C) Eero Tamminen <oak at welho dot com>
16 *
17 * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru>
18 */
19
20/* Original code Copyrights */
21/*
22 * Copyright (c) 1992 Branko Lankester
23 * Copyright (c) 1992 Roger Binns
24 * Copyright (C) 1994-1996 Charles L. Blake.
25 * Copyright (C) 1992-1998 Michael K. Johnson
26 * May be distributed under the conditions of the
27 * GNU Library General Public License
28 */
29
30#include <sys/types.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <unistd.h>
34#include <string.h>
35#include <sys/ioctl.h>
36/* get page info */
37#include <asm/page.h>
38#include "busybox.h"
39
40//#define FEATURE_CPU_USAGE_PERCENTAGE /* + 2k */
41
42#ifdef FEATURE_CPU_USAGE_PERCENTAGE
43#include <time.h>
44#include <sys/time.h>
45#include <fcntl.h>
46#include <netinet/in.h> /* htons */
47#endif
48
49
50typedef int (*cmp_t)(procps_status_t *P, procps_status_t *Q);
51
52static procps_status_t *top; /* Hehe */
53static int ntop;
54
55
56static int pid_sort (procps_status_t *P, procps_status_t *Q)
57{
58 return (Q->pid - P->pid);
59}
60
61static int mem_sort (procps_status_t *P, procps_status_t *Q)
62{
63 return (int)(Q->rss - P->rss);
64}
65
66#ifdef FEATURE_CPU_USAGE_PERCENTAGE
67
68#define sort_depth 3
69static cmp_t sort_function[sort_depth];
70
71static int pcpu_sort (procps_status_t *P, procps_status_t *Q)
72{
73 return (Q->pcpu - P->pcpu);
74}
75
76static int time_sort (procps_status_t *P, procps_status_t *Q)
77{
78 return (int)((Q->stime + Q->utime) - (P->stime + P->utime));
79}
80
81int mult_lvl_cmp(void* a, void* b) {
82 int i, cmp_val;
83
84 for(i = 0; i < sort_depth; i++) {
85 cmp_val = (*sort_function[i])(a, b);
86 if (cmp_val != 0)
87 return cmp_val;
88 }
89 return 0;
90}
91
92/* This structure stores some critical information from one frame to
93 the next. mostly used for sorting. Added cumulative and resident fields. */
94struct save_hist {
95 int ticks;
96 int pid;
97 int utime;
98 int stime;
99};
100
101/*
102 * Calculates percent cpu usage for each task.
103 */
104
105static struct save_hist *save_history;
106
107static unsigned long Hertz;
108
109/***********************************************************************
110 * Some values in /proc are expressed in units of 1/HZ seconds, where HZ
111 * is the kernel clock tick rate. One of these units is called a jiffy.
112 * The HZ value used in the kernel may vary according to hacker desire.
113 * According to Linus Torvalds, this is not true. He considers the values
114 * in /proc as being in architecture-dependent units that have no relation
115 * to the kernel clock tick rate. Examination of the kernel source code
116 * reveals that opinion as wishful thinking.
117 *
118 * In any case, we need the HZ constant as used in /proc. (the real HZ value
119 * may differ, but we don't care) There are several ways we could get HZ:
120 *
121 * 1. Include the kernel header file. If it changes, recompile this library.
122 * 2. Use the sysconf() function. When HZ changes, recompile the C library!
123 * 3. Ask the kernel. This is obviously correct...
124 *
125 * Linus Torvalds won't let us ask the kernel, because he thinks we should
126 * not know the HZ value. Oh well, we don't have to listen to him.
127 * Someone smuggled out the HZ value. :-)
128 *
129 * This code should work fine, even if Linus fixes the kernel to match his
130 * stated behavior. The code only fails in case of a partial conversion.
131 *
132 */
133
134#define FILE_TO_BUF(filename, fd) do{ \
135 if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) { \
136 bb_perror_msg_and_die("/proc not be mounted?"); \
137 } \
138 lseek(fd, 0L, SEEK_SET); \
139 if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \
140 bb_perror_msg_and_die("%s", filename); \
141 } \
142 buf[local_n] = '\0'; \
143}while(0)
144
145#define FILE_TO_BUF2(filename, fd) do{ \
146 lseek(fd, 0L, SEEK_SET); \
147 if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \
148 bb_perror_msg_and_die("%s", filename); \
149 } \
150 buf[local_n] = '\0'; \
151}while(0)
152
153static void init_Hertz_value(void) {
154 unsigned long user_j, nice_j, sys_j, other_j; /* jiffies (clock ticks) */
155 double up_1, up_2, seconds;
156 unsigned long jiffies, h;
157 char buf[80];
158 int uptime_fd = -1;
159 int stat_fd = -1;
160
161 long smp_num_cpus = sysconf(_SC_NPROCESSORS_CONF);
162
163 if(smp_num_cpus<1) smp_num_cpus=1;
164 do {
165 int local_n;
166
167 FILE_TO_BUF("uptime", uptime_fd);
168 up_1 = strtod(buf, 0);
169 FILE_TO_BUF("stat", stat_fd);
170 sscanf(buf, "cpu %lu %lu %lu %lu", &user_j, &nice_j, &sys_j, &other_j);
171 FILE_TO_BUF2("uptime", uptime_fd);
172 up_2 = strtod(buf, 0);
173 } while((long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */
174
175 close(uptime_fd);
176 close(stat_fd);
177
178 jiffies = user_j + nice_j + sys_j + other_j;
179 seconds = (up_1 + up_2) / 2;
180 h = (unsigned long)( (double)jiffies/seconds/smp_num_cpus );
181 /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */
182 switch(h){
183 case 30 ... 34 : Hertz = 32; break; /* ia64 emulator */
184 case 48 ... 52 : Hertz = 50; break;
185 case 58 ... 62 : Hertz = 60; break;
186 case 63 ... 65 : Hertz = 64; break; /* StrongARM /Shark */
187 case 95 ... 105 : Hertz = 100; break; /* normal Linux */
188 case 124 ... 132 : Hertz = 128; break; /* MIPS, ARM */
189 case 195 ... 204 : Hertz = 200; break; /* normal << 1 */
190 case 253 ... 260 : Hertz = 256; break;
191 case 295 ... 304 : Hertz = 300; break; /* 3 cpus */
192 case 393 ... 408 : Hertz = 400; break; /* normal << 2 */
193 case 495 ... 504 : Hertz = 500; break; /* 5 cpus */
194 case 595 ... 604 : Hertz = 600; break; /* 6 cpus */
195 case 695 ... 704 : Hertz = 700; break; /* 7 cpus */
196 case 790 ... 808 : Hertz = 800; break; /* normal << 3 */
197 case 895 ... 904 : Hertz = 900; break; /* 9 cpus */
198 case 990 ... 1010 : Hertz = 1000; break; /* ARM */
199 case 1015 ... 1035 : Hertz = 1024; break; /* Alpha, ia64 */
200 case 1095 ... 1104 : Hertz = 1100; break; /* 11 cpus */
201 case 1180 ... 1220 : Hertz = 1200; break; /* Alpha */
202 default:
203 /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */
204 Hertz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;
205 }
206}
207
208static void do_stats(void)
209{
210 struct timeval t;
211 static struct timeval oldtime;
212 struct timezone timez;
213 float elapsed_time;
214
215 procps_status_t *cur;
216 int total_time, i, n;
217 static int prev_count;
218 int systime, usrtime, pid;
219
220 struct save_hist *New_save_hist;
221
222 /*
223 * Finds the current time (in microseconds) and calculates the time
224 * elapsed since the last update.
225 */
226 gettimeofday(&t, &timez);
227 elapsed_time = (t.tv_sec - oldtime.tv_sec)
228 + (float) (t.tv_usec - oldtime.tv_usec) / 1000000.0;
229 oldtime.tv_sec = t.tv_sec;
230 oldtime.tv_usec = t.tv_usec;
231
232 New_save_hist = alloca(sizeof(struct save_hist)*ntop);
233 /*
234 * Make a pass through the data to get stats.
235 */
236 for(n = 0; n < ntop; n++) {
237 cur = top + n;
238
239 /*
240 * Calculate time in cur process. Time is sum of user time
241 * (usrtime) plus system time (systime).
242 */
243 systime = cur->stime;
244 usrtime = cur->utime;
245 pid = cur->pid;
246 total_time = systime + usrtime;
247 New_save_hist[n].ticks = total_time;
248 New_save_hist[n].pid = pid;
249 New_save_hist[n].stime = systime;
250 New_save_hist[n].utime = usrtime;
251
252 /* find matching entry from previous pass */
253 for (i = 0; i < prev_count; i++) {
254 if (save_history[i].pid == pid) {
255 total_time -= save_history[i].ticks;
256 systime -= save_history[i].stime;
257 usrtime -= save_history[i].utime;
258 break;
259 }
260 }
261
262 /*
263 * Calculate percent cpu time for cur task.
264 */
265 i = (total_time * 10 * 100/Hertz) / elapsed_time;
266 if (i > 999)
267 i = 999;
268 cur->pcpu = i;
269
270 }
271
272 /*
273 * Save cur frame's information.
274 */
275 free(save_history);
276 save_history = memcpy(xmalloc(sizeof(struct save_hist)*n), New_save_hist,
277 sizeof(struct save_hist)*n);
278 prev_count = n;
279 qsort(top, n, sizeof(procps_status_t), (void*)mult_lvl_cmp);
280}
281#else
282static cmp_t sort_function;
283#endif /* FEATURE_CPU_USAGE_PERCENTAGE */
284
285/* display generic info (meminfo / loadavg) */
286static unsigned long display_generic(void)
287{
288 FILE *fp;
289 char buf[80];
290 float avg1, avg2, avg3;
291 unsigned long total, used, mfree, shared, buffers, cached;
292 unsigned int needs_conversion = 1;
293
294 /* read memory info */
295 fp = bb_xfopen("meminfo", "r");
296
297 /*
298 * Old kernels (such as 2.4.x) had a nice summary of memory info that
299 * we could parse, however this is gone entirely in 2.6. Try parsing
300 * the old way first, and if that fails, parse each field manually.
301 *
302 * First, we read in the first line. Old kernels will have bogus
303 * strings we don't care about, whereas new kernels will start right
304 * out with MemTotal:
305 * -- PFM.
306 */
307 if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) {
308 fgets(buf, sizeof(buf), fp); /* skip first line */
309
310 fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu",
311 &total, &used, &mfree, &shared, &buffers, &cached);
312 } else {
313 /*
314 * Revert to manual parsing, which incidentally already has the
315 * sizes in kilobytes. This should be safe for both 2.4 and
316 * 2.6.
317 */
318 needs_conversion = 0;
319
320 fscanf(fp, "MemFree: %lu %s\n", &mfree, buf);
321
322 /*
323 * MemShared: is no longer present in 2.6. Report this as 0,
324 * to maintain consistent behavior with normal procps.
325 */
326 if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2)
327 shared = 0;
328
329 fscanf(fp, "Buffers: %lu %s\n", &buffers, buf);
330 fscanf(fp, "Cached: %lu %s\n", &cached, buf);
331
332 used = total - mfree;
333 }
334 fclose(fp);
335
336 /* read load average */
337 fp = bb_xfopen("loadavg", "r");
338 if (fscanf(fp, "%f %f %f", &avg1, &avg2, &avg3) != 3) {
339 bb_error_msg_and_die("failed to read '%s'", "loadavg");
340 }
341 fclose(fp);
342
343 if (needs_conversion) {
344 /* convert to kilobytes */
345 used /= 1024;
346 mfree /= 1024;
347 shared /= 1024;
348 buffers /= 1024;
349 cached /= 1024;
350 total /= 1024;
351 }
352
353 /* output memory info and load average */
354 /* clear screen & go to top */
355 printf("\e[H\e[J" "Mem: "
356 "%ldK used, %ldK free, %ldK shrd, %ldK buff, %ldK cached\n",
357 used, mfree, shared, buffers, cached);
358 printf("Load average: %.2f, %.2f, %.2f "
359 "(State: S=sleeping R=running, W=waiting)\n",
360 avg1, avg2, avg3);
361 return total;
362}
363
364
365/* display process statuses */
366static void display_status(int count, int col)
367{
368 procps_status_t *s = top;
369 char rss_str_buf[8];
370 unsigned long total_memory = display_generic();
371
372#ifdef FEATURE_CPU_USAGE_PERCENTAGE
373 /* what info of the processes is shown */
374 printf("\n\e[7m PID USER STATUS RSS PPID %%CPU %%MEM COMMAND\e[0m\n");
375#else
376 printf("\n\e[7m PID USER STATUS RSS PPID %%MEM COMMAND\e[0m\n");
377#endif
378
379 while (count--) {
380 char *namecmd = s->short_cmd;
381 int pmem;
382
383 pmem = 1000.0 * s->rss / total_memory;
384 if (pmem > 999) pmem = 999;
385
386 if(s->rss > 10*1024)
387 sprintf(rss_str_buf, "%6ldM", s->rss/1024);
388 else
389 sprintf(rss_str_buf, "%7ld", s->rss);
390#ifdef FEATURE_CPU_USAGE_PERCENTAGE
391 printf("%5d %-8s %s %s %5d %2d.%d %2u.%u ",
392 s->pid, s->user, s->state, rss_str_buf, s->ppid,
393 s->pcpu/10, s->pcpu%10, pmem/10, pmem%10);
394#else
395 printf("%5d %-8s %s %s %5d %2u.%u ",
396 s->pid, s->user, s->state, rss_str_buf, s->ppid,
397 pmem/10, pmem%10);
398#endif
399 if(strlen(namecmd) > col)
400 namecmd[col] = 0;
401 printf("%s\n", namecmd);
402 s++;
403 }
404}
405
406static void clearmems(void)
407{
408 free(top);
409 top = 0;
410 ntop = 0;
411}
412
413#if defined CONFIG_FEATURE_USE_TERMIOS
414#include <termios.h>
415#include <sys/time.h>
416#include <signal.h>
417
418
419static struct termios initial_settings;
420
421static void reset_term(void)
422{
423 tcsetattr(0, TCSANOW, (void *) &initial_settings);
424#ifdef CONFIG_FEATURE_CLEAN_UP
425 clearmems();
426#ifdef FEATURE_CPU_USAGE_PERCENTAGE
427 free(save_history);
428#endif
429#endif /* CONFIG_FEATURE_CLEAN_UP */
430}
431
432static void sig_catcher (int sig)
433{
434 reset_term();
435}
436#endif /* CONFIG_FEATURE_USE_TERMIOS */
437
438
439int top_main(int argc, char **argv)
440{
441 int opt, interval, lines, col;
442#if defined CONFIG_FEATURE_USE_TERMIOS
443 struct termios new_settings;
444 struct timeval tv;
445 fd_set readfds;
446 unsigned char c;
447 struct sigaction sa;
448#endif /* CONFIG_FEATURE_USE_TERMIOS */
449
450 /* Default update rate is 5 seconds */
451 interval = 5;
452
453 /* do normal option parsing */
454 while ((opt = getopt(argc, argv, "d:")) > 0) {
455 switch (opt) {
456 case 'd':
457 interval = atoi(optarg);
458 break;
459 default:
460 bb_show_usage();
461 }
462 }
463
464 /* Default to 25 lines - 5 lines for status */
465 lines = 25 - 5;
466 /* Default CMD format size */
467#ifdef FEATURE_CPU_USAGE_PERCENTAGE
468 col = 35 - 6;
469#else
470 col = 35;
471#endif
472 /* change to /proc */
473 if (chdir("/proc") < 0) {
474 bb_perror_msg_and_die("chdir('/proc')");
475 }
476#if defined CONFIG_FEATURE_USE_TERMIOS
477 tcgetattr(0, (void *) &initial_settings);
478 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
479 new_settings.c_lflag &= ~(ISIG | ICANON); /* unbuffered input */
480 /* Turn off echoing */
481 new_settings.c_lflag &= ~(ECHO | ECHONL);
482
483 signal (SIGTERM, sig_catcher);
484 sigaction (SIGTERM, (struct sigaction *) 0, &sa);
485 sa.sa_flags |= SA_RESTART;
486 sa.sa_flags &= ~SA_INTERRUPT;
487 sigaction (SIGTERM, &sa, (struct sigaction *) 0);
488 sigaction (SIGINT, &sa, (struct sigaction *) 0);
489 tcsetattr(0, TCSANOW, (void *) &new_settings);
490 atexit(reset_term);
491
492 get_terminal_width_height(0, &col, &lines);
493 if (lines > 4) {
494 lines -= 5;
495#ifdef FEATURE_CPU_USAGE_PERCENTAGE
496 col = col - 80 + 35 - 6;
497#else
498 col = col - 80 + 35;
499#endif
500 }
501#endif /* CONFIG_FEATURE_USE_TERMIOS */
502#ifdef FEATURE_CPU_USAGE_PERCENTAGE
503 sort_function[0] = pcpu_sort;
504 sort_function[1] = mem_sort;
505 sort_function[2] = time_sort;
506#else
507 sort_function = mem_sort;
508#endif
509 while (1) {
510 /* read process IDs & status for all the processes */
511 procps_status_t * p;
512
513#ifdef CONFIG_SELINUX
514 while ((p = procps_scan(0, 0, NULL) ) != 0) {
515#else
516 while ((p = procps_scan(0)) != 0) {
517#endif
518 int n = ntop;
519
520 top = xrealloc(top, (++ntop)*sizeof(procps_status_t));
521 memcpy(top + n, p, sizeof(procps_status_t));
522 }
523 if (ntop == 0) {
524 bb_perror_msg_and_die("scandir('/proc')");
525 }
526#ifdef FEATURE_CPU_USAGE_PERCENTAGE
527 if(!Hertz) {
528 init_Hertz_value();
529 do_stats();
530 sleep(1);
531 clearmems();
532 continue;
533 }
534 do_stats();
535#else
536 qsort(top, ntop, sizeof(procps_status_t), (void*)sort_function);
537#endif
538 opt = lines;
539 if (opt > ntop) {
540 opt = ntop;
541 }
542 /* show status for each of the processes */
543 display_status(opt, col);
544#if defined CONFIG_FEATURE_USE_TERMIOS
545 tv.tv_sec = interval;
546 tv.tv_usec = 0;
547 FD_ZERO (&readfds);
548 FD_SET (0, &readfds);
549 select (1, &readfds, NULL, NULL, &tv);
550 if (FD_ISSET (0, &readfds)) {
551 if (read (0, &c, 1) <= 0) { /* signal */
552 return EXIT_FAILURE;
553 }
554 if(c == 'q' || c == initial_settings.c_cc[VINTR])
555 return EXIT_SUCCESS;
556 if(c == 'M') {
557#ifdef FEATURE_CPU_USAGE_PERCENTAGE
558 sort_function[0] = mem_sort;
559 sort_function[1] = pcpu_sort;
560 sort_function[2] = time_sort;
561#else
562 sort_function = mem_sort;
563#endif
564 }
565#ifdef FEATURE_CPU_USAGE_PERCENTAGE
566 if(c == 'P') {
567 sort_function[0] = pcpu_sort;
568 sort_function[1] = mem_sort;
569 sort_function[2] = time_sort;
570 }
571 if(c == 'T') {
572 sort_function[0] = time_sort;
573 sort_function[1] = mem_sort;
574 sort_function[2] = pcpu_sort;
575 }
576#endif
577 if(c == 'N') {
578#ifdef FEATURE_CPU_USAGE_PERCENTAGE
579 sort_function[0] = pid_sort;
580#else
581 sort_function = pid_sort;
582#endif
583 }
584 }
585#else
586 sleep(interval);
587#endif /* CONFIG_FEATURE_USE_TERMIOS */
588 clearmems();
589 }
590
591 return EXIT_SUCCESS;
592}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini uptime implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* This version of uptime doesn't display the number of users on the system,
24 * since busybox init doesn't mess with utmp. For folks using utmp that are
25 * just dying to have # of users reported, feel free to write it as some type
26 * of CONFIG_FEATURE_UTMP_SUPPORT #define
27 */
28
29/* getopt not needed */
30
31
32#include <stdio.h>
33#include <time.h>
34#include <errno.h>
35#include <stdlib.h>
36#include "busybox.h"
37
38static const int FSHIFT = 16; /* nr of bits of precision */
39#define FIXED_1 (1<<FSHIFT) /* 1.0 as fixed-point */
40#define LOAD_INT(x) ((x) >> FSHIFT)
41#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
42
43
44extern int uptime_main(int argc, char **argv)
45{
46 int updays, uphours, upminutes;
47 struct sysinfo info;
48 struct tm *current_time;
49 time_t current_secs;
50
51 time(&current_secs);
52 current_time = localtime(&current_secs);
53
54 sysinfo(&info);
55
56 printf(" %02d:%02d:%02d up ",
57 current_time->tm_hour, current_time->tm_min, current_time->tm_sec);
58 updays = (int) info.uptime / (60*60*24);
59 if (updays)
60 printf("%d day%s, ", updays, (updays != 1) ? "s" : "");
61 upminutes = (int) info.uptime / 60;
62 uphours = (upminutes / 60) % 24;
63 upminutes %= 60;
64 if(uphours)
65 printf("%2d:%02d, ", uphours, upminutes);
66 else
67 printf("%d min, ", upminutes);
68
69 printf("load average: %ld.%02ld, %ld.%02ld, %ld.%02ld\n",
70 LOAD_INT(info.loads[0]), LOAD_FRAC(info.loads[0]),
71 LOAD_INT(info.loads[1]), LOAD_FRAC(info.loads[1]),
72 LOAD_INT(info.loads[2]), LOAD_FRAC(info.loads[2]));
73
74 return EXIT_SUCCESS;
75}
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 @@
1mkdep
2split-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 @@
1conf
2mconf
3lkc_defs.h
4lex.zconf.c
5zconf.tab.h
6zconf.tab.c
7lex.backup
8zconf.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 @@
1Introduction
2------------
3
4The configuration database is collection of configuration options
5organized in a tree structure:
6
7 +- Code maturity level options
8 | +- Prompt for development and/or incomplete code/drivers
9 +- General setup
10 | +- Networking support
11 | +- System V IPC
12 | +- BSD Process Accounting
13 | +- Sysctl support
14 +- Loadable module support
15 | +- Enable loadable module support
16 | +- Set version information on all module symbols
17 | +- Kernel module loader
18 +- ...
19
20Every entry has its own dependencies. These dependencies are used
21to determine the visible of an entry. Any child entry is only
22visible if its parent entry is also visible.
23
24Menu entries
25------------
26
27Most entries define a config option, all other entries help to organize
28them. A single configuration option is defined like this:
29
30config MODVERSIONS
31 bool "Set version information on all module symbols"
32 depends MODULES
33 help
34 Usually, modules have to be recompiled whenever you switch to a new
35 kernel. ...
36
37Every line starts with a key word and can be followed by multiple
38arguments. "config" starts a new config entry. The following lines
39define attributes for this config option. Attributes can be the type of
40the config option, input prompt, dependencies, help text and default
41values. A config option can be defined multiple times with the same
42name, but every definition can have only a single input prompt and the
43type must not conflict.
44
45Menu attributes
46---------------
47
48A menu entry can have a number of attributes. Not all of them are
49applicable everywhere (see syntax).
50
51- type definition: "bool"/"tristate"/"string"/"hex"/"integer"
52 Every config option must have a type. There are only two basic types:
53 tristate and string, the other types base on these two. The type
54 definition optionally accepts an input prompt, so these two examples
55 are equivalent:
56
57 bool "Networking support"
58 and
59 bool
60 prompt "Networking support"
61
62- input prompt: "prompt" <prompt> ["if" <expr>]
63 Every menu entry can have at most one prompt, which is used to display
64 to the user. Optionally dependencies only for this prompt can be added
65 with "if".
66
67- default value: "default" <symbol> ["if" <expr>]
68 A config option can have any number of default values. If multiple
69 default values are visible, only the first defined one is active.
70 Default values are not limited to the menu entry, where they are
71 defined, this means the default can be defined somewhere else or be
72 overriden by an earlier definition.
73 The default value is only assigned to the config symbol if no other
74 value was set by the user (via the input prompt above). If an input
75 prompt is visible the default value is presented to the user and can
76 be overridden by him.
77 Optionally dependencies only for this default value can be added with
78 "if".
79
80- dependencies: "depends on"/"requires" <expr>
81 This defines a dependency for this menu entry. If multiple
82 dependencies are defined they are connected with '&&'. Dependencies
83 are applied to all other options within this menu entry (which also
84 accept "if" expression), so these two examples are equivalent:
85
86 bool "foo" if BAR
87 default y if BAR
88 and
89 depends on BAR
90 bool "foo"
91 default y
92
93- help text: "help"
94 This defines a help text. The end of the help text is determined by
95 the level indentation, this means it ends at the first line which has
96 a smaller indentation than the first line of the help text.
97
98
99Menu dependencies
100-----------------
101
102Dependencies define the visibility of a menu entry and can also reduce
103the input range of tristate symbols. The tristate logic used in the
104expressions uses one more state than normal boolean logic to express the
105module state. Dependency expressions have the following syntax:
106
107<expr> ::= <symbol> (1)
108 <symbol> '=' <symbol> (2)
109 <symbol> '!=' <symbol> (3)
110 '(' <expr> ')' (4)
111 '!' <expr> (5)
112 <expr> '||' <expr> (6)
113 <expr> '&&' <expr> (7)
114
115Expressions are listed in decreasing order of precedence.
116
117(1) Convert the symbol into an expression. Boolean and tristate symbols
118 are simply converted into the respective expression values. All
119 other symbol types result in 'n'.
120(2) If the values of both symbols are equal, it returns 'y',
121 otherwise 'n'.
122(3) If the values of both symbols are equal, it returns 'n',
123 otherwise 'y'.
124(4) Returns the value of the expression. Used to override precedence.
125(5) Returns the result of (2-/expr/).
126(6) Returns the result of min(/expr/, /expr/).
127(7) Returns the result of max(/expr/, /expr/).
128
129An expression can have a value of 'n', 'm' or 'y' (or 0, 1, 2
130respectively for calculations). A menu entry becomes visible when it's
131expression evaluates to 'm' or 'y'.
132
133There are two type of symbols: constant and nonconstant symbols.
134Nonconstant symbols are the most common ones and are defined with the
135'config' statement. Nonconstant symbols consist entirely of alphanumeric
136characters or underscores.
137Constant symbols are only part of expressions. Constant symbols are
138always surrounded by single or double quotes. Within the quote any
139other character is allowed and the quotes can be escaped using '\'.
140
141Menu structure
142--------------
143
144The position of a menu entry in the tree is determined in two ways. First
145it can be specified explicitely:
146
147menu "Network device support"
148 depends NET
149
150config NETDEVICES
151 ...
152
153endmenu
154
155All entries within the "menu" ... "endmenu" block become a submenu of
156"Network device support". All subentries inherit the dependencies from
157the menu entry, e.g. this means the dependency "NET" is added to the
158dependency list of the config option NETDEVICES.
159
160The other way to generate the menu structure is done by analyzing the
161dependencies. If a menu entry somehow depends on the previous entry, it
162can be made a submenu of it. First the the previous (parent) symbol must
163be part of the dependency list and then one of these two condititions
164must be true:
165- the child entry must become invisible, if the parent is set to 'n'
166- the child entry must only be visible, if the parent is visible
167
168config MODULES
169 bool "Enable loadable module support"
170
171config MODVERSIONS
172 bool "Set version information on all module symbols"
173 depends MODULES
174
175comment "module support disabled"
176 depends !MODULES
177
178MODVERSIONS directly depends on MODULES, this means it's only visible if
179MODULES is different from 'n'. The comment on the other hand is always
180visible when MODULES it's visible (the (empty) dependency of MODULES is
181also part of the comment dependencies).
182
183
184Kconfig syntax
185--------------
186
187The configuration file describes a series of menu entries, where every
188line starts with a keyword (except help texts). The following keywords
189end a menu entry:
190- config
191- choice/endchoice
192- comment
193- menu/endmenu
194- if/endif
195- source
196The first four also start the definition of a menu entry.
197
198config:
199
200 "config" <symbol>
201 <config options>
202
203This defines a config symbol <symbol> and accepts any of above
204attributes as options.
205
206choices:
207
208 "choice"
209 <choice options>
210 <choice block>
211 "endchoice"
212
213This defines a choice group and accepts any of above attributes as
214options. A choice can only be of type bool or tristate, while a boolean
215choice only allows a single config entry to be selected, a tristate
216choice also allows any number of config entries to be set to 'm'. This
217can be used if multiple drivers for a single hardware exists and only a
218single driver can be compiled/loaded into the kernel, but all drivers
219can be compiled as modules.
220A choice accepts another option "optional", which allows to set the
221choice to 'n' and no entry needs to be selected.
222
223comment:
224
225 "comment" <prompt>
226 <comment options>
227
228This defines a comment which is displayed to the user during the
229configuration process and is also echoed to the output files. The only
230possible options are dependencies.
231
232menu:
233
234 "menu" <prompt>
235 <menu options>
236 <menu block>
237 "endmenu"
238
239This defines a menu block, see "Menu structure" above for more
240information. The only possible options are dependencies.
241
242if:
243
244 "if" <expr>
245 <if block>
246 "endif"
247
248This defines an if block. The dependency expression <expr> is appended
249to all enclosed menu entries.
250
251source:
252
253 "source" <prompt>
254
255This 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 @@
1# Makefile for BusyBox
2#
3# Copyright (C) 2002 Erik Andersen <andersen@codepoet.org>
4
5top_srcdir=../..
6top_builddir=../..
7srcdir=$(top_srcdir)/scripts/config
8include $(top_builddir)/Rules.mak
9
10all: ncurses conf mconf
11
12LIBS = -lncurses
13ifeq (/usr/include/ncurses/ncurses.h, $(wildcard /usr/include/ncurses/ncurses.h))
14 HOSTNCURSES += -I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"
15else
16ifeq (/usr/include/ncurses/curses.h, $(wildcard /usr/include/ncurses/curses.h))
17 HOSTNCURSES += -I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"
18else
19ifeq (/usr/local/include/ncurses/ncurses.h, $(wildcard /usr/local/include/ncurses/ncurses.h))
20 HOSTCFLAGS += -I/usr/local/include/ncurses -DCURSES_LOC="<ncurses.h>"
21else
22ifeq (/usr/local/include/ncurses/curses.h, $(wildcard /usr/local/include/ncurses/curses.h))
23 HOSTCFLAGS += -I/usr/local/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"
24else
25ifeq (/usr/include/ncurses.h, $(wildcard /usr/include/ncurses.h))
26 HOSTNCURSES += -DCURSES_LOC="<ncurses.h>"
27else
28 HOSTNCURSES += -DCURSES_LOC="<curses.h>"
29endif
30endif
31endif
32endif
33endif
34
35CONF_SRC =conf.c
36MCONF_SRC =mconf.c checklist.c menubox.c textbox.c yesno.c inputbox.c util.c msgbox.c
37SHARED_SRC=zconf.tab.c
38SHARED_DEPS:=$(srcdir)/lkc.h $(srcdir)/lkc_proto.h \
39 lkc_defs.h $(srcdir)/expr.h zconf.tab.h
40CONF_OBJS =$(patsubst %.c,%.o, $(CONF_SRC))
41MCONF_OBJS=$(patsubst %.c,%.o, $(MCONF_SRC))
42SHARED_OBJS=$(patsubst %.c,%.o, $(SHARED_SRC))
43
44conf: $(CONF_OBJS) $(SHARED_OBJS)
45 $(HOSTCC) $(NATIVE_LDFLAGS) $^ -o $@
46
47mconf: $(MCONF_OBJS) $(SHARED_OBJS)
48 $(HOSTCC) $(NATIVE_LDFLAGS) $^ -o $@ $(LIBS)
49
50$(CONF_OBJS): %.o : $(srcdir)/%.c $(SHARED_DEPS)
51 $(HOSTCC) $(HOSTCFLAGS) -I. -c $< -o $@
52
53$(MCONF_OBJS): %.o : $(srcdir)/%.c $(SHARED_DEPS)
54 $(HOSTCC) $(HOSTCFLAGS) $(HOSTNCURSES) -I. -c $< -o $@
55
56lkc_defs.h: $(srcdir)/lkc_proto.h
57 @sed < $< > $@ 's/P(\([^,]*\),.*/#define \1 (\*\1_p)/'
58
59###
60# The following requires flex/bison
61# By default we use the _shipped versions, uncomment the
62# following line if you are modifying the flex/bison src.
63#LKC_GENPARSER := 1
64
65ifdef LKC_GENPARSER
66
67%.tab.c %.tab.h: $(srcdir)/%.y
68 bison -t -d -v -b $* -p $(notdir $*) $<
69
70lex.%.c: $(srcdir)/%.l
71 flex -P$(notdir $*) -o$@ $<
72else
73
74lex.zconf.o: lex.zconf.c $(SHARED_DEPS)
75 $(HOSTCC) $(HOSTCFLAGS) -I$(srcdir) -c $< -o $@
76
77lex.zconf.c: $(srcdir)/lex.zconf.c_shipped
78 cp $< $@
79
80zconf.tab.c: $(srcdir)/zconf.tab.c_shipped
81 cp $< $@
82
83zconf.tab.h: $(srcdir)/zconf.tab.h_shipped
84 cp $< $@
85endif
86
87zconf.tab.o: zconf.tab.c lex.zconf.c $(srcdir)/confdata.c $(srcdir)/expr.c \
88 $(srcdir)/symbol.c $(srcdir)/menu.c $(SHARED_DEPS)
89 $(HOSTCC) $(HOSTCFLAGS) -I$(srcdir) -I. -c $< -o $@
90
91.PHONY: ncurses
92
93ncurses:
94 @echo "main() {}" > lxtemp.c
95 @if $(HOSTCC) lxtemp.c $(LIBS) ; then \
96 rm -f lxtemp.c a.out; \
97 else \
98 rm -f lxtemp.c; \
99 echo -e "\007" ;\
100 echo ">> Unable to find the Ncurses libraries." ;\
101 echo ">>" ;\
102 echo ">> You must have Ncurses installed in order" ;\
103 echo ">> to use 'make menuconfig'" ;\
104 echo ;\
105 exit 1 ;\
106 fi
107
108clean:
109 rm -f *.o *~ core $(TARGETS) $(MCONF_OBJS) $(CONF_OBJS) \
110 conf mconf zconf.tab.c zconf.tab.h lex.zconf.c lkc_defs.h
111
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 @@
1/*
2 * checklist.c -- implements the checklist box
3 *
4 * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
5 * Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension
6 * Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two
7 * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24#include "dialog.h"
25
26static int list_width, check_x, item_x, checkflag;
27
28/*
29 * Print list item
30 */
31static void
32print_item (WINDOW * win, const char *item, int status,
33 int choice, int selected)
34{
35 int i;
36
37 /* Clear 'residue' of last item */
38 wattrset (win, menubox_attr);
39 wmove (win, choice, 0);
40 for (i = 0; i < list_width; i++)
41 waddch (win, ' ');
42
43 wmove (win, choice, check_x);
44 wattrset (win, selected ? check_selected_attr : check_attr);
45 if (checkflag == FLAG_CHECK)
46 wprintw (win, "[%c]", status ? 'X' : ' ');
47 else
48 wprintw (win, "(%c)", status ? 'X' : ' ');
49
50 wattrset (win, selected ? tag_selected_attr : tag_attr);
51 mvwaddch(win, choice, item_x, item[0]);
52 wattrset (win, selected ? item_selected_attr : item_attr);
53 waddstr (win, (char *)item+1);
54 if (selected) {
55 wmove (win, choice, check_x+1);
56 wrefresh (win);
57 }
58}
59
60/*
61 * Print the scroll indicators.
62 */
63static void
64print_arrows (WINDOW * win, int choice, int item_no, int scroll,
65 int y, int x, int height)
66{
67 wmove(win, y, x);
68
69 if (scroll > 0) {
70 wattrset (win, uarrow_attr);
71 waddch (win, ACS_UARROW);
72 waddstr (win, "(-)");
73 }
74 else {
75 wattrset (win, menubox_attr);
76 waddch (win, ACS_HLINE);
77 waddch (win, ACS_HLINE);
78 waddch (win, ACS_HLINE);
79 waddch (win, ACS_HLINE);
80 }
81
82 y = y + height + 1;
83 wmove(win, y, x);
84
85 if ((height < item_no) && (scroll + choice < item_no - 1)) {
86 wattrset (win, darrow_attr);
87 waddch (win, ACS_DARROW);
88 waddstr (win, "(+)");
89 }
90 else {
91 wattrset (win, menubox_border_attr);
92 waddch (win, ACS_HLINE);
93 waddch (win, ACS_HLINE);
94 waddch (win, ACS_HLINE);
95 waddch (win, ACS_HLINE);
96 }
97}
98
99/*
100 * Display the termination buttons
101 */
102static void
103print_buttons( WINDOW *dialog, int height, int width, int selected)
104{
105 int x = width / 2 - 11;
106 int y = height - 2;
107
108 print_button (dialog, "Select", y, x, selected == 0);
109 print_button (dialog, " Help ", y, x + 14, selected == 1);
110
111 wmove(dialog, y, x+1 + 14*selected);
112 wrefresh (dialog);
113}
114
115/*
116 * Display a dialog box with a list of options that can be turned on or off
117 * The `flag' parameter is used to select between radiolist and checklist.
118 */
119int
120dialog_checklist (const char *title, const char *prompt, int height, int width,
121 int list_height, int item_no, struct dialog_list_item ** items,
122 int flag)
123
124{
125 int i, x, y, box_x, box_y;
126 int key = 0, button = 0, choice = 0, scroll = 0, max_choice, *status;
127 WINDOW *dialog, *list;
128
129 checkflag = flag;
130
131 /* Allocate space for storing item on/off status */
132 if ((status = malloc (sizeof (int) * item_no)) == NULL) {
133 endwin ();
134 fprintf (stderr,
135 "\nCan't allocate memory in dialog_checklist().\n");
136 exit (-1);
137 }
138
139 /* Initializes status */
140 for (i = 0; i < item_no; i++) {
141 status[i] = (items[i]->selected == 1); /* ON */
142 if ((!choice && status[i]) || items[i]->selected == 2) /* SELECTED */
143 choice = i + 1;
144 }
145 if (choice)
146 choice--;
147
148 max_choice = MIN (list_height, item_no);
149
150 /* center dialog box on screen */
151 x = (COLS - width) / 2;
152 y = (LINES - height) / 2;
153
154 draw_shadow (stdscr, y, x, height, width);
155
156 dialog = newwin (height, width, y, x);
157 keypad (dialog, TRUE);
158
159 draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
160 wattrset (dialog, border_attr);
161 mvwaddch (dialog, height-3, 0, ACS_LTEE);
162 for (i = 0; i < width - 2; i++)
163 waddch (dialog, ACS_HLINE);
164 wattrset (dialog, dialog_attr);
165 waddch (dialog, ACS_RTEE);
166
167 if (title != NULL && strlen(title) >= width-2 ) {
168 /* truncate long title -- mec */
169 char * title2 = malloc(width-2+1);
170 memcpy( title2, title, width-2 );
171 title2[width-2] = '\0';
172 title = title2;
173 }
174
175 if (title != NULL) {
176 wattrset (dialog, title_attr);
177 mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
178 waddstr (dialog, (char *)title);
179 waddch (dialog, ' ');
180 }
181
182 wattrset (dialog, dialog_attr);
183 print_autowrap (dialog, prompt, width - 2, 1, 3);
184
185 list_width = width - 6;
186 box_y = height - list_height - 5;
187 box_x = (width - list_width) / 2 - 1;
188
189 /* create new window for the list */
190 list = subwin (dialog, list_height, list_width, y+box_y+1, x+box_x+1);
191
192 keypad (list, TRUE);
193
194 /* draw a box around the list items */
195 draw_box (dialog, box_y, box_x, list_height + 2, list_width + 2,
196 menubox_border_attr, menubox_attr);
197
198 /* Find length of longest item in order to center checklist */
199 check_x = 0;
200 for (i = 0; i < item_no; i++)
201 check_x = MAX (check_x, + strlen (items[i]->name) + 4);
202
203 check_x = (list_width - check_x) / 2;
204 item_x = check_x + 4;
205
206 if (choice >= list_height) {
207 scroll = choice - list_height + 1;
208 choice -= scroll;
209 }
210
211 /* Print the list */
212 for (i = 0; i < max_choice; i++) {
213 print_item (list, items[scroll + i]->name,
214 status[i+scroll], i, i == choice);
215 }
216
217 print_arrows(dialog, choice, item_no, scroll,
218 box_y, box_x + check_x + 5, list_height);
219
220 print_buttons(dialog, height, width, 0);
221
222 wnoutrefresh (list);
223 wnoutrefresh (dialog);
224 doupdate ();
225
226 while (key != ESC) {
227 key = wgetch (dialog);
228
229 for (i = 0; i < max_choice; i++)
230 if (toupper(key) == toupper(items[scroll + i]->name[0]))
231 break;
232
233
234 if ( i < max_choice || key == KEY_UP || key == KEY_DOWN ||
235 key == '+' || key == '-' ) {
236 if (key == KEY_UP || key == '-') {
237 if (!choice) {
238 if (!scroll)
239 continue;
240 /* Scroll list down */
241 if (list_height > 1) {
242 /* De-highlight current first item */
243 print_item (list, items[scroll]->name,
244 status[scroll], 0, FALSE);
245 scrollok (list, TRUE);
246 wscrl (list, -1);
247 scrollok (list, FALSE);
248 }
249 scroll--;
250 print_item (list, items[scroll]->name,
251 status[scroll], 0, TRUE);
252 wnoutrefresh (list);
253
254 print_arrows(dialog, choice, item_no, scroll,
255 box_y, box_x + check_x + 5, list_height);
256
257 wrefresh (dialog);
258
259 continue; /* wait for another key press */
260 } else
261 i = choice - 1;
262 } else if (key == KEY_DOWN || key == '+') {
263 if (choice == max_choice - 1) {
264 if (scroll + choice >= item_no - 1)
265 continue;
266 /* Scroll list up */
267 if (list_height > 1) {
268 /* De-highlight current last item before scrolling up */
269 print_item (list, items[scroll + max_choice - 1]->name,
270 status[scroll + max_choice - 1],
271 max_choice - 1, FALSE);
272 scrollok (list, TRUE);
273 scroll (list);
274 scrollok (list, FALSE);
275 }
276 scroll++;
277 print_item (list, items[scroll + max_choice - 1]->name,
278 status[scroll + max_choice - 1],
279 max_choice - 1, TRUE);
280 wnoutrefresh (list);
281
282 print_arrows(dialog, choice, item_no, scroll,
283 box_y, box_x + check_x + 5, list_height);
284
285 wrefresh (dialog);
286
287 continue; /* wait for another key press */
288 } else
289 i = choice + 1;
290 }
291 if (i != choice) {
292 /* De-highlight current item */
293 print_item (list, items[scroll + choice]->name,
294 status[scroll + choice], choice, FALSE);
295 /* Highlight new item */
296 choice = i;
297 print_item (list, items[scroll + choice]->name,
298 status[scroll + choice], choice, TRUE);
299 wnoutrefresh (list);
300 wrefresh (dialog);
301 }
302 continue; /* wait for another key press */
303 }
304 switch (key) {
305 case 'H':
306 case 'h':
307 case '?':
308 for (i = 0; i < item_no; i++)
309 items[i]->selected = 0;
310 items[scroll + choice]->selected = 1;
311 delwin (dialog);
312 free (status);
313 return 1;
314 case TAB:
315 case KEY_LEFT:
316 case KEY_RIGHT:
317 button = ((key == KEY_LEFT ? --button : ++button) < 0)
318 ? 1 : (button > 1 ? 0 : button);
319
320 print_buttons(dialog, height, width, button);
321 wrefresh (dialog);
322 break;
323 case 'S':
324 case 's':
325 case ' ':
326 case '\n':
327 if (!button) {
328 if (flag == FLAG_CHECK) {
329 status[scroll + choice] = !status[scroll + choice];
330 wmove (list, choice, check_x);
331 wattrset (list, check_selected_attr);
332 wprintw (list, "[%c]", status[scroll + choice] ? 'X' : ' ');
333 } else {
334 if (!status[scroll + choice]) {
335 for (i = 0; i < item_no; i++)
336 status[i] = 0;
337 status[scroll + choice] = 1;
338 for (i = 0; i < max_choice; i++)
339 print_item (list, items[scroll + i]->name,
340 status[scroll + i], i, i == choice);
341 }
342 }
343 wnoutrefresh (list);
344 wrefresh (dialog);
345
346 for (i = 0; i < item_no; i++) {
347 items[i]->selected = status[i];
348 }
349 } else {
350 for (i = 0; i < item_no; i++)
351 items[i]->selected = 0;
352 items[scroll + choice]->selected = 1;
353 }
354 delwin (dialog);
355 free (status);
356 return button;
357 case 'X':
358 case 'x':
359 key = ESC;
360 case ESC:
361 break;
362 }
363
364 /* Now, update everything... */
365 doupdate ();
366 }
367
368
369 delwin (dialog);
370 free (status);
371 return -1; /* ESC pressed */
372}
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 @@
1/*
2 * colors.h -- color attribute definitions
3 *
4 * AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21
22/*
23 * Default color definitions
24 *
25 * *_FG = foreground
26 * *_BG = background
27 * *_HL = highlight?
28 */
29#define SCREEN_FG COLOR_CYAN
30#define SCREEN_BG COLOR_BLUE
31#define SCREEN_HL TRUE
32
33#define SHADOW_FG COLOR_BLACK
34#define SHADOW_BG COLOR_BLACK
35#define SHADOW_HL TRUE
36
37#define DIALOG_FG COLOR_BLACK
38#define DIALOG_BG COLOR_WHITE
39#define DIALOG_HL FALSE
40
41#define TITLE_FG COLOR_YELLOW
42#define TITLE_BG COLOR_WHITE
43#define TITLE_HL TRUE
44
45#define BORDER_FG COLOR_WHITE
46#define BORDER_BG COLOR_WHITE
47#define BORDER_HL TRUE
48
49#define BUTTON_ACTIVE_FG COLOR_WHITE
50#define BUTTON_ACTIVE_BG COLOR_BLUE
51#define BUTTON_ACTIVE_HL TRUE
52
53#define BUTTON_INACTIVE_FG COLOR_BLACK
54#define BUTTON_INACTIVE_BG COLOR_WHITE
55#define BUTTON_INACTIVE_HL FALSE
56
57#define BUTTON_KEY_ACTIVE_FG COLOR_WHITE
58#define BUTTON_KEY_ACTIVE_BG COLOR_BLUE
59#define BUTTON_KEY_ACTIVE_HL TRUE
60
61#define BUTTON_KEY_INACTIVE_FG COLOR_RED
62#define BUTTON_KEY_INACTIVE_BG COLOR_WHITE
63#define BUTTON_KEY_INACTIVE_HL FALSE
64
65#define BUTTON_LABEL_ACTIVE_FG COLOR_YELLOW
66#define BUTTON_LABEL_ACTIVE_BG COLOR_BLUE
67#define BUTTON_LABEL_ACTIVE_HL TRUE
68
69#define BUTTON_LABEL_INACTIVE_FG COLOR_BLACK
70#define BUTTON_LABEL_INACTIVE_BG COLOR_WHITE
71#define BUTTON_LABEL_INACTIVE_HL TRUE
72
73#define INPUTBOX_FG COLOR_BLACK
74#define INPUTBOX_BG COLOR_WHITE
75#define INPUTBOX_HL FALSE
76
77#define INPUTBOX_BORDER_FG COLOR_BLACK
78#define INPUTBOX_BORDER_BG COLOR_WHITE
79#define INPUTBOX_BORDER_HL FALSE
80
81#define SEARCHBOX_FG COLOR_BLACK
82#define SEARCHBOX_BG COLOR_WHITE
83#define SEARCHBOX_HL FALSE
84
85#define SEARCHBOX_TITLE_FG COLOR_YELLOW
86#define SEARCHBOX_TITLE_BG COLOR_WHITE
87#define SEARCHBOX_TITLE_HL TRUE
88
89#define SEARCHBOX_BORDER_FG COLOR_WHITE
90#define SEARCHBOX_BORDER_BG COLOR_WHITE
91#define SEARCHBOX_BORDER_HL TRUE
92
93#define POSITION_INDICATOR_FG COLOR_YELLOW
94#define POSITION_INDICATOR_BG COLOR_WHITE
95#define POSITION_INDICATOR_HL TRUE
96
97#define MENUBOX_FG COLOR_BLACK
98#define MENUBOX_BG COLOR_WHITE
99#define MENUBOX_HL FALSE
100
101#define MENUBOX_BORDER_FG COLOR_WHITE
102#define MENUBOX_BORDER_BG COLOR_WHITE
103#define MENUBOX_BORDER_HL TRUE
104
105#define ITEM_FG COLOR_BLACK
106#define ITEM_BG COLOR_WHITE
107#define ITEM_HL FALSE
108
109#define ITEM_SELECTED_FG COLOR_WHITE
110#define ITEM_SELECTED_BG COLOR_BLUE
111#define ITEM_SELECTED_HL TRUE
112
113#define TAG_FG COLOR_YELLOW
114#define TAG_BG COLOR_WHITE
115#define TAG_HL TRUE
116
117#define TAG_SELECTED_FG COLOR_YELLOW
118#define TAG_SELECTED_BG COLOR_BLUE
119#define TAG_SELECTED_HL TRUE
120
121#define TAG_KEY_FG COLOR_YELLOW
122#define TAG_KEY_BG COLOR_WHITE
123#define TAG_KEY_HL TRUE
124
125#define TAG_KEY_SELECTED_FG COLOR_YELLOW
126#define TAG_KEY_SELECTED_BG COLOR_BLUE
127#define TAG_KEY_SELECTED_HL TRUE
128
129#define CHECK_FG COLOR_BLACK
130#define CHECK_BG COLOR_WHITE
131#define CHECK_HL FALSE
132
133#define CHECK_SELECTED_FG COLOR_WHITE
134#define CHECK_SELECTED_BG COLOR_BLUE
135#define CHECK_SELECTED_HL TRUE
136
137#define UARROW_FG COLOR_GREEN
138#define UARROW_BG COLOR_WHITE
139#define UARROW_HL TRUE
140
141#define DARROW_FG COLOR_GREEN
142#define DARROW_BG COLOR_WHITE
143#define DARROW_HL TRUE
144
145/* End of default color definitions */
146
147#define C_ATTR(x,y) ((x ? A_BOLD : 0) | COLOR_PAIR((y)))
148#define COLOR_NAME_LEN 10
149#define COLOR_COUNT 8
150
151/*
152 * Global variables
153 */
154
155typedef struct {
156 char name[COLOR_NAME_LEN];
157 int value;
158} color_names_st;
159
160extern color_names_st color_names[];
161extern 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 @@
1/*
2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
4 */
5
6#include <ctype.h>
7#include <stdlib.h>
8#include <string.h>
9#include <unistd.h>
10#include <time.h>
11#include <sys/stat.h>
12
13#define LKC_DIRECT_LINK
14#include "lkc.h"
15
16static void conf(struct menu *menu);
17static void check_conf(struct menu *menu);
18
19enum {
20 ask_all,
21 ask_new,
22 ask_silent,
23 set_default,
24 set_yes,
25 set_mod,
26 set_no,
27 set_random
28} input_mode = ask_all;
29char *defconfig_file;
30
31static int indent = 1;
32static int valid_stdin = 1;
33static int conf_cnt;
34static char line[128];
35static struct menu *rootEntry;
36
37static char nohelp_text[] = "Sorry, no help available for this option yet.\n";
38
39static void strip(char *str)
40{
41 char *p = str;
42 int l;
43
44 while ((isspace(*p)))
45 p++;
46 l = strlen(p);
47 if (p != str)
48 memmove(str, p, l + 1);
49 if (!l)
50 return;
51 p = str + l - 1;
52 while ((isspace(*p)))
53 *p-- = 0;
54}
55
56static void check_stdin(void)
57{
58 if (!valid_stdin && input_mode == ask_silent) {
59 printf("aborted!\n\n");
60 printf("Console input/output is redirected. ");
61 printf("Run 'make oldconfig' to update configuration.\n\n");
62 exit(1);
63 }
64}
65
66static void conf_askvalue(struct symbol *sym, const char *def)
67{
68 enum symbol_type type = sym_get_type(sym);
69 tristate val;
70
71 if (!sym_has_value(sym))
72 printf("(NEW) ");
73
74 line[0] = '\n';
75 line[1] = 0;
76
77 if (!sym_is_changable(sym)) {
78 printf("%s\n", def);
79 line[0] = '\n';
80 line[1] = 0;
81 return;
82 }
83
84 switch (input_mode) {
85 case ask_new:
86 case ask_silent:
87 if (sym_has_value(sym)) {
88 printf("%s\n", def);
89 return;
90 }
91 check_stdin();
92 case ask_all:
93 fflush(stdout);
94 fgets(line, 128, stdin);
95 return;
96 case set_default:
97 printf("%s\n", def);
98 return;
99 default:
100 break;
101 }
102
103 switch (type) {
104 case S_INT:
105 case S_HEX:
106 case S_STRING:
107 printf("%s\n", def);
108 return;
109 default:
110 ;
111 }
112 switch (input_mode) {
113 case set_yes:
114 if (sym_tristate_within_range(sym, yes)) {
115 line[0] = 'y';
116 line[1] = '\n';
117 line[2] = 0;
118 break;
119 }
120 case set_mod:
121 if (type == S_TRISTATE) {
122 if (sym_tristate_within_range(sym, mod)) {
123 line[0] = 'm';
124 line[1] = '\n';
125 line[2] = 0;
126 break;
127 }
128 } else {
129 if (sym_tristate_within_range(sym, yes)) {
130 line[0] = 'y';
131 line[1] = '\n';
132 line[2] = 0;
133 break;
134 }
135 }
136 case set_no:
137 if (sym_tristate_within_range(sym, no)) {
138 line[0] = 'n';
139 line[1] = '\n';
140 line[2] = 0;
141 break;
142 }
143 case set_random:
144 do {
145 val = (tristate)(random() % 3);
146 } while (!sym_tristate_within_range(sym, val));
147 switch (val) {
148 case no: line[0] = 'n'; break;
149 case mod: line[0] = 'm'; break;
150 case yes: line[0] = 'y'; break;
151 }
152 line[1] = '\n';
153 line[2] = 0;
154 break;
155 default:
156 break;
157 }
158 printf("%s", line);
159}
160
161int conf_string(struct menu *menu)
162{
163 struct symbol *sym = menu->sym;
164 const char *def, *help;
165
166 while (1) {
167 printf("%*s%s ", indent - 1, "", menu->prompt->text);
168 printf("(%s) ", sym->name);
169 def = sym_get_string_value(sym);
170 if (sym_get_string_value(sym))
171 printf("[%s] ", def);
172 conf_askvalue(sym, def);
173 switch (line[0]) {
174 case '\n':
175 break;
176 case '?':
177 /* print help */
178 if (line[1] == '\n') {
179 help = nohelp_text;
180 if (menu->sym->help)
181 help = menu->sym->help;
182 printf("\n%s\n", menu->sym->help);
183 def = NULL;
184 break;
185 }
186 default:
187 line[strlen(line)-1] = 0;
188 def = line;
189 }
190 if (def && sym_set_string_value(sym, def))
191 return 0;
192 }
193}
194
195static int conf_sym(struct menu *menu)
196{
197 struct symbol *sym = menu->sym;
198 int type;
199 tristate oldval, newval;
200 const char *help;
201
202 while (1) {
203 printf("%*s%s ", indent - 1, "", menu->prompt->text);
204 if (sym->name)
205 printf("(%s) ", sym->name);
206 type = sym_get_type(sym);
207 putchar('[');
208 oldval = sym_get_tristate_value(sym);
209 switch (oldval) {
210 case no:
211 putchar('N');
212 break;
213 case mod:
214 putchar('M');
215 break;
216 case yes:
217 putchar('Y');
218 break;
219 }
220 if (oldval != no && sym_tristate_within_range(sym, no))
221 printf("/n");
222 if (oldval != mod && sym_tristate_within_range(sym, mod))
223 printf("/m");
224 if (oldval != yes && sym_tristate_within_range(sym, yes))
225 printf("/y");
226 if (sym->help)
227 printf("/?");
228 printf("] ");
229 conf_askvalue(sym, sym_get_string_value(sym));
230 strip(line);
231
232 switch (line[0]) {
233 case 'n':
234 case 'N':
235 newval = no;
236 if (!line[1] || !strcmp(&line[1], "o"))
237 break;
238 continue;
239 case 'm':
240 case 'M':
241 newval = mod;
242 if (!line[1])
243 break;
244 continue;
245 case 'y':
246 case 'Y':
247 newval = yes;
248 if (!line[1] || !strcmp(&line[1], "es"))
249 break;
250 continue;
251 case 0:
252 newval = oldval;
253 break;
254 case '?':
255 goto help;
256 default:
257 continue;
258 }
259 if (sym_set_tristate_value(sym, newval))
260 return 0;
261help:
262 help = nohelp_text;
263 if (sym->help)
264 help = sym->help;
265 printf("\n%s\n", help);
266 }
267}
268
269static int conf_choice(struct menu *menu)
270{
271 struct symbol *sym, *def_sym;
272 struct menu *child;
273 int type;
274 bool is_new;
275
276 sym = menu->sym;
277 type = sym_get_type(sym);
278 is_new = !sym_has_value(sym);
279 if (sym_is_changable(sym)) {
280 conf_sym(menu);
281 sym_calc_value(sym);
282 switch (sym_get_tristate_value(sym)) {
283 case no:
284 return 1;
285 case mod:
286 return 0;
287 case yes:
288 break;
289 }
290 } else {
291 switch (sym_get_tristate_value(sym)) {
292 case no:
293 return 1;
294 case mod:
295 printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
296 return 0;
297 case yes:
298 break;
299 }
300 }
301
302 while (1) {
303 int cnt, def;
304
305 printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
306 def_sym = sym_get_choice_value(sym);
307 cnt = def = 0;
308 line[0] = '0';
309 line[1] = 0;
310 for (child = menu->list; child; child = child->next) {
311 if (!menu_is_visible(child))
312 continue;
313 if (!child->sym) {
314 printf("%*c %s\n", indent, '*', menu_get_prompt(child));
315 continue;
316 }
317 cnt++;
318 if (child->sym == def_sym) {
319 def = cnt;
320 printf("%*c", indent, '>');
321 } else
322 printf("%*c", indent, ' ');
323 printf(" %d. %s", cnt, menu_get_prompt(child));
324 if (child->sym->name)
325 printf(" (%s)", child->sym->name);
326 if (!sym_has_value(child->sym))
327 printf(" (NEW)");
328 printf("\n");
329 }
330 printf("%*schoice", indent - 1, "");
331 if (cnt == 1) {
332 printf("[1]: 1\n");
333 goto conf_childs;
334 }
335 printf("[1-%d", cnt);
336 if (sym->help)
337 printf("?");
338 printf("]: ");
339 switch (input_mode) {
340 case ask_new:
341 case ask_silent:
342 if (!is_new) {
343 cnt = def;
344 printf("%d\n", cnt);
345 break;
346 }
347 check_stdin();
348 case ask_all:
349 fflush(stdout);
350 fgets(line, 128, stdin);
351 strip(line);
352 if (line[0] == '?') {
353 printf("\n%s\n", menu->sym->help ?
354 menu->sym->help : nohelp_text);
355 continue;
356 }
357 if (!line[0])
358 cnt = def;
359 else if (isdigit(line[0]))
360 cnt = atoi(line);
361 else
362 continue;
363 break;
364 case set_random:
365 def = (random() % cnt) + 1;
366 case set_default:
367 case set_yes:
368 case set_mod:
369 case set_no:
370 cnt = def;
371 printf("%d\n", cnt);
372 break;
373 }
374
375 conf_childs:
376 for (child = menu->list; child; child = child->next) {
377 if (!child->sym || !menu_is_visible(child))
378 continue;
379 if (!--cnt)
380 break;
381 }
382 if (!child)
383 continue;
384 if (line[strlen(line) - 1] == '?') {
385 printf("\n%s\n", child->sym->help ?
386 child->sym->help : nohelp_text);
387 continue;
388 }
389 sym_set_choice_value(sym, child->sym);
390 if (child->list) {
391 indent += 2;
392 conf(child->list);
393 indent -= 2;
394 }
395 return 1;
396 }
397}
398
399static void conf(struct menu *menu)
400{
401 struct symbol *sym;
402 struct property *prop;
403 struct menu *child;
404
405 if (!menu_is_visible(menu))
406 return;
407
408 sym = menu->sym;
409 prop = menu->prompt;
410 if (prop) {
411 const char *prompt;
412
413 switch (prop->type) {
414 case P_MENU:
415 if (input_mode == ask_silent && rootEntry != menu) {
416 check_conf(menu);
417 return;
418 }
419 case P_COMMENT:
420 prompt = menu_get_prompt(menu);
421 if (prompt)
422 printf("%*c\n%*c %s\n%*c\n",
423 indent, '*',
424 indent, '*', prompt,
425 indent, '*');
426 default:
427 ;
428 }
429 }
430
431 if (!sym)
432 goto conf_childs;
433
434 if (sym_is_choice(sym)) {
435 conf_choice(menu);
436 if (sym->curr.tri != mod)
437 return;
438 goto conf_childs;
439 }
440
441 switch (sym->type) {
442 case S_INT:
443 case S_HEX:
444 case S_STRING:
445 conf_string(menu);
446 break;
447 default:
448 conf_sym(menu);
449 break;
450 }
451
452conf_childs:
453 if (sym)
454 indent += 2;
455 for (child = menu->list; child; child = child->next)
456 conf(child);
457 if (sym)
458 indent -= 2;
459}
460
461static void check_conf(struct menu *menu)
462{
463 struct symbol *sym;
464 struct menu *child;
465
466 if (!menu_is_visible(menu))
467 return;
468
469 sym = menu->sym;
470 if (sym) {
471 if (sym_is_changable(sym) && !sym_has_value(sym)) {
472 if (!conf_cnt++)
473 printf("*\n* Restart config...\n*\n");
474 rootEntry = menu_get_parent_menu(menu);
475 conf(rootEntry);
476 }
477 if (sym_is_choice(sym) && sym_get_tristate_value(sym) != mod)
478 return;
479 }
480
481 for (child = menu->list; child; child = child->next)
482 check_conf(child);
483}
484
485int main(int ac, char **av)
486{
487 int i = 1;
488 const char *name;
489 struct stat tmpstat;
490
491 if (ac > i && av[i][0] == '-') {
492 switch (av[i++][1]) {
493 case 'o':
494 input_mode = ask_new;
495 break;
496 case 's':
497 input_mode = ask_silent;
498 valid_stdin = isatty(0) && isatty(1) && isatty(2);
499 break;
500 case 'd':
501 input_mode = set_default;
502 break;
503 case 'D':
504 input_mode = set_default;
505 defconfig_file = av[i++];
506 if (!defconfig_file) {
507 printf("%s: No default config file specified\n",
508 av[0]);
509 exit(1);
510 }
511 break;
512 case 'n':
513 input_mode = set_no;
514 break;
515 case 'm':
516 input_mode = set_mod;
517 break;
518 case 'y':
519 input_mode = set_yes;
520 break;
521 case 'r':
522 input_mode = set_random;
523 srandom(time(NULL));
524 break;
525 case 'h':
526 case '?':
527 printf("%s [-o|-s] config\n", av[0]);
528 exit(0);
529 }
530 }
531 name = av[i];
532 if (!name) {
533 printf("%s: configuration file missing\n", av[0]);
534 }
535 conf_parse(name);
536 //zconfdump(stdout);
537 switch (input_mode) {
538 case set_default:
539 if (!defconfig_file)
540 defconfig_file = conf_get_default_confname();
541 if (conf_read(defconfig_file)) {
542 printf("***\n"
543 "*** Can't find default configuration \"%s\"!\n"
544 "***\n", defconfig_file);
545 exit(1);
546 }
547 break;
548 case ask_silent:
549 if (stat(".config", &tmpstat)) {
550 printf("***\n"
551 "*** You have not yet configured BusyBox!\n"
552 "***\n"
553 "*** Please run some configurator (e.g. \"make oldconfig\" or\n"
554 "*** \"make menuconfig\" or \"make config\").\n"
555 "***\n");
556 exit(1);
557 }
558 case ask_all:
559 case ask_new:
560 conf_read(NULL);
561 break;
562 default:
563 break;
564 }
565
566 if (input_mode != ask_silent) {
567 rootEntry = &rootmenu;
568 conf(&rootmenu);
569 if (input_mode == ask_all) {
570 input_mode = ask_silent;
571 valid_stdin = 1;
572 }
573 }
574 do {
575 conf_cnt = 0;
576 check_conf(&rootmenu);
577 } while (conf_cnt);
578 if (conf_write(NULL)) {
579 fprintf(stderr, "\n*** Error during writing of the BusyBox configuration.\n\n");
580 return 1;
581 }
582 return 0;
583}
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 @@
1/*
2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
4 */
5
6#include <sys/stat.h>
7#include <ctype.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <unistd.h>
12
13#define LKC_DIRECT_LINK
14#include "lkc.h"
15
16const char conf_def_filename[] = ".config";
17
18const char conf_defname[] = "sysdeps/linux/defconfig";
19
20const char *conf_confnames[] = {
21 ".config",
22 conf_defname,
23 NULL,
24};
25
26static char *conf_expand_value(const char *in)
27{
28 struct symbol *sym;
29 const char *src;
30 static char res_value[SYMBOL_MAXLENGTH];
31 char *dst, name[SYMBOL_MAXLENGTH];
32
33 res_value[0] = 0;
34 dst = name;
35 while ((src = strchr(in, '$'))) {
36 strncat(res_value, in, src - in);
37 src++;
38 dst = name;
39 while (isalnum(*src) || *src == '_')
40 *dst++ = *src++;
41 *dst = 0;
42 sym = sym_lookup(name, 0);
43 sym_calc_value(sym);
44 strcat(res_value, sym_get_string_value(sym));
45 in = src;
46 }
47 strcat(res_value, in);
48
49 return res_value;
50}
51
52char *conf_get_default_confname(void)
53{
54 struct stat buf;
55 static char fullname[PATH_MAX+1];
56 char *env, *name;
57
58 name = conf_expand_value(conf_defname);
59 env = getenv(SRCTREE);
60 if (env) {
61 sprintf(fullname, "%s/%s", env, name);
62 if (!stat(fullname, &buf))
63 return fullname;
64 }
65 return name;
66}
67
68int conf_read(const char *name)
69{
70 FILE *in = NULL;
71 char line[1024];
72 char *p, *p2;
73 int lineno = 0;
74 struct symbol *sym;
75 struct property *prop;
76 struct expr *e;
77 int i;
78
79 if (name) {
80 in = zconf_fopen(name);
81 } else {
82 const char **names = conf_confnames;
83 while ((name = *names++)) {
84 name = conf_expand_value(name);
85 in = zconf_fopen(name);
86 if (in) {
87 printf("#\n"
88 "# using defaults found in %s\n"
89 "#\n", name);
90 break;
91 }
92 }
93 }
94
95 if (!in)
96 return 1;
97
98 for_all_symbols(i, sym) {
99 sym->flags |= SYMBOL_NEW | SYMBOL_CHANGED;
100 sym->flags &= ~SYMBOL_VALID;
101 switch (sym->type) {
102 case S_INT:
103 case S_HEX:
104 case S_STRING:
105 if (sym->user.val)
106 free(sym->user.val);
107 default:
108 sym->user.val = NULL;
109 sym->user.tri = no;
110 }
111 }
112
113 while (fgets(line, sizeof(line), in)) {
114 lineno++;
115 sym = NULL;
116 switch (line[0]) {
117 case '#':
118 if (line[1]!=' ')
119 continue;
120 p = strchr(line + 2, ' ');
121 if (!p)
122 continue;
123 *p++ = 0;
124 if (strncmp(p, "is not set", 10))
125 continue;
126 sym = sym_find(line + 2);
127 if (!sym) {
128 fprintf(stderr, "%s:%d: trying to assign nonexistent symbol %s\n", name, lineno, line + 2);
129 break;
130 }
131 switch (sym->type) {
132 case S_BOOLEAN:
133 case S_TRISTATE:
134 sym->user.tri = no;
135 sym->flags &= ~SYMBOL_NEW;
136 break;
137 default:
138 ;
139 }
140 break;
141
142 case 'A' ... 'Z':
143 p = strchr(line, '=');
144 if (!p)
145 continue;
146 *p++ = 0;
147 p2 = strchr(p, '\n');
148 if (p2)
149 *p2 = 0;
150 sym = sym_find(line);
151 if (!sym) {
152 fprintf(stderr, "%s:%d: trying to assign nonexistent symbol %s\n", name, lineno, line);
153 break;
154 }
155 switch (sym->type) {
156 case S_TRISTATE:
157 if (p[0] == 'm') {
158 sym->user.tri = mod;
159 sym->flags &= ~SYMBOL_NEW;
160 break;
161 }
162 case S_BOOLEAN:
163 if (p[0] == 'y') {
164 sym->user.tri = yes;
165 sym->flags &= ~SYMBOL_NEW;
166 break;
167 }
168 if (p[0] == 'n') {
169 sym->user.tri = no;
170 sym->flags &= ~SYMBOL_NEW;
171 break;
172 }
173 break;
174 case S_STRING:
175 if (*p++ != '"')
176 break;
177 for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) {
178 if (*p2 == '"') {
179 *p2 = 0;
180 break;
181 }
182 memmove(p2, p2 + 1, strlen(p2));
183 }
184 if (!p2) {
185 fprintf(stderr, "%s:%d: invalid string found\n", name, lineno);
186 exit(1);
187 }
188 case S_INT:
189 case S_HEX:
190 if (sym_string_valid(sym, p)) {
191 sym->user.val = strdup(p);
192 sym->flags &= ~SYMBOL_NEW;
193 } else {
194 fprintf(stderr, "%s:%d: symbol value '%s' invalid for %s\n", name, lineno, p, sym->name);
195 exit(1);
196 }
197 break;
198 default:
199 ;
200 }
201 break;
202 case '\n':
203 break;
204 default:
205 continue;
206 }
207 if (sym && sym_is_choice_value(sym)) {
208 struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
209 switch (sym->user.tri) {
210 case no:
211 break;
212 case mod:
213 if (cs->user.tri == yes)
214 /* warn? */;
215 break;
216 case yes:
217 if (cs->user.tri != no)
218 /* warn? */;
219 cs->user.val = sym;
220 break;
221 }
222 cs->user.tri = E_OR(cs->user.tri, sym->user.tri);
223 cs->flags &= ~SYMBOL_NEW;
224 }
225 }
226 fclose(in);
227
228 if (modules_sym)
229 sym_calc_value(modules_sym);
230 for_all_symbols(i, sym) {
231 sym_calc_value(sym);
232 if (sym_has_value(sym) && !sym_is_choice_value(sym)) {
233 if (sym->visible == no)
234 sym->flags |= SYMBOL_NEW;
235 switch (sym->type) {
236 case S_STRING:
237 case S_INT:
238 case S_HEX:
239 if (!sym_string_within_range(sym, sym->user.val))
240 sym->flags |= SYMBOL_NEW;
241 default:
242 break;
243 }
244 }
245 if (!sym_is_choice(sym))
246 continue;
247 prop = sym_get_choice_prop(sym);
248 for (e = prop->expr; e; e = e->left.expr)
249 if (e->right.sym->visible != no)
250 sym->flags |= e->right.sym->flags & SYMBOL_NEW;
251 }
252
253 sym_change_count = 1;
254
255 return 0;
256}
257
258int conf_write(const char *name)
259{
260 FILE *out, *out_h;
261 struct symbol *sym;
262 struct menu *menu;
263 const char *basename;
264 char dirname[128], tmpname[128], newname[128];
265 int type, l;
266 const char *str;
267
268 dirname[0] = 0;
269 if (name && name[0]) {
270 struct stat st;
271 char *slash;
272
273 if (!stat(name, &st) && S_ISDIR(st.st_mode)) {
274 strcpy(dirname, name);
275 strcat(dirname, "/");
276 basename = conf_def_filename;
277 } else if ((slash = strrchr(name, '/'))) {
278 int size = slash - name + 1;
279 memcpy(dirname, name, size);
280 dirname[size] = 0;
281 if (slash[1])
282 basename = slash + 1;
283 else
284 basename = conf_def_filename;
285 } else
286 basename = name;
287 } else
288 basename = conf_def_filename;
289
290 sprintf(newname, "%s.tmpconfig.%d", dirname, getpid());
291 out = fopen(newname, "w");
292 if (!out)
293 return 1;
294 out_h = NULL;
295 if (!name) {
296 out_h = fopen(".tmpconfig.h", "w");
297 if (!out_h)
298 return 1;
299 }
300 fprintf(out, "#\n"
301 "# Automatically generated make config: don't edit\n"
302 "#\n");
303 if (out_h) {
304 fprintf(out_h, "/*\n"
305 " * Automatically generated header file: don't edit\n"
306 " */\n\n"
307 "#define AUTOCONF_INCLUDED\n\n"
308 "/* Version Number */\n"
309 "#define BB_VER \"%s\"\n"
310 "#define BB_BT \"%s\"\n",
311 getenv("VERSION"),
312 getenv("BUILDTIME"));
313 if (getenv("EXTRA_VERSION"))
314 fprintf(out_h, "#define BB_EXTRA_VERSION \"%s\"\n",
315 getenv("EXTRA_VERSION"));
316 fprintf(out_h, "\n");
317 }
318
319 if (!sym_change_count)
320 sym_clear_all_valid();
321
322 menu = rootmenu.list;
323 while (menu) {
324 sym = menu->sym;
325 if (!sym) {
326 if (!menu_is_visible(menu))
327 goto next;
328 str = menu_get_prompt(menu);
329 fprintf(out, "\n"
330 "#\n"
331 "# %s\n"
332 "#\n", str);
333 if (out_h)
334 fprintf(out_h, "\n"
335 "/*\n"
336 " * %s\n"
337 " */\n", str);
338 } else if (!(sym->flags & SYMBOL_CHOICE)) {
339 sym_calc_value(sym);
340 if (!(sym->flags & SYMBOL_WRITE))
341 goto next;
342 sym->flags &= ~SYMBOL_WRITE;
343 type = sym->type;
344 if (type == S_TRISTATE) {
345 sym_calc_value(modules_sym);
346 if (modules_sym->curr.tri == no)
347 type = S_BOOLEAN;
348 }
349 switch (type) {
350 case S_BOOLEAN:
351 case S_TRISTATE:
352 switch (sym_get_tristate_value(sym)) {
353 case no:
354 fprintf(out, "# %s is not set\n", sym->name);
355 if (out_h)
356 fprintf(out_h, "#undef %s\n", sym->name);
357 break;
358 case mod:
359#if 0
360 fprintf(out, "%s=m\n", sym->name);
361 if (out_h)
362 fprintf(out_h, "#define %s_MODULE 1\n", sym->name);
363#endif
364 break;
365 case yes:
366 fprintf(out, "%s=y\n", sym->name);
367 if (out_h)
368 fprintf(out_h, "#define %s 1\n", sym->name);
369 break;
370 }
371 break;
372 case S_STRING:
373 // fix me
374 str = sym_get_string_value(sym);
375 fprintf(out, "%s=\"", sym->name);
376 if (out_h)
377 fprintf(out_h, "#define %s \"", sym->name);
378 do {
379 l = strcspn(str, "\"\\");
380 if (l) {
381 fwrite(str, l, 1, out);
382 if (out_h)
383 fwrite(str, l, 1, out_h);
384 }
385 str += l;
386 while (*str == '\\' || *str == '"') {
387 fprintf(out, "\\%c", *str);
388 if (out_h)
389 fprintf(out_h, "\\%c", *str);
390 str++;
391 }
392 } while (*str);
393 fputs("\"\n", out);
394 if (out_h)
395 fputs("\"\n", out_h);
396 break;
397 case S_HEX:
398 str = sym_get_string_value(sym);
399 if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) {
400 fprintf(out, "%s=%s\n", sym->name, str);
401 if (out_h)
402 fprintf(out_h, "#define %s 0x%s\n", sym->name, str);
403 break;
404 }
405 case S_INT:
406 str = sym_get_string_value(sym);
407 fprintf(out, "%s=%s\n", sym->name, str);
408 if (out_h)
409 fprintf(out_h, "#define %s %s\n", sym->name, str);
410 break;
411 }
412 }
413
414 next:
415 if (menu->list) {
416 menu = menu->list;
417 continue;
418 }
419 if (menu->next)
420 menu = menu->next;
421 else while ((menu = menu->parent)) {
422 if (menu->next) {
423 menu = menu->next;
424 break;
425 }
426 }
427 }
428 fclose(out);
429 if (out_h) {
430 fclose(out_h);
431 rename(".tmpconfig.h", "include/config.h");
432 file_write_dep(NULL);
433 }
434 if (!name || basename != conf_def_filename) {
435 if (!name)
436 name = conf_def_filename;
437 sprintf(tmpname, "%s.old", name);
438 rename(name, tmpname);
439 }
440 sprintf(tmpname, "%s%s", dirname, basename);
441 if (rename(newname, tmpname))
442 return 1;
443
444 sym_change_count = 0;
445
446 return 0;
447}
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 @@
1
2/*
3 * dialog.h -- common declarations for all dialog modules
4 *
5 * AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include <sys/types.h>
23#include <fcntl.h>
24#include <unistd.h>
25#include <ctype.h>
26#include <stdlib.h>
27#include <string.h>
28
29#ifdef CURSES_LOC
30#include CURSES_LOC
31
32/*
33 * Colors in ncurses 1.9.9e do not work properly since foreground and
34 * background colors are OR'd rather than separately masked. This version
35 * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible
36 * with standard curses. The simplest fix (to make this work with standard
37 * curses) uses the wbkgdset() function, not used in the original hack.
38 * Turn it off if we're building with 1.9.9e, since it just confuses things.
39 */
40#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE)
41#define OLD_NCURSES 1
42#undef wbkgdset
43#define wbkgdset(w,p) /*nothing*/
44#else
45#define OLD_NCURSES 0
46#endif
47
48#define TR(params) _tracef params
49
50#define ESC 27
51#define TAB 9
52#define MAX_LEN 2048
53#define BUF_SIZE (10*1024)
54#define MIN(x,y) (x < y ? x : y)
55#define MAX(x,y) (x > y ? x : y)
56
57
58#ifndef ACS_ULCORNER
59#define ACS_ULCORNER '+'
60#endif
61#ifndef ACS_LLCORNER
62#define ACS_LLCORNER '+'
63#endif
64#ifndef ACS_URCORNER
65#define ACS_URCORNER '+'
66#endif
67#ifndef ACS_LRCORNER
68#define ACS_LRCORNER '+'
69#endif
70#ifndef ACS_HLINE
71#define ACS_HLINE '-'
72#endif
73#ifndef ACS_VLINE
74#define ACS_VLINE '|'
75#endif
76#ifndef ACS_LTEE
77#define ACS_LTEE '+'
78#endif
79#ifndef ACS_RTEE
80#define ACS_RTEE '+'
81#endif
82#ifndef ACS_UARROW
83#define ACS_UARROW '^'
84#endif
85#ifndef ACS_DARROW
86#define ACS_DARROW 'v'
87#endif
88
89/*
90 * Attribute names
91 */
92#define screen_attr attributes[0]
93#define shadow_attr attributes[1]
94#define dialog_attr attributes[2]
95#define title_attr attributes[3]
96#define border_attr attributes[4]
97#define button_active_attr attributes[5]
98#define button_inactive_attr attributes[6]
99#define button_key_active_attr attributes[7]
100#define button_key_inactive_attr attributes[8]
101#define button_label_active_attr attributes[9]
102#define button_label_inactive_attr attributes[10]
103#define inputbox_attr attributes[11]
104#define inputbox_border_attr attributes[12]
105#define searchbox_attr attributes[13]
106#define searchbox_title_attr attributes[14]
107#define searchbox_border_attr attributes[15]
108#define position_indicator_attr attributes[16]
109#define menubox_attr attributes[17]
110#define menubox_border_attr attributes[18]
111#define item_attr attributes[19]
112#define item_selected_attr attributes[20]
113#define tag_attr attributes[21]
114#define tag_selected_attr attributes[22]
115#define tag_key_attr attributes[23]
116#define tag_key_selected_attr attributes[24]
117#define check_attr attributes[25]
118#define check_selected_attr attributes[26]
119#define uarrow_attr attributes[27]
120#define darrow_attr attributes[28]
121
122/* number of attributes */
123#define ATTRIBUTE_COUNT 29
124
125/*
126 * Global variables
127 */
128extern bool use_colors;
129
130extern chtype attributes[];
131#endif
132
133extern char *backtitle;
134
135struct dialog_list_item {
136 char *name;
137 int namelen;
138 char *tag;
139 int selected; /* Set to 1 by dialog_*() function. */
140};
141
142/*
143 * Function prototypes
144 */
145
146void init_dialog (void);
147void end_dialog (void);
148void dialog_clear (void);
149#ifdef CURSES_LOC
150void attr_clear (WINDOW * win, int height, int width, chtype attr);
151void color_setup (void);
152void print_autowrap (WINDOW * win, const char *prompt, int width, int y, int x);
153void print_button (WINDOW * win, const char *label, int y, int x, int selected);
154void draw_box (WINDOW * win, int y, int x, int height, int width, chtype box,
155 chtype border);
156void draw_shadow (WINDOW * win, int y, int x, int height, int width);
157#endif
158
159int first_alpha (const char *string, const char *exempt);
160int dialog_yesno (const char *title, const char *prompt, int height, int width);
161int dialog_msgbox (const char *title, const char *prompt, int height,
162 int width, int pause);
163int dialog_textbox (const char *title, const char *file, int height, int width);
164int dialog_menu (const char *title, const char *prompt, int height, int width,
165 int menu_height, const char *choice, int item_no,
166 struct dialog_list_item ** items);
167int dialog_checklist (const char *title, const char *prompt, int height,
168 int width, int list_height, int item_no,
169 struct dialog_list_item ** items, int flag);
170extern unsigned char dialog_input_result[];
171int dialog_inputbox (const char *title, const char *prompt, int height,
172 int width, const char *init);
173
174struct dialog_list_item *first_sel_item(int item_no,
175 struct dialog_list_item ** items);
176
177/*
178 * This is the base for fictitious keys, which activate
179 * the buttons.
180 *
181 * Mouse-generated keys are the following:
182 * -- the first 32 are used as numbers, in addition to '0'-'9'
183 * -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o')
184 * -- uppercase chars are used to invoke the button (M_EVENT + 'O')
185 */
186#ifdef CURSES_LOC
187#define M_EVENT (KEY_MAX+1)
188#endif
189
190
191/*
192 * The `flag' parameter in checklist is used to select between
193 * radiolist and checklist
194 */
195#define FLAG_CHECK 1
196#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 @@
1/*
2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9
10#define LKC_DIRECT_LINK
11#include "lkc.h"
12
13#define DEBUG_EXPR 0
14
15struct expr *expr_alloc_symbol(struct symbol *sym)
16{
17 struct expr *e = malloc(sizeof(*e));
18 memset(e, 0, sizeof(*e));
19 e->type = E_SYMBOL;
20 e->left.sym = sym;
21 return e;
22}
23
24struct expr *expr_alloc_one(enum expr_type type, struct expr *ce)
25{
26 struct expr *e = malloc(sizeof(*e));
27 memset(e, 0, sizeof(*e));
28 e->type = type;
29 e->left.expr = ce;
30 return e;
31}
32
33struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2)
34{
35 struct expr *e = malloc(sizeof(*e));
36 memset(e, 0, sizeof(*e));
37 e->type = type;
38 e->left.expr = e1;
39 e->right.expr = e2;
40 return e;
41}
42
43struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2)
44{
45 struct expr *e = malloc(sizeof(*e));
46 memset(e, 0, sizeof(*e));
47 e->type = type;
48 e->left.sym = s1;
49 e->right.sym = s2;
50 return e;
51}
52
53struct expr *expr_alloc_and(struct expr *e1, struct expr *e2)
54{
55 if (!e1)
56 return e2;
57 return e2 ? expr_alloc_two(E_AND, e1, e2) : e1;
58}
59
60struct expr *expr_alloc_or(struct expr *e1, struct expr *e2)
61{
62 if (!e1)
63 return e2;
64 return e2 ? expr_alloc_two(E_OR, e1, e2) : e1;
65}
66
67struct expr *expr_copy(struct expr *org)
68{
69 struct expr *e;
70
71 if (!org)
72 return NULL;
73
74 e = malloc(sizeof(*org));
75 memcpy(e, org, sizeof(*org));
76 switch (org->type) {
77 case E_SYMBOL:
78 e->left = org->left;
79 break;
80 case E_NOT:
81 e->left.expr = expr_copy(org->left.expr);
82 break;
83 case E_EQUAL:
84 case E_UNEQUAL:
85 e->left.sym = org->left.sym;
86 e->right.sym = org->right.sym;
87 break;
88 case E_AND:
89 case E_OR:
90 case E_CHOICE:
91 e->left.expr = expr_copy(org->left.expr);
92 e->right.expr = expr_copy(org->right.expr);
93 break;
94 default:
95 printf("can't copy type %d\n", e->type);
96 free(e);
97 e = NULL;
98 break;
99 }
100
101 return e;
102}
103
104void expr_free(struct expr *e)
105{
106 if (!e)
107 return;
108
109 switch (e->type) {
110 case E_SYMBOL:
111 break;
112 case E_NOT:
113 expr_free(e->left.expr);
114 return;
115 case E_EQUAL:
116 case E_UNEQUAL:
117 break;
118 case E_OR:
119 case E_AND:
120 expr_free(e->left.expr);
121 expr_free(e->right.expr);
122 break;
123 default:
124 printf("how to free type %d?\n", e->type);
125 break;
126 }
127 free(e);
128}
129
130static int trans_count;
131
132#define e1 (*ep1)
133#define e2 (*ep2)
134
135static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2)
136{
137 if (e1->type == type) {
138 __expr_eliminate_eq(type, &e1->left.expr, &e2);
139 __expr_eliminate_eq(type, &e1->right.expr, &e2);
140 return;
141 }
142 if (e2->type == type) {
143 __expr_eliminate_eq(type, &e1, &e2->left.expr);
144 __expr_eliminate_eq(type, &e1, &e2->right.expr);
145 return;
146 }
147 if (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
148 e1->left.sym == e2->left.sym && (e1->left.sym->flags & (SYMBOL_YES|SYMBOL_NO)))
149 return;
150 if (!expr_eq(e1, e2))
151 return;
152 trans_count++;
153 expr_free(e1); expr_free(e2);
154 switch (type) {
155 case E_OR:
156 e1 = expr_alloc_symbol(&symbol_no);
157 e2 = expr_alloc_symbol(&symbol_no);
158 break;
159 case E_AND:
160 e1 = expr_alloc_symbol(&symbol_yes);
161 e2 = expr_alloc_symbol(&symbol_yes);
162 break;
163 default:
164 ;
165 }
166}
167
168void expr_eliminate_eq(struct expr **ep1, struct expr **ep2)
169{
170 if (!e1 || !e2)
171 return;
172 switch (e1->type) {
173 case E_OR:
174 case E_AND:
175 __expr_eliminate_eq(e1->type, ep1, ep2);
176 default:
177 ;
178 }
179 if (e1->type != e2->type) switch (e2->type) {
180 case E_OR:
181 case E_AND:
182 __expr_eliminate_eq(e2->type, ep1, ep2);
183 default:
184 ;
185 }
186 e1 = expr_eliminate_yn(e1);
187 e2 = expr_eliminate_yn(e2);
188}
189
190#undef e1
191#undef e2
192
193int expr_eq(struct expr *e1, struct expr *e2)
194{
195 int res, old_count;
196
197 if (e1->type != e2->type)
198 return 0;
199 switch (e1->type) {
200 case E_EQUAL:
201 case E_UNEQUAL:
202 return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym;
203 case E_SYMBOL:
204 return e1->left.sym == e2->left.sym;
205 case E_NOT:
206 return expr_eq(e1->left.expr, e2->left.expr);
207 case E_AND:
208 case E_OR:
209 e1 = expr_copy(e1);
210 e2 = expr_copy(e2);
211 old_count = trans_count;
212 expr_eliminate_eq(&e1, &e2);
213 res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
214 e1->left.sym == e2->left.sym);
215 expr_free(e1);
216 expr_free(e2);
217 trans_count = old_count;
218 return res;
219 case E_CHOICE:
220 case E_RANGE:
221 case E_NONE:
222 /* panic */;
223 }
224
225 if (DEBUG_EXPR) {
226 expr_fprint(e1, stdout);
227 printf(" = ");
228 expr_fprint(e2, stdout);
229 printf(" ?\n");
230 }
231
232 return 0;
233}
234
235struct expr *expr_eliminate_yn(struct expr *e)
236{
237 struct expr *tmp;
238
239 if (e) switch (e->type) {
240 case E_AND:
241 e->left.expr = expr_eliminate_yn(e->left.expr);
242 e->right.expr = expr_eliminate_yn(e->right.expr);
243 if (e->left.expr->type == E_SYMBOL) {
244 if (e->left.expr->left.sym == &symbol_no) {
245 expr_free(e->left.expr);
246 expr_free(e->right.expr);
247 e->type = E_SYMBOL;
248 e->left.sym = &symbol_no;
249 e->right.expr = NULL;
250 return e;
251 } else if (e->left.expr->left.sym == &symbol_yes) {
252 free(e->left.expr);
253 tmp = e->right.expr;
254 *e = *(e->right.expr);
255 free(tmp);
256 return e;
257 }
258 }
259 if (e->right.expr->type == E_SYMBOL) {
260 if (e->right.expr->left.sym == &symbol_no) {
261 expr_free(e->left.expr);
262 expr_free(e->right.expr);
263 e->type = E_SYMBOL;
264 e->left.sym = &symbol_no;
265 e->right.expr = NULL;
266 return e;
267 } else if (e->right.expr->left.sym == &symbol_yes) {
268 free(e->right.expr);
269 tmp = e->left.expr;
270 *e = *(e->left.expr);
271 free(tmp);
272 return e;
273 }
274 }
275 break;
276 case E_OR:
277 e->left.expr = expr_eliminate_yn(e->left.expr);
278 e->right.expr = expr_eliminate_yn(e->right.expr);
279 if (e->left.expr->type == E_SYMBOL) {
280 if (e->left.expr->left.sym == &symbol_no) {
281 free(e->left.expr);
282 tmp = e->right.expr;
283 *e = *(e->right.expr);
284 free(tmp);
285 return e;
286 } else if (e->left.expr->left.sym == &symbol_yes) {
287 expr_free(e->left.expr);
288 expr_free(e->right.expr);
289 e->type = E_SYMBOL;
290 e->left.sym = &symbol_yes;
291 e->right.expr = NULL;
292 return e;
293 }
294 }
295 if (e->right.expr->type == E_SYMBOL) {
296 if (e->right.expr->left.sym == &symbol_no) {
297 free(e->right.expr);
298 tmp = e->left.expr;
299 *e = *(e->left.expr);
300 free(tmp);
301 return e;
302 } else if (e->right.expr->left.sym == &symbol_yes) {
303 expr_free(e->left.expr);
304 expr_free(e->right.expr);
305 e->type = E_SYMBOL;
306 e->left.sym = &symbol_yes;
307 e->right.expr = NULL;
308 return e;
309 }
310 }
311 break;
312 default:
313 ;
314 }
315 return e;
316}
317
318/*
319 * bool FOO!=n => FOO
320 */
321struct expr *expr_trans_bool(struct expr *e)
322{
323 if (!e)
324 return NULL;
325 switch (e->type) {
326 case E_AND:
327 case E_OR:
328 case E_NOT:
329 e->left.expr = expr_trans_bool(e->left.expr);
330 e->right.expr = expr_trans_bool(e->right.expr);
331 break;
332 case E_UNEQUAL:
333 // FOO!=n -> FOO
334 if (e->left.sym->type == S_TRISTATE) {
335 if (e->right.sym == &symbol_no) {
336 e->type = E_SYMBOL;
337 e->right.sym = NULL;
338 }
339 }
340 break;
341 default:
342 ;
343 }
344 return e;
345}
346
347/*
348 * e1 || e2 -> ?
349 */
350struct expr *expr_join_or(struct expr *e1, struct expr *e2)
351{
352 struct expr *tmp;
353 struct symbol *sym1, *sym2;
354
355 if (expr_eq(e1, e2))
356 return expr_copy(e1);
357 if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
358 return NULL;
359 if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
360 return NULL;
361 if (e1->type == E_NOT) {
362 tmp = e1->left.expr;
363 if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL)
364 return NULL;
365 sym1 = tmp->left.sym;
366 } else
367 sym1 = e1->left.sym;
368 if (e2->type == E_NOT) {
369 if (e2->left.expr->type != E_SYMBOL)
370 return NULL;
371 sym2 = e2->left.expr->left.sym;
372 } else
373 sym2 = e2->left.sym;
374 if (sym1 != sym2)
375 return NULL;
376 if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
377 return NULL;
378 if (sym1->type == S_TRISTATE) {
379 if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
380 ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
381 (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) {
382 // (a='y') || (a='m') -> (a!='n')
383 return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no);
384 }
385 if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
386 ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
387 (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) {
388 // (a='y') || (a='n') -> (a!='m')
389 return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod);
390 }
391 if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
392 ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
393 (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) {
394 // (a='m') || (a='n') -> (a!='y')
395 return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes);
396 }
397 }
398 if (sym1->type == S_BOOLEAN && sym1 == sym2) {
399 if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) ||
400 (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL))
401 return expr_alloc_symbol(&symbol_yes);
402 }
403
404 if (DEBUG_EXPR) {
405 printf("optimize (");
406 expr_fprint(e1, stdout);
407 printf(") || (");
408 expr_fprint(e2, stdout);
409 printf(")?\n");
410 }
411 return NULL;
412}
413
414struct expr *expr_join_and(struct expr *e1, struct expr *e2)
415{
416 struct expr *tmp;
417 struct symbol *sym1, *sym2;
418
419 if (expr_eq(e1, e2))
420 return expr_copy(e1);
421 if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
422 return NULL;
423 if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
424 return NULL;
425 if (e1->type == E_NOT) {
426 tmp = e1->left.expr;
427 if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL)
428 return NULL;
429 sym1 = tmp->left.sym;
430 } else
431 sym1 = e1->left.sym;
432 if (e2->type == E_NOT) {
433 if (e2->left.expr->type != E_SYMBOL)
434 return NULL;
435 sym2 = e2->left.expr->left.sym;
436 } else
437 sym2 = e2->left.sym;
438 if (sym1 != sym2)
439 return NULL;
440 if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
441 return NULL;
442
443 if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) ||
444 (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes))
445 // (a) && (a='y') -> (a='y')
446 return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
447
448 if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) ||
449 (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no))
450 // (a) && (a!='n') -> (a)
451 return expr_alloc_symbol(sym1);
452
453 if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) ||
454 (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod))
455 // (a) && (a!='m') -> (a='y')
456 return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
457
458 if (sym1->type == S_TRISTATE) {
459 if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) {
460 // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
461 sym2 = e1->right.sym;
462 if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
463 return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
464 : expr_alloc_symbol(&symbol_no);
465 }
466 if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) {
467 // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
468 sym2 = e2->right.sym;
469 if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
470 return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
471 : expr_alloc_symbol(&symbol_no);
472 }
473 if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
474 ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
475 (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes)))
476 // (a!='y') && (a!='n') -> (a='m')
477 return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod);
478
479 if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
480 ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
481 (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes)))
482 // (a!='y') && (a!='m') -> (a='n')
483 return expr_alloc_comp(E_EQUAL, sym1, &symbol_no);
484
485 if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
486 ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
487 (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod)))
488 // (a!='m') && (a!='n') -> (a='m')
489 return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
490
491 if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) ||
492 (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) ||
493 (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) ||
494 (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes))
495 return NULL;
496 }
497
498 if (DEBUG_EXPR) {
499 printf("optimize (");
500 expr_fprint(e1, stdout);
501 printf(") && (");
502 expr_fprint(e2, stdout);
503 printf(")?\n");
504 }
505 return NULL;
506}
507
508static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2)
509{
510#define e1 (*ep1)
511#define e2 (*ep2)
512 struct expr *tmp;
513
514 if (e1->type == type) {
515 expr_eliminate_dups1(type, &e1->left.expr, &e2);
516 expr_eliminate_dups1(type, &e1->right.expr, &e2);
517 return;
518 }
519 if (e2->type == type) {
520 expr_eliminate_dups1(type, &e1, &e2->left.expr);
521 expr_eliminate_dups1(type, &e1, &e2->right.expr);
522 return;
523 }
524 if (e1 == e2)
525 return;
526
527 switch (e1->type) {
528 case E_OR: case E_AND:
529 expr_eliminate_dups1(e1->type, &e1, &e1);
530 default:
531 ;
532 }
533
534 switch (type) {
535 case E_OR:
536 tmp = expr_join_or(e1, e2);
537 if (tmp) {
538 expr_free(e1); expr_free(e2);
539 e1 = expr_alloc_symbol(&symbol_no);
540 e2 = tmp;
541 trans_count++;
542 }
543 break;
544 case E_AND:
545 tmp = expr_join_and(e1, e2);
546 if (tmp) {
547 expr_free(e1); expr_free(e2);
548 e1 = expr_alloc_symbol(&symbol_yes);
549 e2 = tmp;
550 trans_count++;
551 }
552 break;
553 default:
554 ;
555 }
556#undef e1
557#undef e2
558}
559
560static void expr_eliminate_dups2(enum expr_type type, struct expr **ep1, struct expr **ep2)
561{
562#define e1 (*ep1)
563#define e2 (*ep2)
564 struct expr *tmp, *tmp1, *tmp2;
565
566 if (e1->type == type) {
567 expr_eliminate_dups2(type, &e1->left.expr, &e2);
568 expr_eliminate_dups2(type, &e1->right.expr, &e2);
569 return;
570 }
571 if (e2->type == type) {
572 expr_eliminate_dups2(type, &e1, &e2->left.expr);
573 expr_eliminate_dups2(type, &e1, &e2->right.expr);
574 }
575 if (e1 == e2)
576 return;
577
578 switch (e1->type) {
579 case E_OR:
580 expr_eliminate_dups2(e1->type, &e1, &e1);
581 // (FOO || BAR) && (!FOO && !BAR) -> n
582 tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1)));
583 tmp2 = expr_copy(e2);
584 tmp = expr_extract_eq_and(&tmp1, &tmp2);
585 if (expr_is_yes(tmp1)) {
586 expr_free(e1);
587 e1 = expr_alloc_symbol(&symbol_no);
588 trans_count++;
589 }
590 expr_free(tmp2);
591 expr_free(tmp1);
592 expr_free(tmp);
593 break;
594 case E_AND:
595 expr_eliminate_dups2(e1->type, &e1, &e1);
596 // (FOO && BAR) || (!FOO || !BAR) -> y
597 tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1)));
598 tmp2 = expr_copy(e2);
599 tmp = expr_extract_eq_or(&tmp1, &tmp2);
600 if (expr_is_no(tmp1)) {
601 expr_free(e1);
602 e1 = expr_alloc_symbol(&symbol_yes);
603 trans_count++;
604 }
605 expr_free(tmp2);
606 expr_free(tmp1);
607 expr_free(tmp);
608 break;
609 default:
610 ;
611 }
612#undef e1
613#undef e2
614}
615
616struct expr *expr_eliminate_dups(struct expr *e)
617{
618 int oldcount;
619 if (!e)
620 return e;
621
622 oldcount = trans_count;
623 while (1) {
624 trans_count = 0;
625 switch (e->type) {
626 case E_OR: case E_AND:
627 expr_eliminate_dups1(e->type, &e, &e);
628 expr_eliminate_dups2(e->type, &e, &e);
629 default:
630 ;
631 }
632 if (!trans_count)
633 break;
634 e = expr_eliminate_yn(e);
635 }
636 trans_count = oldcount;
637 return e;
638}
639
640struct expr *expr_transform(struct expr *e)
641{
642 struct expr *tmp;
643
644 if (!e)
645 return NULL;
646 switch (e->type) {
647 case E_EQUAL:
648 case E_UNEQUAL:
649 case E_SYMBOL:
650 case E_CHOICE:
651 break;
652 default:
653 e->left.expr = expr_transform(e->left.expr);
654 e->right.expr = expr_transform(e->right.expr);
655 }
656
657 switch (e->type) {
658 case E_EQUAL:
659 if (e->left.sym->type != S_BOOLEAN)
660 break;
661 if (e->right.sym == &symbol_no) {
662 e->type = E_NOT;
663 e->left.expr = expr_alloc_symbol(e->left.sym);
664 e->right.sym = NULL;
665 break;
666 }
667 if (e->right.sym == &symbol_mod) {
668 printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name);
669 e->type = E_SYMBOL;
670 e->left.sym = &symbol_no;
671 e->right.sym = NULL;
672 break;
673 }
674 if (e->right.sym == &symbol_yes) {
675 e->type = E_SYMBOL;
676 e->right.sym = NULL;
677 break;
678 }
679 break;
680 case E_UNEQUAL:
681 if (e->left.sym->type != S_BOOLEAN)
682 break;
683 if (e->right.sym == &symbol_no) {
684 e->type = E_SYMBOL;
685 e->right.sym = NULL;
686 break;
687 }
688 if (e->right.sym == &symbol_mod) {
689 printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name);
690 e->type = E_SYMBOL;
691 e->left.sym = &symbol_yes;
692 e->right.sym = NULL;
693 break;
694 }
695 if (e->right.sym == &symbol_yes) {
696 e->type = E_NOT;
697 e->left.expr = expr_alloc_symbol(e->left.sym);
698 e->right.sym = NULL;
699 break;
700 }
701 break;
702 case E_NOT:
703 switch (e->left.expr->type) {
704 case E_NOT:
705 // !!a -> a
706 tmp = e->left.expr->left.expr;
707 free(e->left.expr);
708 free(e);
709 e = tmp;
710 e = expr_transform(e);
711 break;
712 case E_EQUAL:
713 case E_UNEQUAL:
714 // !a='x' -> a!='x'
715 tmp = e->left.expr;
716 free(e);
717 e = tmp;
718 e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL;
719 break;
720 case E_OR:
721 // !(a || b) -> !a && !b
722 tmp = e->left.expr;
723 e->type = E_AND;
724 e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
725 tmp->type = E_NOT;
726 tmp->right.expr = NULL;
727 e = expr_transform(e);
728 break;
729 case E_AND:
730 // !(a && b) -> !a || !b
731 tmp = e->left.expr;
732 e->type = E_OR;
733 e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
734 tmp->type = E_NOT;
735 tmp->right.expr = NULL;
736 e = expr_transform(e);
737 break;
738 case E_SYMBOL:
739 if (e->left.expr->left.sym == &symbol_yes) {
740 // !'y' -> 'n'
741 tmp = e->left.expr;
742 free(e);
743 e = tmp;
744 e->type = E_SYMBOL;
745 e->left.sym = &symbol_no;
746 break;
747 }
748 if (e->left.expr->left.sym == &symbol_mod) {
749 // !'m' -> 'm'
750 tmp = e->left.expr;
751 free(e);
752 e = tmp;
753 e->type = E_SYMBOL;
754 e->left.sym = &symbol_mod;
755 break;
756 }
757 if (e->left.expr->left.sym == &symbol_no) {
758 // !'n' -> 'y'
759 tmp = e->left.expr;
760 free(e);
761 e = tmp;
762 e->type = E_SYMBOL;
763 e->left.sym = &symbol_yes;
764 break;
765 }
766 break;
767 default:
768 ;
769 }
770 break;
771 default:
772 ;
773 }
774 return e;
775}
776
777int expr_contains_symbol(struct expr *dep, struct symbol *sym)
778{
779 if (!dep)
780 return 0;
781
782 switch (dep->type) {
783 case E_AND:
784 case E_OR:
785 return expr_contains_symbol(dep->left.expr, sym) ||
786 expr_contains_symbol(dep->right.expr, sym);
787 case E_SYMBOL:
788 return dep->left.sym == sym;
789 case E_EQUAL:
790 case E_UNEQUAL:
791 return dep->left.sym == sym ||
792 dep->right.sym == sym;
793 case E_NOT:
794 return expr_contains_symbol(dep->left.expr, sym);
795 default:
796 ;
797 }
798 return 0;
799}
800
801bool expr_depends_symbol(struct expr *dep, struct symbol *sym)
802{
803 if (!dep)
804 return false;
805
806 switch (dep->type) {
807 case E_AND:
808 return expr_depends_symbol(dep->left.expr, sym) ||
809 expr_depends_symbol(dep->right.expr, sym);
810 case E_SYMBOL:
811 return dep->left.sym == sym;
812 case E_EQUAL:
813 if (dep->left.sym == sym) {
814 if (dep->right.sym == &symbol_yes || dep->right.sym == &symbol_mod)
815 return true;
816 }
817 break;
818 case E_UNEQUAL:
819 if (dep->left.sym == sym) {
820 if (dep->right.sym == &symbol_no)
821 return true;
822 }
823 break;
824 default:
825 ;
826 }
827 return false;
828}
829
830struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2)
831{
832 struct expr *tmp = NULL;
833 expr_extract_eq(E_AND, &tmp, ep1, ep2);
834 if (tmp) {
835 *ep1 = expr_eliminate_yn(*ep1);
836 *ep2 = expr_eliminate_yn(*ep2);
837 }
838 return tmp;
839}
840
841struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2)
842{
843 struct expr *tmp = NULL;
844 expr_extract_eq(E_OR, &tmp, ep1, ep2);
845 if (tmp) {
846 *ep1 = expr_eliminate_yn(*ep1);
847 *ep2 = expr_eliminate_yn(*ep2);
848 }
849 return tmp;
850}
851
852void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2)
853{
854#define e1 (*ep1)
855#define e2 (*ep2)
856 if (e1->type == type) {
857 expr_extract_eq(type, ep, &e1->left.expr, &e2);
858 expr_extract_eq(type, ep, &e1->right.expr, &e2);
859 return;
860 }
861 if (e2->type == type) {
862 expr_extract_eq(type, ep, ep1, &e2->left.expr);
863 expr_extract_eq(type, ep, ep1, &e2->right.expr);
864 return;
865 }
866 if (expr_eq(e1, e2)) {
867 *ep = *ep ? expr_alloc_two(type, *ep, e1) : e1;
868 expr_free(e2);
869 if (type == E_AND) {
870 e1 = expr_alloc_symbol(&symbol_yes);
871 e2 = expr_alloc_symbol(&symbol_yes);
872 } else if (type == E_OR) {
873 e1 = expr_alloc_symbol(&symbol_no);
874 e2 = expr_alloc_symbol(&symbol_no);
875 }
876 }
877#undef e1
878#undef e2
879}
880
881struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym)
882{
883 struct expr *e1, *e2;
884
885 if (!e) {
886 e = expr_alloc_symbol(sym);
887 if (type == E_UNEQUAL)
888 e = expr_alloc_one(E_NOT, e);
889 return e;
890 }
891 switch (e->type) {
892 case E_AND:
893 e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
894 e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
895 if (sym == &symbol_yes)
896 e = expr_alloc_two(E_AND, e1, e2);
897 if (sym == &symbol_no)
898 e = expr_alloc_two(E_OR, e1, e2);
899 if (type == E_UNEQUAL)
900 e = expr_alloc_one(E_NOT, e);
901 return e;
902 case E_OR:
903 e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
904 e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
905 if (sym == &symbol_yes)
906 e = expr_alloc_two(E_OR, e1, e2);
907 if (sym == &symbol_no)
908 e = expr_alloc_two(E_AND, e1, e2);
909 if (type == E_UNEQUAL)
910 e = expr_alloc_one(E_NOT, e);
911 return e;
912 case E_NOT:
913 return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym);
914 case E_UNEQUAL:
915 case E_EQUAL:
916 if (type == E_EQUAL) {
917 if (sym == &symbol_yes)
918 return expr_copy(e);
919 if (sym == &symbol_mod)
920 return expr_alloc_symbol(&symbol_no);
921 if (sym == &symbol_no)
922 return expr_alloc_one(E_NOT, expr_copy(e));
923 } else {
924 if (sym == &symbol_yes)
925 return expr_alloc_one(E_NOT, expr_copy(e));
926 if (sym == &symbol_mod)
927 return expr_alloc_symbol(&symbol_yes);
928 if (sym == &symbol_no)
929 return expr_copy(e);
930 }
931 break;
932 case E_SYMBOL:
933 return expr_alloc_comp(type, e->left.sym, sym);
934 case E_CHOICE:
935 case E_RANGE:
936 case E_NONE:
937 /* panic */;
938 }
939 return NULL;
940}
941
942tristate expr_calc_value(struct expr *e)
943{
944 tristate val1, val2;
945 const char *str1, *str2;
946
947 if (!e)
948 return yes;
949
950 switch (e->type) {
951 case E_SYMBOL:
952 sym_calc_value(e->left.sym);
953 return e->left.sym->curr.tri;
954 case E_AND:
955 val1 = expr_calc_value(e->left.expr);
956 val2 = expr_calc_value(e->right.expr);
957 return E_AND(val1, val2);
958 case E_OR:
959 val1 = expr_calc_value(e->left.expr);
960 val2 = expr_calc_value(e->right.expr);
961 return E_OR(val1, val2);
962 case E_NOT:
963 val1 = expr_calc_value(e->left.expr);
964 return E_NOT(val1);
965 case E_EQUAL:
966 sym_calc_value(e->left.sym);
967 sym_calc_value(e->right.sym);
968 str1 = sym_get_string_value(e->left.sym);
969 str2 = sym_get_string_value(e->right.sym);
970 return !strcmp(str1, str2) ? yes : no;
971 case E_UNEQUAL:
972 sym_calc_value(e->left.sym);
973 sym_calc_value(e->right.sym);
974 str1 = sym_get_string_value(e->left.sym);
975 str2 = sym_get_string_value(e->right.sym);
976 return !strcmp(str1, str2) ? no : yes;
977 default:
978 printf("expr_calc_value: %d?\n", e->type);
979 return no;
980 }
981}
982
983int expr_compare_type(enum expr_type t1, enum expr_type t2)
984{
985#if 0
986 return 1;
987#else
988 if (t1 == t2)
989 return 0;
990 switch (t1) {
991 case E_EQUAL:
992 case E_UNEQUAL:
993 if (t2 == E_NOT)
994 return 1;
995 case E_NOT:
996 if (t2 == E_AND)
997 return 1;
998 case E_AND:
999 if (t2 == E_OR)
1000 return 1;
1001 case E_OR:
1002 if (t2 == E_CHOICE)
1003 return 1;
1004 case E_CHOICE:
1005 if (t2 == 0)
1006 return 1;
1007 default:
1008 return -1;
1009 }
1010 printf("[%dgt%d?]", t1, t2);
1011 return 0;
1012#endif
1013}
1014
1015void expr_print(struct expr *e, void (*fn)(void *, const char *), void *data, int prevtoken)
1016{
1017 if (!e) {
1018 fn(data, "y");
1019 return;
1020 }
1021
1022 if (expr_compare_type(prevtoken, e->type) > 0)
1023 fn(data, "(");
1024 switch (e->type) {
1025 case E_SYMBOL:
1026 if (e->left.sym->name)
1027 fn(data, e->left.sym->name);
1028 else
1029 fn(data, "<choice>");
1030 break;
1031 case E_NOT:
1032 fn(data, "!");
1033 expr_print(e->left.expr, fn, data, E_NOT);
1034 break;
1035 case E_EQUAL:
1036 fn(data, e->left.sym->name);
1037 fn(data, "=");
1038 fn(data, e->right.sym->name);
1039 break;
1040 case E_UNEQUAL:
1041 fn(data, e->left.sym->name);
1042 fn(data, "!=");
1043 fn(data, e->right.sym->name);
1044 break;
1045 case E_OR:
1046 expr_print(e->left.expr, fn, data, E_OR);
1047 fn(data, " || ");
1048 expr_print(e->right.expr, fn, data, E_OR);
1049 break;
1050 case E_AND:
1051 expr_print(e->left.expr, fn, data, E_AND);
1052 fn(data, " && ");
1053 expr_print(e->right.expr, fn, data, E_AND);
1054 break;
1055 case E_CHOICE:
1056 fn(data, e->right.sym->name);
1057 if (e->left.expr) {
1058 fn(data, " ^ ");
1059 expr_print(e->left.expr, fn, data, E_CHOICE);
1060 }
1061 break;
1062 case E_RANGE:
1063 fn(data, "[");
1064 fn(data, e->left.sym->name);
1065 fn(data, " ");
1066 fn(data, e->right.sym->name);
1067 fn(data, "]");
1068 break;
1069 default:
1070 {
1071 char buf[32];
1072 sprintf(buf, "<unknown type %d>", e->type);
1073 fn(data, buf);
1074 break;
1075 }
1076 }
1077 if (expr_compare_type(prevtoken, e->type) > 0)
1078 fn(data, ")");
1079}
1080
1081static void expr_print_file_helper(void *data, const char *str)
1082{
1083 fwrite(str, strlen(str), 1, data);
1084}
1085
1086void expr_fprint(struct expr *e, FILE *out)
1087{
1088 expr_print(e, expr_print_file_helper, out, E_NONE);
1089}
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 @@
1/*
2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
4 */
5
6#ifndef EXPR_H
7#define EXPR_H
8
9#ifdef __cplusplus
10extern "C" {
11#endif
12
13#include <stdio.h>
14#ifndef __cplusplus
15#include <stdbool.h>
16#endif
17
18struct file {
19 struct file *next;
20 struct file *parent;
21 char *name;
22 int lineno;
23 int flags;
24};
25
26#define FILE_BUSY 0x0001
27#define FILE_SCANNED 0x0002
28#define FILE_PRINTED 0x0004
29
30typedef enum tristate {
31 no, mod, yes
32} tristate;
33
34enum expr_type {
35 E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_CHOICE, E_SYMBOL, E_RANGE
36};
37
38union expr_data {
39 struct expr *expr;
40 struct symbol *sym;
41};
42
43struct expr {
44 enum expr_type type;
45 union expr_data left, right;
46};
47
48#define E_OR(dep1, dep2) (((dep1)>(dep2))?(dep1):(dep2))
49#define E_AND(dep1, dep2) (((dep1)<(dep2))?(dep1):(dep2))
50#define E_NOT(dep) (2-(dep))
51
52struct expr_value {
53 struct expr *expr;
54 tristate tri;
55};
56
57struct symbol_value {
58 void *val;
59 tristate tri;
60};
61
62enum symbol_type {
63 S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING, S_OTHER
64};
65
66struct symbol {
67 struct symbol *next;
68 char *name;
69 char *help;
70 enum symbol_type type;
71 struct symbol_value curr, user;
72 tristate visible;
73 int flags;
74 struct property *prop;
75 struct expr *dep, *dep2;
76 struct expr_value rev_dep;
77};
78
79#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)
80
81#define SYMBOL_YES 0x0001
82#define SYMBOL_MOD 0x0002
83#define SYMBOL_NO 0x0004
84#define SYMBOL_CONST 0x0007
85#define SYMBOL_CHECK 0x0008
86#define SYMBOL_CHOICE 0x0010
87#define SYMBOL_CHOICEVAL 0x0020
88#define SYMBOL_PRINTED 0x0040
89#define SYMBOL_VALID 0x0080
90#define SYMBOL_OPTIONAL 0x0100
91#define SYMBOL_WRITE 0x0200
92#define SYMBOL_CHANGED 0x0400
93#define SYMBOL_NEW 0x0800
94#define SYMBOL_AUTO 0x1000
95#define SYMBOL_CHECKED 0x2000
96#define SYMBOL_CHECK_DONE 0x4000
97#define SYMBOL_WARNED 0x8000
98
99#define SYMBOL_MAXLENGTH 256
100#define SYMBOL_HASHSIZE 257
101#define SYMBOL_HASHMASK 0xff
102
103enum prop_type {
104 P_UNKNOWN, P_PROMPT, P_COMMENT, P_MENU, P_DEFAULT, P_CHOICE, P_SELECT, P_RANGE
105};
106
107struct property {
108 struct property *next;
109 struct symbol *sym;
110 enum prop_type type;
111 const char *text;
112 struct expr_value visible;
113 struct expr *expr;
114 struct menu *menu;
115 struct file *file;
116 int lineno;
117};
118
119#define for_all_properties(sym, st, tok) \
120 for (st = sym->prop; st; st = st->next) \
121 if (st->type == (tok))
122#define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT)
123#define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE)
124#define for_all_prompts(sym, st) \
125 for (st = sym->prop; st; st = st->next) \
126 if (st->text)
127
128struct menu {
129 struct menu *next;
130 struct menu *parent;
131 struct menu *list;
132 struct symbol *sym;
133 struct property *prompt;
134 struct expr *dep;
135 unsigned int flags;
136 //char *help;
137 struct file *file;
138 int lineno;
139 void *data;
140};
141
142#define MENU_CHANGED 0x0001
143#define MENU_ROOT 0x0002
144
145#ifndef SWIG
146
147extern struct file *file_list;
148extern struct file *current_file;
149struct file *lookup_file(const char *name);
150
151extern struct symbol symbol_yes, symbol_no, symbol_mod;
152extern struct symbol *modules_sym;
153extern int cdebug;
154struct expr *expr_alloc_symbol(struct symbol *sym);
155struct expr *expr_alloc_one(enum expr_type type, struct expr *ce);
156struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2);
157struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2);
158struct expr *expr_alloc_and(struct expr *e1, struct expr *e2);
159struct expr *expr_alloc_or(struct expr *e1, struct expr *e2);
160struct expr *expr_copy(struct expr *org);
161void expr_free(struct expr *e);
162int expr_eq(struct expr *e1, struct expr *e2);
163void expr_eliminate_eq(struct expr **ep1, struct expr **ep2);
164tristate expr_calc_value(struct expr *e);
165struct expr *expr_eliminate_yn(struct expr *e);
166struct expr *expr_trans_bool(struct expr *e);
167struct expr *expr_eliminate_dups(struct expr *e);
168struct expr *expr_transform(struct expr *e);
169int expr_contains_symbol(struct expr *dep, struct symbol *sym);
170bool expr_depends_symbol(struct expr *dep, struct symbol *sym);
171struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2);
172struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2);
173void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2);
174struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym);
175
176void expr_fprint(struct expr *e, FILE *out);
177
178static inline int expr_is_yes(struct expr *e)
179{
180 return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes);
181}
182
183static inline int expr_is_no(struct expr *e)
184{
185 return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no);
186}
187#endif
188
189#ifdef __cplusplus
190}
191#endif
192
193#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 @@
1/*
2 * inputbox.c -- implements the input box
3 *
4 * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
5 * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include "dialog.h"
23
24unsigned char dialog_input_result[MAX_LEN + 1];
25
26/*
27 * Print the termination buttons
28 */
29static void
30print_buttons(WINDOW *dialog, int height, int width, int selected)
31{
32 int x = width / 2 - 11;
33 int y = height - 2;
34
35 print_button (dialog, " Ok ", y, x, selected==0);
36 print_button (dialog, " Help ", y, x + 14, selected==1);
37
38 wmove(dialog, y, x+1+14*selected);
39 wrefresh(dialog);
40}
41
42/*
43 * Display a dialog box for inputing a string
44 */
45int
46dialog_inputbox (const char *title, const char *prompt, int height, int width,
47 const char *init)
48{
49 int i, x, y, box_y, box_x, box_width;
50 int input_x = 0, scroll = 0, key = 0, button = -1;
51 unsigned char *instr = dialog_input_result;
52 WINDOW *dialog;
53
54 /* center dialog box on screen */
55 x = (COLS - width) / 2;
56 y = (LINES - height) / 2;
57
58
59 draw_shadow (stdscr, y, x, height, width);
60
61 dialog = newwin (height, width, y, x);
62 keypad (dialog, TRUE);
63
64 draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
65 wattrset (dialog, border_attr);
66 mvwaddch (dialog, height-3, 0, ACS_LTEE);
67 for (i = 0; i < width - 2; i++)
68 waddch (dialog, ACS_HLINE);
69 wattrset (dialog, dialog_attr);
70 waddch (dialog, ACS_RTEE);
71
72 if (title != NULL && strlen(title) >= width-2 ) {
73 /* truncate long title -- mec */
74 char * title2 = malloc(width-2+1);
75 memcpy( title2, title, width-2 );
76 title2[width-2] = '\0';
77 title = title2;
78 }
79
80 if (title != NULL) {
81 wattrset (dialog, title_attr);
82 mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
83 waddstr (dialog, (char *)title);
84 waddch (dialog, ' ');
85 }
86
87 wattrset (dialog, dialog_attr);
88 print_autowrap (dialog, prompt, width - 2, 1, 3);
89
90 /* Draw the input field box */
91 box_width = width - 6;
92 getyx (dialog, y, x);
93 box_y = y + 2;
94 box_x = (width - box_width) / 2;
95 draw_box (dialog, y + 1, box_x - 1, 3, box_width + 2,
96 border_attr, dialog_attr);
97
98 print_buttons(dialog, height, width, 0);
99
100 /* Set up the initial value */
101 wmove (dialog, box_y, box_x);
102 wattrset (dialog, inputbox_attr);
103
104 if (!init)
105 instr[0] = '\0';
106 else
107 strcpy (instr, init);
108
109 input_x = strlen (instr);
110
111 if (input_x >= box_width) {
112 scroll = input_x - box_width + 1;
113 input_x = box_width - 1;
114 for (i = 0; i < box_width - 1; i++)
115 waddch (dialog, instr[scroll + i]);
116 } else
117 waddstr (dialog, instr);
118
119 wmove (dialog, box_y, box_x + input_x);
120
121 wrefresh (dialog);
122
123 while (key != ESC) {
124 key = wgetch (dialog);
125
126 if (button == -1) { /* Input box selected */
127 switch (key) {
128 case TAB:
129 case KEY_UP:
130 case KEY_DOWN:
131 break;
132 case KEY_LEFT:
133 continue;
134 case KEY_RIGHT:
135 continue;
136 case KEY_BACKSPACE:
137 case 127:
138 if (input_x || scroll) {
139 wattrset (dialog, inputbox_attr);
140 if (!input_x) {
141 scroll = scroll < box_width - 1 ?
142 0 : scroll - (box_width - 1);
143 wmove (dialog, box_y, box_x);
144 for (i = 0; i < box_width; i++)
145 waddch (dialog, instr[scroll + input_x + i] ?
146 instr[scroll + input_x + i] : ' ');
147 input_x = strlen (instr) - scroll;
148 } else
149 input_x--;
150 instr[scroll + input_x] = '\0';
151 mvwaddch (dialog, box_y, input_x + box_x, ' ');
152 wmove (dialog, box_y, input_x + box_x);
153 wrefresh (dialog);
154 }
155 continue;
156 default:
157 if (key < 0x100 && isprint (key)) {
158 if (scroll + input_x < MAX_LEN) {
159 wattrset (dialog, inputbox_attr);
160 instr[scroll + input_x] = key;
161 instr[scroll + input_x + 1] = '\0';
162 if (input_x == box_width - 1) {
163 scroll++;
164 wmove (dialog, box_y, box_x);
165 for (i = 0; i < box_width - 1; i++)
166 waddch (dialog, instr[scroll + i]);
167 } else {
168 wmove (dialog, box_y, input_x++ + box_x);
169 waddch (dialog, key);
170 }
171 wrefresh (dialog);
172 } else
173 flash (); /* Alarm user about overflow */
174 continue;
175 }
176 }
177 }
178 switch (key) {
179 case 'O':
180 case 'o':
181 delwin (dialog);
182 return 0;
183 case 'H':
184 case 'h':
185 delwin (dialog);
186 return 1;
187 case KEY_UP:
188 case KEY_LEFT:
189 switch (button) {
190 case -1:
191 button = 1; /* Indicates "Cancel" button is selected */
192 print_buttons(dialog, height, width, 1);
193 break;
194 case 0:
195 button = -1; /* Indicates input box is selected */
196 print_buttons(dialog, height, width, 0);
197 wmove (dialog, box_y, box_x + input_x);
198 wrefresh (dialog);
199 break;
200 case 1:
201 button = 0; /* Indicates "OK" button is selected */
202 print_buttons(dialog, height, width, 0);
203 break;
204 }
205 break;
206 case TAB:
207 case KEY_DOWN:
208 case KEY_RIGHT:
209 switch (button) {
210 case -1:
211 button = 0; /* Indicates "OK" button is selected */
212 print_buttons(dialog, height, width, 0);
213 break;
214 case 0:
215 button = 1; /* Indicates "Cancel" button is selected */
216 print_buttons(dialog, height, width, 1);
217 break;
218 case 1:
219 button = -1; /* Indicates input box is selected */
220 print_buttons(dialog, height, width, 0);
221 wmove (dialog, box_y, box_x + input_x);
222 wrefresh (dialog);
223 break;
224 }
225 break;
226 case ' ':
227 case '\n':
228 delwin (dialog);
229 return (button == -1 ? 0 : button);
230 case 'X':
231 case 'x':
232 key = ESC;
233 case ESC:
234 break;
235 }
236 }
237
238 delwin (dialog);
239 return -1; /* ESC pressed */
240}
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 @@
1
2#line 3 "lex.zconf.c"
3
4#define YY_INT_ALIGNED short int
5
6/* A lexical scanner generated by flex */
7
8#define FLEX_SCANNER
9#define YY_FLEX_MAJOR_VERSION 2
10#define YY_FLEX_MINOR_VERSION 5
11#define YY_FLEX_SUBMINOR_VERSION 31
12#if YY_FLEX_SUBMINOR_VERSION > 0
13#define FLEX_BETA
14#endif
15
16/* First, we deal with platform-specific or compiler-specific issues. */
17
18/* begin standard C headers. */
19#include <stdio.h>
20#include <string.h>
21#include <errno.h>
22#include <stdlib.h>
23
24/* end standard C headers. */
25
26/* flex integer type definitions */
27
28#ifndef FLEXINT_H
29#define FLEXINT_H
30
31/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
32
33#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
34#include <inttypes.h>
35typedef int8_t flex_int8_t;
36typedef uint8_t flex_uint8_t;
37typedef int16_t flex_int16_t;
38typedef uint16_t flex_uint16_t;
39typedef int32_t flex_int32_t;
40typedef uint32_t flex_uint32_t;
41#else
42typedef signed char flex_int8_t;
43typedef short int flex_int16_t;
44typedef int flex_int32_t;
45typedef unsigned char flex_uint8_t;
46typedef unsigned short int flex_uint16_t;
47typedef unsigned int flex_uint32_t;
48#endif /* ! C99 */
49
50/* Limits of integral types. */
51#ifndef INT8_MIN
52#define INT8_MIN (-128)
53#endif
54#ifndef INT16_MIN
55#define INT16_MIN (-32767-1)
56#endif
57#ifndef INT32_MIN
58#define INT32_MIN (-2147483647-1)
59#endif
60#ifndef INT8_MAX
61#define INT8_MAX (127)
62#endif
63#ifndef INT16_MAX
64#define INT16_MAX (32767)
65#endif
66#ifndef INT32_MAX
67#define INT32_MAX (2147483647)
68#endif
69#ifndef UINT8_MAX
70#define UINT8_MAX (255U)
71#endif
72#ifndef UINT16_MAX
73#define UINT16_MAX (65535U)
74#endif
75#ifndef UINT32_MAX
76#define UINT32_MAX (4294967295U)
77#endif
78
79#endif /* ! FLEXINT_H */
80
81#ifdef __cplusplus
82
83/* The "const" storage-class-modifier is valid. */
84#define YY_USE_CONST
85
86#else /* ! __cplusplus */
87
88#if __STDC__
89
90#define YY_USE_CONST
91
92#endif /* __STDC__ */
93#endif /* ! __cplusplus */
94
95#ifdef YY_USE_CONST
96#define yyconst const
97#else
98#define yyconst
99#endif
100
101/* Returned upon end-of-file. */
102#define YY_NULL 0
103
104/* Promotes a possibly negative, possibly signed char to an unsigned
105 * integer for use as an array index. If the signed char is negative,
106 * we want to instead treat it as an 8-bit unsigned char, hence the
107 * double cast.
108 */
109#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
110
111/* Enter a start condition. This macro really ought to take a parameter,
112 * but we do it the disgusting crufty way forced on us by the ()-less
113 * definition of BEGIN.
114 */
115#define BEGIN (yy_start) = 1 + 2 *
116
117/* Translate the current start state into a value that can be later handed
118 * to BEGIN to return to the state. The YYSTATE alias is for lex
119 * compatibility.
120 */
121#define YY_START (((yy_start) - 1) / 2)
122#define YYSTATE YY_START
123
124/* Action number for EOF rule of a given start state. */
125#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
126
127/* Special action meaning "start processing a new file". */
128#define YY_NEW_FILE zconfrestart(zconfin )
129
130#define YY_END_OF_BUFFER_CHAR 0
131
132/* Size of default input buffer. */
133#ifndef YY_BUF_SIZE
134#define YY_BUF_SIZE 16384
135#endif
136
137#ifndef YY_TYPEDEF_YY_BUFFER_STATE
138#define YY_TYPEDEF_YY_BUFFER_STATE
139typedef struct yy_buffer_state *YY_BUFFER_STATE;
140#endif
141
142extern int zconfleng;
143
144extern FILE *zconfin, *zconfout;
145
146#define EOB_ACT_CONTINUE_SCAN 0
147#define EOB_ACT_END_OF_FILE 1
148#define EOB_ACT_LAST_MATCH 2
149
150 #define YY_LESS_LINENO(n)
151
152/* Return all but the first "n" matched characters back to the input stream. */
153#define yyless(n) \
154 do \
155 { \
156 /* Undo effects of setting up zconftext. */ \
157 int yyless_macro_arg = (n); \
158 YY_LESS_LINENO(yyless_macro_arg);\
159 *yy_cp = (yy_hold_char); \
160 YY_RESTORE_YY_MORE_OFFSET \
161 (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
162 YY_DO_BEFORE_ACTION; /* set up zconftext again */ \
163 } \
164 while ( 0 )
165
166#define unput(c) yyunput( c, (yytext_ptr) )
167
168/* The following is because we cannot portably get our hands on size_t
169 * (without autoconf's help, which isn't available because we want
170 * flex-generated scanners to compile on their own).
171 */
172
173#ifndef YY_TYPEDEF_YY_SIZE_T
174#define YY_TYPEDEF_YY_SIZE_T
175typedef unsigned int yy_size_t;
176#endif
177
178#ifndef YY_STRUCT_YY_BUFFER_STATE
179#define YY_STRUCT_YY_BUFFER_STATE
180struct yy_buffer_state
181 {
182 FILE *yy_input_file;
183
184 char *yy_ch_buf; /* input buffer */
185 char *yy_buf_pos; /* current position in input buffer */
186
187 /* Size of input buffer in bytes, not including room for EOB
188 * characters.
189 */
190 yy_size_t yy_buf_size;
191
192 /* Number of characters read into yy_ch_buf, not including EOB
193 * characters.
194 */
195 int yy_n_chars;
196
197 /* Whether we "own" the buffer - i.e., we know we created it,
198 * and can realloc() it to grow it, and should free() it to
199 * delete it.
200 */
201 int yy_is_our_buffer;
202
203 /* Whether this is an "interactive" input source; if so, and
204 * if we're using stdio for input, then we want to use getc()
205 * instead of fread(), to make sure we stop fetching input after
206 * each newline.
207 */
208 int yy_is_interactive;
209
210 /* Whether we're considered to be at the beginning of a line.
211 * If so, '^' rules will be active on the next match, otherwise
212 * not.
213 */
214 int yy_at_bol;
215
216 int yy_bs_lineno; /**< The line count. */
217 int yy_bs_column; /**< The column count. */
218
219 /* Whether to try to fill the input buffer when we reach the
220 * end of it.
221 */
222 int yy_fill_buffer;
223
224 int yy_buffer_status;
225
226#define YY_BUFFER_NEW 0
227#define YY_BUFFER_NORMAL 1
228 /* When an EOF's been seen but there's still some text to process
229 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
230 * shouldn't try reading from the input source any more. We might
231 * still have a bunch of tokens to match, though, because of
232 * possible backing-up.
233 *
234 * When we actually see the EOF, we change the status to "new"
235 * (via zconfrestart()), so that the user can continue scanning by
236 * just pointing zconfin at a new input file.
237 */
238#define YY_BUFFER_EOF_PENDING 2
239
240 };
241#endif /* !YY_STRUCT_YY_BUFFER_STATE */
242
243/* Stack of input buffers. */
244static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
245static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
246static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
247
248/* We provide macros for accessing buffer states in case in the
249 * future we want to put the buffer states in a more general
250 * "scanner state".
251 *
252 * Returns the top of the stack, or NULL.
253 */
254#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
255 ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
256 : NULL)
257
258/* Same as previous macro, but useful when we know that the buffer stack is not
259 * NULL or when we need an lvalue. For internal use only.
260 */
261#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
262
263/* yy_hold_char holds the character lost when zconftext is formed. */
264static char yy_hold_char;
265static int yy_n_chars; /* number of characters read into yy_ch_buf */
266int zconfleng;
267
268/* Points to current character in buffer. */
269static char *yy_c_buf_p = (char *) 0;
270static int yy_init = 1; /* whether we need to initialize */
271static int yy_start = 0; /* start state number */
272
273/* Flag which is used to allow zconfwrap()'s to do buffer switches
274 * instead of setting up a fresh zconfin. A bit of a hack ...
275 */
276static int yy_did_buffer_switch_on_eof;
277
278void zconfrestart (FILE *input_file );
279void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer );
280YY_BUFFER_STATE zconf_create_buffer (FILE *file,int size );
281void zconf_delete_buffer (YY_BUFFER_STATE b );
282void zconf_flush_buffer (YY_BUFFER_STATE b );
283void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer );
284void zconfpop_buffer_state (void );
285
286static void zconfensure_buffer_stack (void );
287static void zconf_load_buffer_state (void );
288static void zconf_init_buffer (YY_BUFFER_STATE b,FILE *file );
289
290#define YY_FLUSH_BUFFER zconf_flush_buffer(YY_CURRENT_BUFFER )
291
292YY_BUFFER_STATE zconf_scan_buffer (char *base,yy_size_t size );
293YY_BUFFER_STATE zconf_scan_string (yyconst char *yy_str );
294YY_BUFFER_STATE zconf_scan_bytes (yyconst char *bytes,int len );
295
296void *zconfalloc (yy_size_t );
297void *zconfrealloc (void *,yy_size_t );
298void zconffree (void * );
299
300#define yy_new_buffer zconf_create_buffer
301
302#define yy_set_interactive(is_interactive) \
303 { \
304 if ( ! YY_CURRENT_BUFFER ){ \
305 zconfensure_buffer_stack (); \
306 YY_CURRENT_BUFFER_LVALUE = \
307 zconf_create_buffer(zconfin,YY_BUF_SIZE ); \
308 } \
309 YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
310 }
311
312#define yy_set_bol(at_bol) \
313 { \
314 if ( ! YY_CURRENT_BUFFER ){\
315 zconfensure_buffer_stack (); \
316 YY_CURRENT_BUFFER_LVALUE = \
317 zconf_create_buffer(zconfin,YY_BUF_SIZE ); \
318 } \
319 YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
320 }
321
322#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
323
324/* Begin user sect3 */
325
326#define zconfwrap(n) 1
327#define YY_SKIP_YYWRAP
328
329typedef unsigned char YY_CHAR;
330
331FILE *zconfin = (FILE *) 0, *zconfout = (FILE *) 0;
332
333typedef int yy_state_type;
334
335extern int zconflineno;
336
337int zconflineno = 1;
338
339extern char *zconftext;
340#define yytext_ptr zconftext
341static yyconst flex_int16_t yy_nxt[][38] =
342 {
343 {
344 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
345 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
346 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
347 0, 0, 0, 0, 0, 0, 0, 0
348 },
349
350 {
351 11, 12, 13, 14, 12, 12, 15, 12, 12, 12,
352 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
353 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
354 12, 12, 12, 12, 12, 12, 12, 12
355 },
356
357 {
358 11, 12, 13, 14, 12, 12, 15, 12, 12, 12,
359 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
360
361 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
362 12, 12, 12, 12, 12, 12, 12, 12
363 },
364
365 {
366 11, 16, 16, 17, 16, 16, 16, 16, 16, 16,
367 16, 16, 16, 18, 16, 16, 18, 18, 19, 20,
368 21, 22, 18, 18, 23, 24, 18, 25, 18, 26,
369 27, 18, 28, 29, 30, 18, 18, 16
370 },
371
372 {
373 11, 16, 16, 17, 16, 16, 16, 16, 16, 16,
374 16, 16, 16, 18, 16, 16, 18, 18, 19, 20,
375 21, 22, 18, 18, 23, 24, 18, 25, 18, 26,
376 27, 18, 28, 29, 30, 18, 18, 16
377
378 },
379
380 {
381 11, 31, 32, 33, 31, 31, 31, 31, 31, 31,
382 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
383 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
384 31, 31, 31, 31, 31, 31, 31, 31
385 },
386
387 {
388 11, 31, 32, 33, 31, 31, 31, 31, 31, 31,
389 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
390 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
391 31, 31, 31, 31, 31, 31, 31, 31
392 },
393
394 {
395 11, 34, 34, 35, 34, 36, 34, 34, 36, 34,
396 34, 34, 34, 34, 34, 37, 34, 34, 34, 34,
397
398 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
399 34, 34, 34, 34, 34, 34, 34, 34
400 },
401
402 {
403 11, 34, 34, 35, 34, 36, 34, 34, 36, 34,
404 34, 34, 34, 34, 34, 37, 34, 34, 34, 34,
405 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
406 34, 34, 34, 34, 34, 34, 34, 34
407 },
408
409 {
410 11, 38, 38, 39, 40, 41, 42, 43, 41, 44,
411 45, 46, 47, 47, 48, 49, 47, 47, 47, 47,
412 47, 47, 47, 47, 47, 50, 47, 47, 47, 51,
413 47, 47, 47, 47, 47, 47, 47, 52
414
415 },
416
417 {
418 11, 38, 38, 39, 40, 41, 42, 43, 41, 44,
419 45, 46, 47, 47, 48, 49, 47, 47, 47, 47,
420 47, 47, 47, 47, 47, 50, 47, 47, 47, 51,
421 47, 47, 47, 47, 47, 47, 47, 52
422 },
423
424 {
425 -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
426 -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
427 -11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
428 -11, -11, -11, -11, -11, -11, -11, -11
429 },
430
431 {
432 11, -12, -12, -12, -12, -12, -12, -12, -12, -12,
433 -12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
434
435 -12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
436 -12, -12, -12, -12, -12, -12, -12, -12
437 },
438
439 {
440 11, -13, 53, 54, -13, -13, 55, -13, -13, -13,
441 -13, -13, -13, -13, -13, -13, -13, -13, -13, -13,
442 -13, -13, -13, -13, -13, -13, -13, -13, -13, -13,
443 -13, -13, -13, -13, -13, -13, -13, -13
444 },
445
446 {
447 11, -14, -14, -14, -14, -14, -14, -14, -14, -14,
448 -14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
449 -14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
450 -14, -14, -14, -14, -14, -14, -14, -14
451
452 },
453
454 {
455 11, 56, 56, 57, 56, 56, 56, 56, 56, 56,
456 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
457 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
458 56, 56, 56, 56, 56, 56, 56, 56
459 },
460
461 {
462 11, -16, -16, -16, -16, -16, -16, -16, -16, -16,
463 -16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
464 -16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
465 -16, -16, -16, -16, -16, -16, -16, -16
466 },
467
468 {
469 11, -17, -17, -17, -17, -17, -17, -17, -17, -17,
470 -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
471
472 -17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
473 -17, -17, -17, -17, -17, -17, -17, -17
474 },
475
476 {
477 11, -18, -18, -18, -18, -18, -18, -18, -18, -18,
478 -18, -18, -18, 58, -18, -18, 58, 58, 58, 58,
479 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
480 58, 58, 58, 58, 58, 58, 58, -18
481 },
482
483 {
484 11, -19, -19, -19, -19, -19, -19, -19, -19, -19,
485 -19, -19, -19, 58, -19, -19, 58, 58, 58, 58,
486 58, 58, 58, 58, 58, 58, 58, 58, 58, 59,
487 58, 58, 58, 58, 58, 58, 58, -19
488
489 },
490
491 {
492 11, -20, -20, -20, -20, -20, -20, -20, -20, -20,
493 -20, -20, -20, 58, -20, -20, 58, 58, 58, 58,
494 58, 58, 58, 58, 60, 58, 58, 58, 58, 61,
495 58, 58, 58, 58, 58, 58, 58, -20
496 },
497
498 {
499 11, -21, -21, -21, -21, -21, -21, -21, -21, -21,
500 -21, -21, -21, 58, -21, -21, 58, 58, 58, 58,
501 58, 62, 58, 58, 58, 58, 58, 58, 58, 58,
502 58, 58, 58, 58, 58, 58, 58, -21
503 },
504
505 {
506 11, -22, -22, -22, -22, -22, -22, -22, -22, -22,
507 -22, -22, -22, 58, -22, -22, 58, 58, 58, 58,
508
509 58, 58, 58, 58, 58, 58, 58, 58, 63, 58,
510 58, 58, 58, 58, 58, 58, 58, -22
511 },
512
513 {
514 11, -23, -23, -23, -23, -23, -23, -23, -23, -23,
515 -23, -23, -23, 58, -23, -23, 58, 58, 58, 58,
516 58, 64, 58, 58, 58, 58, 58, 58, 58, 58,
517 58, 58, 58, 58, 58, 58, 58, -23
518 },
519
520 {
521 11, -24, -24, -24, -24, -24, -24, -24, -24, -24,
522 -24, -24, -24, 58, -24, -24, 58, 58, 58, 58,
523 58, 58, 65, 58, 58, 58, 58, 58, 66, 58,
524 58, 58, 58, 58, 58, 58, 58, -24
525
526 },
527
528 {
529 11, -25, -25, -25, -25, -25, -25, -25, -25, -25,
530 -25, -25, -25, 58, -25, -25, 58, 67, 58, 58,
531 58, 68, 58, 58, 58, 58, 58, 58, 58, 58,
532 58, 58, 58, 58, 58, 58, 58, -25
533 },
534
535 {
536 11, -26, -26, -26, -26, -26, -26, -26, -26, -26,
537 -26, -26, -26, 58, -26, -26, 58, 58, 58, 58,
538 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
539 69, 58, 58, 58, 58, 58, 58, -26
540 },
541
542 {
543 11, -27, -27, -27, -27, -27, -27, -27, -27, -27,
544 -27, -27, -27, 58, -27, -27, 58, 58, 58, 58,
545
546 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
547 58, 58, 70, 58, 58, 58, 58, -27
548 },
549
550 {
551 11, -28, -28, -28, -28, -28, -28, -28, -28, -28,
552 -28, -28, -28, 58, -28, -28, 58, 71, 58, 58,
553 58, 72, 58, 58, 58, 58, 58, 58, 58, 58,
554 58, 58, 58, 58, 58, 58, 58, -28
555 },
556
557 {
558 11, -29, -29, -29, -29, -29, -29, -29, -29, -29,
559 -29, -29, -29, 58, -29, -29, 58, 58, 58, 58,
560 58, 73, 58, 58, 58, 58, 58, 58, 58, 74,
561 58, 58, 58, 58, 75, 58, 58, -29
562
563 },
564
565 {
566 11, -30, -30, -30, -30, -30, -30, -30, -30, -30,
567 -30, -30, -30, 58, -30, -30, 58, 58, 58, 58,
568 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
569 58, 58, 76, 58, 58, 58, 58, -30
570 },
571
572 {
573 11, 77, 77, -31, 77, 77, 77, 77, 77, 77,
574 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
575 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
576 77, 77, 77, 77, 77, 77, 77, 77
577 },
578
579 {
580 11, -32, 78, 79, -32, -32, -32, -32, -32, -32,
581 -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
582
583 -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
584 -32, -32, -32, -32, -32, -32, -32, -32
585 },
586
587 {
588 11, 80, -33, -33, 80, 80, 80, 80, 80, 80,
589 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
590 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
591 80, 80, 80, 80, 80, 80, 80, 80
592 },
593
594 {
595 11, 81, 81, 82, 81, -34, 81, 81, -34, 81,
596 81, 81, 81, 81, 81, -34, 81, 81, 81, 81,
597 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
598 81, 81, 81, 81, 81, 81, 81, 81
599
600 },
601
602 {
603 11, -35, -35, -35, -35, -35, -35, -35, -35, -35,
604 -35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
605 -35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
606 -35, -35, -35, -35, -35, -35, -35, -35
607 },
608
609 {
610 11, -36, -36, -36, -36, -36, -36, -36, -36, -36,
611 -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
612 -36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
613 -36, -36, -36, -36, -36, -36, -36, -36
614 },
615
616 {
617 11, 83, 83, 84, 83, 83, 83, 83, 83, 83,
618 83, 83, 83, 83, 83, 83, 83, 83, 83, 83,
619
620 83, 83, 83, 83, 83, 83, 83, 83, 83, 83,
621 83, 83, 83, 83, 83, 83, 83, 83
622 },
623
624 {
625 11, -38, -38, -38, -38, -38, -38, -38, -38, -38,
626 -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
627 -38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
628 -38, -38, -38, -38, -38, -38, -38, -38
629 },
630
631 {
632 11, -39, -39, -39, -39, -39, -39, -39, -39, -39,
633 -39, -39, -39, -39, -39, -39, -39, -39, -39, -39,
634 -39, -39, -39, -39, -39, -39, -39, -39, -39, -39,
635 -39, -39, -39, -39, -39, -39, -39, -39
636
637 },
638
639 {
640 11, -40, -40, -40, -40, -40, -40, -40, -40, -40,
641 -40, -40, -40, -40, 85, -40, -40, -40, -40, -40,
642 -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
643 -40, -40, -40, -40, -40, -40, -40, -40
644 },
645
646 {
647 11, -41, -41, -41, -41, -41, -41, -41, -41, -41,
648 -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
649 -41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
650 -41, -41, -41, -41, -41, -41, -41, -41
651 },
652
653 {
654 11, 86, 86, -42, 86, 86, 86, 86, 86, 86,
655 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
656
657 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
658 86, 86, 86, 86, 86, 86, 86, 86
659 },
660
661 {
662 11, -43, -43, -43, -43, -43, -43, 87, -43, -43,
663 -43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
664 -43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
665 -43, -43, -43, -43, -43, -43, -43, -43
666 },
667
668 {
669 11, -44, -44, -44, -44, -44, -44, -44, -44, -44,
670 -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
671 -44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
672 -44, -44, -44, -44, -44, -44, -44, -44
673
674 },
675
676 {
677 11, -45, -45, -45, -45, -45, -45, -45, -45, -45,
678 -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
679 -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
680 -45, -45, -45, -45, -45, -45, -45, -45
681 },
682
683 {
684 11, -46, -46, -46, -46, -46, -46, -46, -46, -46,
685 -46, 88, 89, 89, -46, -46, 89, 89, 89, 89,
686 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
687 89, 89, 89, 89, 89, 89, 89, -46
688 },
689
690 {
691 11, -47, -47, -47, -47, -47, -47, -47, -47, -47,
692 -47, 89, 89, 89, -47, -47, 89, 89, 89, 89,
693
694 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
695 89, 89, 89, 89, 89, 89, 89, -47
696 },
697
698 {
699 11, -48, -48, -48, -48, -48, -48, -48, -48, -48,
700 -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
701 -48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
702 -48, -48, -48, -48, -48, -48, -48, -48
703 },
704
705 {
706 11, -49, -49, 90, -49, -49, -49, -49, -49, -49,
707 -49, -49, -49, -49, -49, -49, -49, -49, -49, -49,
708 -49, -49, -49, -49, -49, -49, -49, -49, -49, -49,
709 -49, -49, -49, -49, -49, -49, -49, -49
710
711 },
712
713 {
714 11, -50, -50, -50, -50, -50, -50, -50, -50, -50,
715 -50, 89, 89, 89, -50, -50, 89, 89, 89, 89,
716 89, 89, 91, 89, 89, 89, 89, 89, 89, 89,
717 89, 89, 89, 89, 89, 89, 89, -50
718 },
719
720 {
721 11, -51, -51, -51, -51, -51, -51, -51, -51, -51,
722 -51, 89, 89, 89, -51, -51, 89, 89, 89, 89,
723 89, 89, 89, 89, 89, 89, 89, 89, 92, 89,
724 89, 89, 89, 89, 89, 89, 89, -51
725 },
726
727 {
728 11, -52, -52, -52, -52, -52, -52, -52, -52, -52,
729 -52, -52, -52, -52, -52, -52, -52, -52, -52, -52,
730
731 -52, -52, -52, -52, -52, -52, -52, -52, -52, -52,
732 -52, -52, -52, -52, -52, -52, -52, 93
733 },
734
735 {
736 11, -53, 53, 54, -53, -53, 55, -53, -53, -53,
737 -53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
738 -53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
739 -53, -53, -53, -53, -53, -53, -53, -53
740 },
741
742 {
743 11, -54, -54, -54, -54, -54, -54, -54, -54, -54,
744 -54, -54, -54, -54, -54, -54, -54, -54, -54, -54,
745 -54, -54, -54, -54, -54, -54, -54, -54, -54, -54,
746 -54, -54, -54, -54, -54, -54, -54, -54
747
748 },
749
750 {
751 11, 56, 56, 57, 56, 56, 56, 56, 56, 56,
752 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
753 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
754 56, 56, 56, 56, 56, 56, 56, 56
755 },
756
757 {
758 11, 56, 56, 57, 56, 56, 56, 56, 56, 56,
759 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
760 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
761 56, 56, 56, 56, 56, 56, 56, 56
762 },
763
764 {
765 11, -57, -57, -57, -57, -57, -57, -57, -57, -57,
766 -57, -57, -57, -57, -57, -57, -57, -57, -57, -57,
767
768 -57, -57, -57, -57, -57, -57, -57, -57, -57, -57,
769 -57, -57, -57, -57, -57, -57, -57, -57
770 },
771
772 {
773 11, -58, -58, -58, -58, -58, -58, -58, -58, -58,
774 -58, -58, -58, 58, -58, -58, 58, 58, 58, 58,
775 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
776 58, 58, 58, 58, 58, 58, 58, -58
777 },
778
779 {
780 11, -59, -59, -59, -59, -59, -59, -59, -59, -59,
781 -59, -59, -59, 58, -59, -59, 58, 58, 58, 58,
782 58, 58, 58, 58, 58, 58, 58, 58, 58, 94,
783 58, 58, 58, 58, 58, 58, 58, -59
784
785 },
786
787 {
788 11, -60, -60, -60, -60, -60, -60, -60, -60, -60,
789 -60, -60, -60, 58, -60, -60, 58, 58, 58, 58,
790 58, 58, 58, 58, 58, 58, 58, 58, 58, 95,
791 58, 58, 58, 58, 58, 58, 58, -60
792 },
793
794 {
795 11, -61, -61, -61, -61, -61, -61, -61, -61, -61,
796 -61, -61, -61, 58, -61, -61, 58, 58, 58, 58,
797 58, 58, 58, 58, 58, 58, 58, 96, 97, 58,
798 58, 58, 58, 58, 58, 58, 58, -61
799 },
800
801 {
802 11, -62, -62, -62, -62, -62, -62, -62, -62, -62,
803 -62, -62, -62, 58, -62, -62, 58, 58, 58, 58,
804
805 58, 58, 98, 58, 58, 58, 58, 58, 58, 58,
806 99, 58, 58, 58, 58, 58, 58, -62
807 },
808
809 {
810 11, -63, -63, -63, -63, -63, -63, -63, -63, -63,
811 -63, -63, -63, 58, -63, -63, 58, 100, 58, 58,
812 101, 58, 58, 58, 58, 58, 58, 58, 58, 58,
813 58, 58, 58, 58, 58, 58, 58, -63
814 },
815
816 {
817 11, -64, -64, -64, -64, -64, -64, -64, -64, -64,
818 -64, -64, -64, 58, -64, -64, 58, 58, 58, 58,
819 58, 58, 58, 58, 58, 58, 102, 58, 58, 58,
820 58, 58, 58, 58, 58, 58, 103, -64
821
822 },
823
824 {
825 11, -65, -65, -65, -65, -65, -65, -65, -65, -65,
826 -65, -65, -65, 58, -65, -65, 58, 58, 58, 58,
827 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
828 58, 58, 58, 58, 58, 58, 58, -65
829 },
830
831 {
832 11, -66, -66, -66, -66, -66, -66, -66, -66, -66,
833 -66, -66, -66, 58, -66, -66, 58, 58, 58, 58,
834 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
835 58, 58, 58, 58, 104, 58, 58, -66
836 },
837
838 {
839 11, -67, -67, -67, -67, -67, -67, -67, -67, -67,
840 -67, -67, -67, 58, -67, -67, 58, 58, 58, 58,
841
842 58, 58, 58, 58, 58, 105, 58, 58, 58, 58,
843 58, 58, 58, 58, 58, 58, 58, -67
844 },
845
846 {
847 11, -68, -68, -68, -68, -68, -68, -68, -68, -68,
848 -68, -68, -68, 58, -68, -68, 58, 58, 58, 58,
849 58, 58, 58, 58, 58, 58, 58, 58, 106, 58,
850 58, 58, 58, 58, 58, 58, 58, -68
851 },
852
853 {
854 11, -69, -69, -69, -69, -69, -69, -69, -69, -69,
855 -69, -69, -69, 58, -69, -69, 58, 58, 58, 58,
856 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
857 58, 58, 58, 58, 107, 58, 58, -69
858
859 },
860
861 {
862 11, -70, -70, -70, -70, -70, -70, -70, -70, -70,
863 -70, -70, -70, 58, -70, -70, 58, 58, 58, 58,
864 58, 58, 58, 58, 58, 58, 58, 58, 58, 108,
865 58, 58, 58, 58, 58, 58, 58, -70
866 },
867
868 {
869 11, -71, -71, -71, -71, -71, -71, -71, -71, -71,
870 -71, -71, -71, 58, -71, -71, 58, 58, 58, 58,
871 58, 58, 58, 58, 58, 58, 58, 58, 109, 58,
872 58, 58, 58, 58, 58, 58, 58, -71
873 },
874
875 {
876 11, -72, -72, -72, -72, -72, -72, -72, -72, -72,
877 -72, -72, -72, 58, -72, -72, 58, 58, 58, 58,
878
879 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
880 58, 110, 58, 58, 58, 58, 58, -72
881 },
882
883 {
884 11, -73, -73, -73, -73, -73, -73, -73, -73, -73,
885 -73, -73, -73, 58, -73, -73, 58, 58, 58, 58,
886 58, 58, 58, 58, 58, 58, 111, 58, 58, 58,
887 58, 58, 58, 58, 58, 58, 58, -73
888 },
889
890 {
891 11, -74, -74, -74, -74, -74, -74, -74, -74, -74,
892 -74, -74, -74, 58, -74, -74, 58, 58, 58, 58,
893 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
894 58, 58, 58, 58, 58, 112, 58, -74
895
896 },
897
898 {
899 11, -75, -75, -75, -75, -75, -75, -75, -75, -75,
900 -75, -75, -75, 58, -75, -75, 58, 58, 58, 58,
901 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
902 58, 58, 113, 58, 58, 58, 58, -75
903 },
904
905 {
906 11, -76, -76, -76, -76, -76, -76, -76, -76, -76,
907 -76, -76, -76, 58, -76, -76, 58, 58, 58, 58,
908 58, 58, 58, 58, 58, 114, 58, 58, 58, 58,
909 58, 58, 58, 58, 58, 58, 58, -76
910 },
911
912 {
913 11, 77, 77, -77, 77, 77, 77, 77, 77, 77,
914 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
915
916 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
917 77, 77, 77, 77, 77, 77, 77, 77
918 },
919
920 {
921 11, -78, 78, 79, -78, -78, -78, -78, -78, -78,
922 -78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
923 -78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
924 -78, -78, -78, -78, -78, -78, -78, -78
925 },
926
927 {
928 11, 80, -79, -79, 80, 80, 80, 80, 80, 80,
929 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
930 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
931 80, 80, 80, 80, 80, 80, 80, 80
932
933 },
934
935 {
936 11, -80, -80, -80, -80, -80, -80, -80, -80, -80,
937 -80, -80, -80, -80, -80, -80, -80, -80, -80, -80,
938 -80, -80, -80, -80, -80, -80, -80, -80, -80, -80,
939 -80, -80, -80, -80, -80, -80, -80, -80
940 },
941
942 {
943 11, 81, 81, 82, 81, -81, 81, 81, -81, 81,
944 81, 81, 81, 81, 81, -81, 81, 81, 81, 81,
945 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
946 81, 81, 81, 81, 81, 81, 81, 81
947 },
948
949 {
950 11, -82, -82, -82, -82, -82, -82, -82, -82, -82,
951 -82, -82, -82, -82, -82, -82, -82, -82, -82, -82,
952
953 -82, -82, -82, -82, -82, -82, -82, -82, -82, -82,
954 -82, -82, -82, -82, -82, -82, -82, -82
955 },
956
957 {
958 11, -83, -83, 84, -83, -83, -83, -83, -83, -83,
959 -83, -83, -83, -83, -83, -83, -83, -83, -83, -83,
960 -83, -83, -83, -83, -83, -83, -83, -83, -83, -83,
961 -83, -83, -83, -83, -83, -83, -83, -83
962 },
963
964 {
965 11, -84, -84, -84, -84, -84, -84, -84, -84, -84,
966 -84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
967 -84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
968 -84, -84, -84, -84, -84, -84, -84, -84
969
970 },
971
972 {
973 11, -85, -85, -85, -85, -85, -85, -85, -85, -85,
974 -85, -85, -85, -85, -85, -85, -85, -85, -85, -85,
975 -85, -85, -85, -85, -85, -85, -85, -85, -85, -85,
976 -85, -85, -85, -85, -85, -85, -85, -85
977 },
978
979 {
980 11, 86, 86, -86, 86, 86, 86, 86, 86, 86,
981 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
982 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
983 86, 86, 86, 86, 86, 86, 86, 86
984 },
985
986 {
987 11, -87, -87, -87, -87, -87, -87, -87, -87, -87,
988 -87, -87, -87, -87, -87, -87, -87, -87, -87, -87,
989
990 -87, -87, -87, -87, -87, -87, -87, -87, -87, -87,
991 -87, -87, -87, -87, -87, -87, -87, -87
992 },
993
994 {
995 11, -88, -88, -88, -88, -88, -88, -88, -88, -88,
996 -88, 115, 89, 89, -88, -88, 89, 89, 89, 89,
997 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
998 89, 89, 89, 89, 89, 89, 89, -88
999 },
1000
1001 {
1002 11, -89, -89, -89, -89, -89, -89, -89, -89, -89,
1003 -89, 89, 89, 89, -89, -89, 89, 89, 89, 89,
1004 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
1005 89, 89, 89, 89, 89, 89, 89, -89
1006
1007 },
1008
1009 {
1010 11, -90, -90, -90, -90, -90, -90, -90, -90, -90,
1011 -90, -90, -90, -90, -90, -90, -90, -90, -90, -90,
1012 -90, -90, -90, -90, -90, -90, -90, -90, -90, -90,
1013 -90, -90, -90, -90, -90, -90, -90, -90
1014 },
1015
1016 {
1017 11, -91, -91, -91, -91, -91, -91, -91, -91, -91,
1018 -91, 89, 89, 89, -91, -91, 89, 89, 89, 89,
1019 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
1020 89, 89, 89, 89, 89, 89, 89, -91
1021 },
1022
1023 {
1024 11, -92, -92, -92, -92, -92, -92, -92, -92, -92,
1025 -92, 89, 89, 89, -92, -92, 89, 89, 89, 89,
1026
1027 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
1028 89, 89, 89, 89, 89, 89, 89, -92
1029 },
1030
1031 {
1032 11, -93, -93, -93, -93, -93, -93, -93, -93, -93,
1033 -93, -93, -93, -93, -93, -93, -93, -93, -93, -93,
1034 -93, -93, -93, -93, -93, -93, -93, -93, -93, -93,
1035 -93, -93, -93, -93, -93, -93, -93, -93
1036 },
1037
1038 {
1039 11, -94, -94, -94, -94, -94, -94, -94, -94, -94,
1040 -94, -94, -94, 58, -94, -94, 58, 58, 58, 58,
1041 58, 58, 58, 58, 58, 58, 116, 58, 58, 58,
1042 58, 58, 58, 58, 58, 58, 58, -94
1043
1044 },
1045
1046 {
1047 11, -95, -95, -95, -95, -95, -95, -95, -95, -95,
1048 -95, -95, -95, 58, -95, -95, 58, 58, 58, 58,
1049 58, 58, 58, 58, 58, 117, 58, 58, 58, 58,
1050 58, 58, 58, 58, 58, 58, 58, -95
1051 },
1052
1053 {
1054 11, -96, -96, -96, -96, -96, -96, -96, -96, -96,
1055 -96, -96, -96, 58, -96, -96, 58, 58, 58, 58,
1056 58, 58, 58, 58, 58, 58, 58, 118, 58, 58,
1057 58, 58, 58, 58, 58, 58, 58, -96
1058 },
1059
1060 {
1061 11, -97, -97, -97, -97, -97, -97, -97, -97, -97,
1062 -97, -97, -97, 58, -97, -97, 58, 58, 58, 58,
1063
1064 58, 58, 119, 58, 58, 58, 58, 58, 58, 58,
1065 58, 58, 58, 58, 58, 58, 58, -97
1066 },
1067
1068 {
1069 11, -98, -98, -98, -98, -98, -98, -98, -98, -98,
1070 -98, -98, -98, 58, -98, -98, 120, 121, 58, 58,
1071 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1072 58, 58, 58, 58, 58, 58, 58, -98
1073 },
1074
1075 {
1076 11, -99, -99, -99, -99, -99, -99, -99, -99, -99,
1077 -99, -99, -99, 58, -99, -99, 58, 58, 58, 58,
1078 58, 122, 58, 58, 58, 58, 58, 58, 58, 58,
1079 58, 58, 58, 58, 58, 58, 58, -99
1080
1081 },
1082
1083 {
1084 11, -100, -100, -100, -100, -100, -100, -100, -100, -100,
1085 -100, -100, -100, 58, -100, -100, 58, 58, 123, 58,
1086 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1087 58, 58, 58, 58, 58, 58, 58, -100
1088 },
1089
1090 {
1091 11, -101, -101, -101, -101, -101, -101, -101, -101, -101,
1092 -101, -101, -101, 58, -101, -101, 58, 58, 58, 124,
1093 58, 58, 58, 58, 58, 125, 58, 126, 58, 58,
1094 58, 58, 58, 58, 58, 58, 58, -101
1095 },
1096
1097 {
1098 11, -102, -102, -102, -102, -102, -102, -102, -102, -102,
1099 -102, -102, -102, 58, -102, -102, 58, 58, 58, 58,
1100
1101 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1102 127, 58, 58, 58, 58, 58, 58, -102
1103 },
1104
1105 {
1106 11, -103, -103, -103, -103, -103, -103, -103, -103, -103,
1107 -103, -103, -103, 58, -103, -103, 58, 58, 58, 58,
1108 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1109 58, 58, 58, 58, 58, 58, 58, -103
1110 },
1111
1112 {
1113 11, -104, -104, -104, -104, -104, -104, -104, -104, -104,
1114 -104, -104, -104, 58, -104, -104, 58, 58, 58, 58,
1115 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1116 58, 58, 58, 58, 58, 58, 58, -104
1117
1118 },
1119
1120 {
1121 11, -105, -105, -105, -105, -105, -105, -105, -105, -105,
1122 -105, -105, -105, 58, -105, -105, 58, 58, 58, 58,
1123 58, 58, 58, 58, 58, 58, 58, 58, 128, 58,
1124 58, 58, 58, 58, 58, 58, 58, -105
1125 },
1126
1127 {
1128 11, -106, -106, -106, -106, -106, -106, -106, -106, -106,
1129 -106, -106, -106, 58, -106, -106, 58, 58, 58, 58,
1130 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1131 58, 58, 58, 58, 58, 129, 58, -106
1132 },
1133
1134 {
1135 11, -107, -107, -107, -107, -107, -107, -107, -107, -107,
1136 -107, -107, -107, 58, -107, -107, 58, 58, 58, 58,
1137
1138 58, 58, 58, 58, 58, 130, 58, 58, 58, 58,
1139 58, 58, 58, 58, 58, 58, 58, -107
1140 },
1141
1142 {
1143 11, -108, -108, -108, -108, -108, -108, -108, -108, -108,
1144 -108, -108, -108, 58, -108, -108, 58, 58, 58, 58,
1145 58, 58, 58, 58, 58, 58, 58, 131, 58, 58,
1146 58, 58, 58, 58, 58, 58, 58, -108
1147 },
1148
1149 {
1150 11, -109, -109, -109, -109, -109, -109, -109, -109, -109,
1151 -109, -109, -109, 58, -109, -109, 58, 58, 58, 58,
1152 58, 58, 58, 132, 58, 58, 58, 58, 58, 58,
1153 58, 58, 58, 58, 58, 58, 58, -109
1154
1155 },
1156
1157 {
1158 11, -110, -110, -110, -110, -110, -110, -110, -110, -110,
1159 -110, -110, -110, 58, -110, -110, 58, 58, 58, 58,
1160 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1161 58, 58, 58, 58, 58, 133, 58, -110
1162 },
1163
1164 {
1165 11, -111, -111, -111, -111, -111, -111, -111, -111, -111,
1166 -111, -111, -111, 58, -111, -111, 58, 58, 58, 58,
1167 58, 134, 58, 58, 58, 58, 58, 58, 58, 58,
1168 58, 58, 58, 58, 58, 58, 58, -111
1169 },
1170
1171 {
1172 11, -112, -112, -112, -112, -112, -112, -112, -112, -112,
1173 -112, -112, -112, 58, -112, -112, 58, 58, 58, 58,
1174
1175 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1176 58, 58, 135, 58, 58, 58, 58, -112
1177 },
1178
1179 {
1180 11, -113, -113, -113, -113, -113, -113, -113, -113, -113,
1181 -113, -113, -113, 58, -113, -113, 58, 58, 58, 58,
1182 58, 58, 58, 58, 58, 136, 58, 58, 58, 58,
1183 58, 58, 58, 58, 58, 58, 58, -113
1184 },
1185
1186 {
1187 11, -114, -114, -114, -114, -114, -114, -114, -114, -114,
1188 -114, -114, -114, 58, -114, -114, 58, 58, 58, 58,
1189 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1190 58, 58, 58, 137, 58, 58, 58, -114
1191
1192 },
1193
1194 {
1195 11, -115, -115, -115, -115, -115, -115, -115, -115, -115,
1196 -115, 89, 89, 89, -115, -115, 89, 89, 89, 89,
1197 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
1198 89, 89, 89, 89, 89, 89, 89, -115
1199 },
1200
1201 {
1202 11, -116, -116, -116, -116, -116, -116, -116, -116, -116,
1203 -116, -116, -116, 58, -116, -116, 58, 58, 58, 58,
1204 58, 138, 58, 58, 58, 58, 58, 58, 58, 58,
1205 58, 58, 58, 58, 58, 58, 58, -116
1206 },
1207
1208 {
1209 11, -117, -117, -117, -117, -117, -117, -117, -117, -117,
1210 -117, -117, -117, 58, -117, -117, 58, 58, 58, 139,
1211
1212 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1213 58, 58, 58, 58, 58, 58, 58, -117
1214 },
1215
1216 {
1217 11, -118, -118, -118, -118, -118, -118, -118, -118, -118,
1218 -118, -118, -118, 58, -118, -118, 58, 58, 58, 58,
1219 58, 140, 58, 58, 58, 58, 58, 58, 58, 58,
1220 58, 58, 58, 58, 58, 58, 58, -118
1221 },
1222
1223 {
1224 11, -119, -119, -119, -119, -119, -119, -119, -119, -119,
1225 -119, -119, -119, 58, -119, -119, 58, 58, 58, 58,
1226 58, 58, 58, 58, 58, 141, 58, 58, 58, 58,
1227 58, 58, 58, 58, 58, 58, 58, -119
1228
1229 },
1230
1231 {
1232 11, -120, -120, -120, -120, -120, -120, -120, -120, -120,
1233 -120, -120, -120, 58, -120, -120, 58, 58, 142, 58,
1234 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1235 58, 58, 58, 58, 143, 58, 58, -120
1236 },
1237
1238 {
1239 11, -121, -121, -121, -121, -121, -121, -121, -121, -121,
1240 -121, -121, -121, 58, -121, -121, 58, 58, 58, 58,
1241 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1242 58, 58, 58, 58, 58, 144, 58, -121
1243 },
1244
1245 {
1246 11, -122, -122, -122, -122, -122, -122, -122, -122, -122,
1247 -122, -122, -122, 58, -122, -122, 58, 58, 58, 58,
1248
1249 58, 58, 58, 58, 58, 58, 58, 58, 145, 58,
1250 58, 58, 58, 58, 58, 58, 58, -122
1251 },
1252
1253 {
1254 11, -123, -123, -123, -123, -123, -123, -123, -123, -123,
1255 -123, -123, -123, 58, -123, -123, 58, 58, 58, 58,
1256 58, 58, 58, 58, 58, 58, 146, 58, 58, 58,
1257 58, 58, 58, 58, 58, 58, 58, -123
1258 },
1259
1260 {
1261 11, -124, -124, -124, -124, -124, -124, -124, -124, -124,
1262 -124, -124, -124, 58, -124, -124, 58, 58, 58, 58,
1263 58, 58, 58, 58, 147, 58, 58, 58, 58, 58,
1264 58, 58, 58, 58, 58, 58, 58, -124
1265
1266 },
1267
1268 {
1269 11, -125, -125, -125, -125, -125, -125, -125, -125, -125,
1270 -125, -125, -125, 58, -125, -125, 58, 58, 58, 58,
1271 58, 58, 148, 58, 58, 58, 58, 58, 58, 58,
1272 58, 58, 58, 58, 58, 58, 58, -125
1273 },
1274
1275 {
1276 11, -126, -126, -126, -126, -126, -126, -126, -126, -126,
1277 -126, -126, -126, 58, -126, -126, 58, 58, 58, 58,
1278 58, 149, 58, 58, 58, 58, 58, 58, 58, 58,
1279 58, 58, 58, 58, 58, 58, 58, -126
1280 },
1281
1282 {
1283 11, -127, -127, -127, -127, -127, -127, -127, -127, -127,
1284 -127, -127, -127, 58, -127, -127, 58, 58, 58, 58,
1285
1286 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1287 58, 58, 58, 58, 58, 58, 58, -127
1288 },
1289
1290 {
1291 11, -128, -128, -128, -128, -128, -128, -128, -128, -128,
1292 -128, -128, -128, 58, -128, -128, 58, 58, 58, 58,
1293 58, 58, 58, 58, 58, 58, 58, 150, 58, 58,
1294 58, 58, 58, 58, 58, 58, 58, -128
1295 },
1296
1297 {
1298 11, -129, -129, -129, -129, -129, -129, -129, -129, -129,
1299 -129, -129, -129, 58, -129, -129, 58, 58, 58, 151,
1300 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1301 58, 58, 58, 58, 58, 58, 58, -129
1302
1303 },
1304
1305 {
1306 11, -130, -130, -130, -130, -130, -130, -130, -130, -130,
1307 -130, -130, -130, 58, -130, -130, 58, 58, 58, 58,
1308 58, 58, 58, 58, 58, 58, 58, 58, 58, 152,
1309 58, 58, 58, 58, 58, 58, 58, -130
1310 },
1311
1312 {
1313 11, -131, -131, -131, -131, -131, -131, -131, -131, -131,
1314 -131, -131, -131, 58, -131, -131, 58, 58, 58, 58,
1315 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1316 153, 58, 58, 58, 58, 58, 58, -131
1317 },
1318
1319 {
1320 11, -132, -132, -132, -132, -132, -132, -132, -132, -132,
1321 -132, -132, -132, 58, -132, -132, 58, 58, 58, 58,
1322
1323 58, 154, 58, 58, 58, 58, 58, 58, 58, 58,
1324 58, 58, 58, 58, 58, 58, 58, -132
1325 },
1326
1327 {
1328 11, -133, -133, -133, -133, -133, -133, -133, -133, -133,
1329 -133, -133, -133, 58, -133, -133, 58, 58, 58, 58,
1330 58, 58, 58, 58, 58, 155, 58, 58, 58, 58,
1331 58, 58, 58, 58, 58, 58, 58, -133
1332 },
1333
1334 {
1335 11, -134, -134, -134, -134, -134, -134, -134, -134, -134,
1336 -134, -134, -134, 58, -134, -134, 58, 58, 58, 156,
1337 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1338 58, 58, 58, 58, 58, 58, 58, -134
1339
1340 },
1341
1342 {
1343 11, -135, -135, -135, -135, -135, -135, -135, -135, -135,
1344 -135, -135, -135, 58, -135, -135, 58, 58, 58, 157,
1345 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1346 58, 58, 58, 58, 58, 58, 58, -135
1347 },
1348
1349 {
1350 11, -136, -136, -136, -136, -136, -136, -136, -136, -136,
1351 -136, -136, -136, 58, -136, -136, 58, 58, 58, 58,
1352 58, 58, 58, 58, 58, 58, 58, 58, 158, 58,
1353 58, 58, 58, 58, 58, 58, 58, -136
1354 },
1355
1356 {
1357 11, -137, -137, -137, -137, -137, -137, -137, -137, -137,
1358 -137, -137, -137, 58, -137, -137, 58, 58, 58, 58,
1359
1360 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1361 58, 58, 58, 58, 159, 58, 58, -137
1362 },
1363
1364 {
1365 11, -138, -138, -138, -138, -138, -138, -138, -138, -138,
1366 -138, -138, -138, 58, -138, -138, 58, 160, 58, 58,
1367 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1368 58, 58, 58, 58, 58, 58, 58, -138
1369 },
1370
1371 {
1372 11, -139, -139, -139, -139, -139, -139, -139, -139, -139,
1373 -139, -139, -139, 58, -139, -139, 58, 58, 58, 58,
1374 58, 161, 58, 58, 58, 58, 58, 58, 58, 58,
1375 58, 58, 58, 58, 58, 58, 58, -139
1376
1377 },
1378
1379 {
1380 11, -140, -140, -140, -140, -140, -140, -140, -140, -140,
1381 -140, -140, -140, 58, -140, -140, 58, 58, 58, 58,
1382 58, 58, 58, 58, 58, 58, 58, 58, 162, 58,
1383 58, 58, 58, 58, 58, 58, 58, -140
1384 },
1385
1386 {
1387 11, -141, -141, -141, -141, -141, -141, -141, -141, -141,
1388 -141, -141, -141, 58, -141, -141, 58, 58, 58, 58,
1389 58, 58, 58, 163, 58, 58, 58, 58, 58, 58,
1390 58, 58, 58, 58, 58, 58, 58, -141
1391 },
1392
1393 {
1394 11, -142, -142, -142, -142, -142, -142, -142, -142, -142,
1395 -142, -142, -142, 58, -142, -142, 58, 58, 58, 58,
1396
1397 58, 58, 58, 58, 58, 58, 58, 58, 58, 164,
1398 58, 58, 58, 58, 58, 58, 58, -142
1399 },
1400
1401 {
1402 11, -143, -143, -143, -143, -143, -143, -143, -143, -143,
1403 -143, -143, -143, 58, -143, -143, 58, 58, 58, 58,
1404 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1405 58, 58, 165, 58, 58, 58, 58, -143
1406 },
1407
1408 {
1409 11, -144, -144, -144, -144, -144, -144, -144, -144, -144,
1410 -144, -144, -144, 58, -144, -144, 58, 58, 58, 58,
1411 58, 58, 58, 58, 58, 58, 166, 58, 58, 58,
1412 58, 58, 58, 58, 58, 58, 58, -144
1413
1414 },
1415
1416 {
1417 11, -145, -145, -145, -145, -145, -145, -145, -145, -145,
1418 -145, -145, -145, 58, -145, -145, 58, 58, 58, 58,
1419 167, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1420 58, 58, 58, 58, 58, 58, 58, -145
1421 },
1422
1423 {
1424 11, -146, -146, -146, -146, -146, -146, -146, -146, -146,
1425 -146, -146, -146, 58, -146, -146, 58, 58, 58, 58,
1426 58, 168, 58, 58, 58, 58, 58, 58, 58, 58,
1427 58, 58, 58, 58, 58, 58, 58, -146
1428 },
1429
1430 {
1431 11, -147, -147, -147, -147, -147, -147, -147, -147, -147,
1432 -147, -147, -147, 58, -147, -147, 58, 58, 58, 58,
1433
1434 58, 58, 58, 58, 58, 58, 58, 58, 58, 169,
1435 58, 58, 58, 58, 58, 58, 58, -147
1436 },
1437
1438 {
1439 11, -148, -148, -148, -148, -148, -148, -148, -148, -148,
1440 -148, -148, -148, 58, -148, -148, 58, 58, 58, 58,
1441 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1442 58, 58, 58, 58, 58, 58, 58, -148
1443 },
1444
1445 {
1446 11, -149, -149, -149, -149, -149, -149, -149, -149, -149,
1447 -149, -149, -149, 58, -149, -149, 58, 58, 58, 58,
1448 58, 58, 58, 58, 58, 58, 58, 58, 170, 58,
1449 58, 58, 58, 58, 58, 58, 58, -149
1450
1451 },
1452
1453 {
1454 11, -150, -150, -150, -150, -150, -150, -150, -150, -150,
1455 -150, -150, -150, 58, -150, -150, 58, 58, 58, 58,
1456 58, 171, 58, 58, 58, 58, 58, 58, 58, 58,
1457 58, 58, 58, 58, 58, 58, 58, -150
1458 },
1459
1460 {
1461 11, -151, -151, -151, -151, -151, -151, -151, -151, -151,
1462 -151, -151, -151, 58, -151, -151, 58, 58, 58, 58,
1463 58, 58, 58, 58, 58, 58, 58, 58, 58, 172,
1464 58, 58, 58, 58, 58, 58, 58, -151
1465 },
1466
1467 {
1468 11, -152, -152, -152, -152, -152, -152, -152, -152, -152,
1469 -152, -152, -152, 58, -152, -152, 58, 58, 58, 58,
1470
1471 58, 58, 58, 58, 58, 58, 58, 58, 173, 58,
1472 58, 58, 58, 58, 58, 58, 58, -152
1473 },
1474
1475 {
1476 11, -153, -153, -153, -153, -153, -153, -153, -153, -153,
1477 -153, -153, -153, 58, -153, -153, 58, 58, 58, 58,
1478 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1479 58, 58, 58, 58, 174, 58, 58, -153
1480 },
1481
1482 {
1483 11, -154, -154, -154, -154, -154, -154, -154, -154, -154,
1484 -154, -154, -154, 58, -154, -154, 58, 58, 58, 58,
1485 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1486 58, 58, 58, 58, 58, 58, 58, -154
1487
1488 },
1489
1490 {
1491 11, -155, -155, -155, -155, -155, -155, -155, -155, -155,
1492 -155, -155, -155, 58, -155, -155, 58, 58, 58, 58,
1493 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1494 58, 58, 175, 58, 58, 58, 58, -155
1495 },
1496
1497 {
1498 11, -156, -156, -156, -156, -156, -156, -156, -156, -156,
1499 -156, -156, -156, 58, -156, -156, 58, 58, 58, 58,
1500 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1501 58, 58, 58, 58, 176, 58, 58, -156
1502 },
1503
1504 {
1505 11, -157, -157, -157, -157, -157, -157, -157, -157, -157,
1506 -157, -157, -157, 58, -157, -157, 58, 58, 58, 58,
1507
1508 58, 177, 58, 58, 58, 58, 58, 58, 58, 58,
1509 58, 58, 58, 58, 58, 58, 58, -157
1510 },
1511
1512 {
1513 11, -158, -158, -158, -158, -158, -158, -158, -158, -158,
1514 -158, -158, -158, 58, -158, -158, 58, 58, 58, 58,
1515 58, 58, 58, 178, 58, 58, 58, 58, 58, 58,
1516 58, 58, 58, 58, 58, 58, 58, -158
1517 },
1518
1519 {
1520 11, -159, -159, -159, -159, -159, -159, -159, -159, -159,
1521 -159, -159, -159, 58, -159, -159, 58, 179, 58, 58,
1522 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1523 58, 58, 58, 58, 58, 58, 58, -159
1524
1525 },
1526
1527 {
1528 11, -160, -160, -160, -160, -160, -160, -160, -160, -160,
1529 -160, -160, -160, 58, -160, -160, 58, 58, 58, 58,
1530 58, 58, 58, 58, 58, 58, 58, 58, 180, 58,
1531 58, 58, 58, 58, 58, 58, 58, -160
1532 },
1533
1534 {
1535 11, -161, -161, -161, -161, -161, -161, -161, -161, -161,
1536 -161, -161, -161, 58, -161, -161, 58, 58, 58, 58,
1537 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1538 58, 58, 58, 58, 58, 58, 58, -161
1539 },
1540
1541 {
1542 11, -162, -162, -162, -162, -162, -162, -162, -162, -162,
1543 -162, -162, -162, 58, -162, -162, 58, 58, 58, 58,
1544
1545 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1546 58, 58, 58, 58, 181, 58, 58, -162
1547 },
1548
1549 {
1550 11, -163, -163, -163, -163, -163, -163, -163, -163, -163,
1551 -163, -163, -163, 58, -163, -163, 58, 58, 58, 58,
1552 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1553 58, 58, 58, 58, 58, 58, 58, -163
1554 },
1555
1556 {
1557 11, -164, -164, -164, -164, -164, -164, -164, -164, -164,
1558 -164, -164, -164, 58, -164, -164, 58, 58, 58, 58,
1559 58, 58, 58, 58, 58, 58, 58, 58, 58, 182,
1560 58, 58, 58, 58, 58, 58, 58, -164
1561
1562 },
1563
1564 {
1565 11, -165, -165, -165, -165, -165, -165, -165, -165, -165,
1566 -165, -165, -165, 58, -165, -165, 58, 58, 58, 58,
1567 58, 58, 58, 58, 58, 183, 58, 58, 58, 58,
1568 58, 58, 58, 58, 58, 58, 58, -165
1569 },
1570
1571 {
1572 11, -166, -166, -166, -166, -166, -166, -166, -166, -166,
1573 -166, -166, -166, 58, -166, -166, 58, 58, 58, 58,
1574 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1575 58, 58, 58, 58, 184, 58, 58, -166
1576 },
1577
1578 {
1579 11, -167, -167, -167, -167, -167, -167, -167, -167, -167,
1580 -167, -167, -167, 58, -167, -167, 58, 58, 58, 58,
1581
1582 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1583 58, 58, 58, 185, 58, 58, 58, -167
1584 },
1585
1586 {
1587 11, -168, -168, -168, -168, -168, -168, -168, -168, -168,
1588 -168, -168, -168, 58, -168, -168, 58, 58, 58, 58,
1589 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1590 58, 58, 58, 58, 58, 58, 58, -168
1591 },
1592
1593 {
1594 11, -169, -169, -169, -169, -169, -169, -169, -169, -169,
1595 -169, -169, -169, 58, -169, -169, 58, 58, 58, 58,
1596 58, 58, 58, 58, 58, 186, 58, 58, 58, 58,
1597 58, 58, 58, 58, 58, 58, 58, -169
1598
1599 },
1600
1601 {
1602 11, -170, -170, -170, -170, -170, -170, -170, -170, -170,
1603 -170, -170, -170, 58, -170, -170, 58, 58, 58, 58,
1604 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1605 58, 58, 58, 58, 58, 187, 58, -170
1606 },
1607
1608 {
1609 11, -171, -171, -171, -171, -171, -171, -171, -171, -171,
1610 -171, -171, -171, 58, -171, -171, 58, 58, 58, 58,
1611 58, 58, 58, 58, 58, 58, 58, 58, 188, 58,
1612 58, 58, 58, 58, 58, 58, 58, -171
1613 },
1614
1615 {
1616 11, -172, -172, -172, -172, -172, -172, -172, -172, -172,
1617 -172, -172, -172, 58, -172, -172, 58, 58, 58, 58,
1618
1619 58, 58, 58, 58, 58, 58, 58, 58, 189, 58,
1620 58, 58, 58, 58, 58, 58, 58, -172
1621 },
1622
1623 {
1624 11, -173, -173, -173, -173, -173, -173, -173, -173, -173,
1625 -173, -173, -173, 58, -173, -173, 58, 190, 58, 58,
1626 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1627 58, 58, 58, 58, 58, 58, 58, -173
1628 },
1629
1630 {
1631 11, -174, -174, -174, -174, -174, -174, -174, -174, -174,
1632 -174, -174, -174, 58, -174, -174, 58, 58, 58, 58,
1633 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1634 58, 58, 58, 58, 58, 58, 58, -174
1635
1636 },
1637
1638 {
1639 11, -175, -175, -175, -175, -175, -175, -175, -175, -175,
1640 -175, -175, -175, 58, -175, -175, 58, 58, 58, 58,
1641 58, 191, 58, 58, 58, 58, 58, 58, 58, 58,
1642 58, 58, 58, 58, 58, 58, 58, -175
1643 },
1644
1645 {
1646 11, -176, -176, -176, -176, -176, -176, -176, -176, -176,
1647 -176, -176, -176, 58, -176, -176, 58, 58, 58, 58,
1648 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1649 58, 58, 58, 58, 58, 58, 58, -176
1650 },
1651
1652 {
1653 11, -177, -177, -177, -177, -177, -177, -177, -177, -177,
1654 -177, -177, -177, 58, -177, -177, 58, 58, 58, 58,
1655
1656 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1657 58, 58, 58, 58, 58, 58, 58, -177
1658 },
1659
1660 {
1661 11, -178, -178, -178, -178, -178, -178, -178, -178, -178,
1662 -178, -178, -178, 58, -178, -178, 58, 58, 58, 58,
1663 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1664 58, 58, 58, 58, 58, 58, 58, -178
1665 },
1666
1667 {
1668 11, -179, -179, -179, -179, -179, -179, -179, -179, -179,
1669 -179, -179, -179, 58, -179, -179, 58, 58, 58, 58,
1670 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1671 58, 58, 58, 58, 192, 58, 58, -179
1672
1673 },
1674
1675 {
1676 11, -180, -180, -180, -180, -180, -180, -180, -180, -180,
1677 -180, -180, -180, 58, -180, -180, 58, 58, 58, 58,
1678 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1679 58, 58, 58, 58, 58, 58, 58, -180
1680 },
1681
1682 {
1683 11, -181, -181, -181, -181, -181, -181, -181, -181, -181,
1684 -181, -181, -181, 58, -181, -181, 58, 58, 58, 58,
1685 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1686 58, 58, 58, 58, 58, 58, 58, -181
1687 },
1688
1689 {
1690 11, -182, -182, -182, -182, -182, -182, -182, -182, -182,
1691 -182, -182, -182, 58, -182, -182, 58, 58, 58, 58,
1692
1693 58, 58, 58, 58, 58, 58, 193, 58, 58, 58,
1694 58, 58, 58, 58, 58, 58, 58, -182
1695 },
1696
1697 {
1698 11, -183, -183, -183, -183, -183, -183, -183, -183, -183,
1699 -183, -183, -183, 58, -183, -183, 58, 58, 58, 58,
1700 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1701 58, 58, 58, 194, 58, 58, 58, -183
1702 },
1703
1704 {
1705 11, -184, -184, -184, -184, -184, -184, -184, -184, -184,
1706 -184, -184, -184, 58, -184, -184, 58, 58, 58, 58,
1707 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1708 58, 58, 58, 58, 58, 58, 58, -184
1709
1710 },
1711
1712 {
1713 11, -185, -185, -185, -185, -185, -185, -185, -185, -185,
1714 -185, -185, -185, 58, -185, -185, 58, 58, 58, 58,
1715 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1716 58, 58, 58, 58, 58, 58, 58, -185
1717 },
1718
1719 {
1720 11, -186, -186, -186, -186, -186, -186, -186, -186, -186,
1721 -186, -186, -186, 58, -186, -186, 58, 58, 58, 195,
1722 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1723 58, 58, 58, 58, 58, 58, 58, -186
1724 },
1725
1726 {
1727 11, -187, -187, -187, -187, -187, -187, -187, -187, -187,
1728 -187, -187, -187, 58, -187, -187, 58, 58, 58, 58,
1729
1730 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1731 58, 58, 58, 58, 58, 58, 58, -187
1732 },
1733
1734 {
1735 11, -188, -188, -188, -188, -188, -188, -188, -188, -188,
1736 -188, -188, -188, 58, -188, -188, 58, 58, 58, 58,
1737 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1738 58, 58, 58, 58, 58, 196, 58, -188
1739 },
1740
1741 {
1742 11, -189, -189, -189, -189, -189, -189, -189, -189, -189,
1743 -189, -189, -189, 58, -189, -189, 58, 58, 58, 58,
1744 58, 58, 197, 58, 58, 58, 58, 58, 58, 58,
1745 58, 58, 58, 58, 58, 58, 58, -189
1746
1747 },
1748
1749 {
1750 11, -190, -190, -190, -190, -190, -190, -190, -190, -190,
1751 -190, -190, -190, 58, -190, -190, 58, 58, 58, 58,
1752 58, 58, 58, 58, 58, 58, 198, 58, 58, 58,
1753 58, 58, 58, 58, 58, 58, 58, -190
1754 },
1755
1756 {
1757 11, -191, -191, -191, -191, -191, -191, -191, -191, -191,
1758 -191, -191, -191, 58, -191, -191, 58, 58, 58, 58,
1759 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1760 58, 58, 58, 199, 58, 58, 58, -191
1761 },
1762
1763 {
1764 11, -192, -192, -192, -192, -192, -192, -192, -192, -192,
1765 -192, -192, -192, 58, -192, -192, 58, 58, 58, 58,
1766
1767 58, 200, 58, 58, 58, 58, 58, 58, 58, 58,
1768 58, 58, 58, 58, 58, 58, 58, -192
1769 },
1770
1771 {
1772 11, -193, -193, -193, -193, -193, -193, -193, -193, -193,
1773 -193, -193, -193, 58, -193, -193, 58, 58, 58, 58,
1774 58, 201, 58, 58, 58, 58, 58, 58, 58, 58,
1775 58, 58, 58, 58, 58, 58, 58, -193
1776 },
1777
1778 {
1779 11, -194, -194, -194, -194, -194, -194, -194, -194, -194,
1780 -194, -194, -194, 58, -194, -194, 58, 58, 58, 58,
1781 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1782 58, 58, 58, 58, 202, 58, 58, -194
1783
1784 },
1785
1786 {
1787 11, -195, -195, -195, -195, -195, -195, -195, -195, -195,
1788 -195, -195, -195, 58, -195, -195, 58, 58, 58, 58,
1789 58, 203, 58, 58, 58, 58, 58, 58, 58, 58,
1790 58, 58, 58, 58, 58, 58, 58, -195
1791 },
1792
1793 {
1794 11, -196, -196, -196, -196, -196, -196, -196, -196, -196,
1795 -196, -196, -196, 58, -196, -196, 58, 58, 58, 58,
1796 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1797 58, 58, 58, 58, 58, 58, 58, -196
1798 },
1799
1800 {
1801 11, -197, -197, -197, -197, -197, -197, -197, -197, -197,
1802 -197, -197, -197, 58, -197, -197, 58, 58, 58, 58,
1803
1804 58, 58, 58, 58, 58, 204, 58, 58, 58, 58,
1805 58, 58, 58, 58, 58, 58, 58, -197
1806 },
1807
1808 {
1809 11, -198, -198, -198, -198, -198, -198, -198, -198, -198,
1810 -198, -198, -198, 58, -198, -198, 58, 58, 58, 58,
1811 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1812 58, 58, 58, 58, 58, 58, 58, -198
1813 },
1814
1815 {
1816 11, -199, -199, -199, -199, -199, -199, -199, -199, -199,
1817 -199, -199, -199, 58, -199, -199, 58, 58, 58, 58,
1818 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1819 58, 58, 58, 58, 58, 58, 58, -199
1820
1821 },
1822
1823 {
1824 11, -200, -200, -200, -200, -200, -200, -200, -200, -200,
1825 -200, -200, -200, 58, -200, -200, 58, 58, 58, 58,
1826 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1827 58, 58, 58, 58, 58, 58, 58, -200
1828 },
1829
1830 {
1831 11, -201, -201, -201, -201, -201, -201, -201, -201, -201,
1832 -201, -201, -201, 58, -201, -201, 58, 205, 58, 58,
1833 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1834 58, 58, 58, 58, 58, 58, 58, -201
1835 },
1836
1837 {
1838 11, -202, -202, -202, -202, -202, -202, -202, -202, -202,
1839 -202, -202, -202, 58, -202, -202, 58, 206, 58, 58,
1840
1841 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1842 58, 58, 58, 58, 58, 58, 58, -202
1843 },
1844
1845 {
1846 11, -203, -203, -203, -203, -203, -203, -203, -203, -203,
1847 -203, -203, -203, 58, -203, -203, 58, 58, 58, 58,
1848 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1849 58, 58, 58, 58, 58, 58, 58, -203
1850 },
1851
1852 {
1853 11, -204, -204, -204, -204, -204, -204, -204, -204, -204,
1854 -204, -204, -204, 58, -204, -204, 58, 58, 58, 58,
1855 58, 58, 58, 207, 58, 58, 58, 58, 58, 58,
1856 58, 58, 58, 58, 58, 58, 58, -204
1857
1858 },
1859
1860 {
1861 11, -205, -205, -205, -205, -205, -205, -205, -205, -205,
1862 -205, -205, -205, 58, -205, -205, 58, 58, 58, 58,
1863 58, 58, 58, 58, 58, 58, 58, 58, 208, 58,
1864 58, 58, 58, 58, 58, 58, 58, -205
1865 },
1866
1867 {
1868 11, -206, -206, -206, -206, -206, -206, -206, -206, -206,
1869 -206, -206, -206, 58, -206, -206, 58, 58, 58, 58,
1870 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1871 58, 58, 58, 58, 209, 58, 58, -206
1872 },
1873
1874 {
1875 11, -207, -207, -207, -207, -207, -207, -207, -207, -207,
1876 -207, -207, -207, 58, -207, -207, 58, 58, 58, 58,
1877
1878 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1879 58, 58, 58, 58, 58, 58, 58, -207
1880 },
1881
1882 {
1883 11, -208, -208, -208, -208, -208, -208, -208, -208, -208,
1884 -208, -208, -208, 58, -208, -208, 58, 58, 58, 58,
1885 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1886 58, 58, 58, 58, 58, 58, 58, -208
1887 },
1888
1889 {
1890 11, -209, -209, -209, -209, -209, -209, -209, -209, -209,
1891 -209, -209, -209, 58, -209, -209, 58, 58, 58, 58,
1892 58, 210, 58, 58, 58, 58, 58, 58, 58, 58,
1893 58, 58, 58, 58, 58, 58, 58, -209
1894
1895 },
1896
1897 {
1898 11, -210, -210, -210, -210, -210, -210, -210, -210, -210,
1899 -210, -210, -210, 58, -210, -210, 58, 58, 58, 58,
1900 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
1901 58, 58, 58, 58, 58, 58, 58, -210
1902 },
1903
1904 } ;
1905
1906static yy_state_type yy_get_previous_state (void );
1907static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
1908static int yy_get_next_buffer (void );
1909static void yy_fatal_error (yyconst char msg[] );
1910
1911/* Done after the current pattern has been matched and before the
1912 * corresponding action - sets up zconftext.
1913 */
1914#define YY_DO_BEFORE_ACTION \
1915 (yytext_ptr) = yy_bp; \
1916 zconfleng = (size_t) (yy_cp - yy_bp); \
1917 (yy_hold_char) = *yy_cp; \
1918 *yy_cp = '\0'; \
1919 (yy_c_buf_p) = yy_cp;
1920
1921#define YY_NUM_RULES 64
1922#define YY_END_OF_BUFFER 65
1923/* This struct is not used in this scanner,
1924 but its presence is necessary. */
1925struct yy_trans_info
1926 {
1927 flex_int32_t yy_verify;
1928 flex_int32_t yy_nxt;
1929 };
1930static yyconst flex_int16_t yy_accept[211] =
1931 { 0,
1932 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1933 65, 5, 4, 3, 2, 36, 37, 35, 35, 35,
1934 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
1935 63, 60, 62, 55, 59, 58, 57, 53, 48, 42,
1936 47, 51, 53, 40, 41, 50, 50, 43, 53, 50,
1937 50, 53, 4, 3, 2, 2, 1, 35, 35, 35,
1938 35, 35, 35, 35, 16, 35, 35, 35, 35, 35,
1939 35, 35, 35, 35, 35, 35, 63, 60, 62, 61,
1940 55, 54, 57, 56, 44, 51, 38, 50, 50, 52,
1941 45, 46, 39, 35, 35, 35, 35, 35, 35, 35,
1942
1943 35, 35, 30, 29, 35, 35, 35, 35, 35, 35,
1944 35, 35, 35, 35, 49, 25, 35, 35, 35, 35,
1945 35, 35, 35, 35, 35, 35, 15, 35, 7, 35,
1946 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
1947 35, 35, 35, 35, 35, 35, 35, 17, 35, 35,
1948 35, 35, 35, 34, 35, 35, 35, 35, 35, 35,
1949 10, 35, 13, 35, 35, 35, 35, 33, 35, 35,
1950 35, 35, 35, 22, 35, 32, 9, 31, 35, 26,
1951 12, 35, 35, 21, 18, 35, 8, 35, 35, 35,
1952 35, 35, 27, 35, 35, 6, 35, 20, 19, 23,
1953
1954 35, 35, 11, 35, 35, 35, 14, 28, 35, 24
1955 } ;
1956
1957static yyconst flex_int32_t yy_ec[256] =
1958 { 0,
1959 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
1960 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1961 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1962 1, 2, 4, 5, 6, 1, 1, 7, 8, 9,
1963 10, 1, 1, 1, 11, 12, 12, 13, 13, 13,
1964 13, 13, 13, 13, 13, 13, 13, 1, 1, 1,
1965 14, 1, 1, 1, 13, 13, 13, 13, 13, 13,
1966 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
1967 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
1968 1, 15, 1, 1, 16, 1, 17, 18, 19, 20,
1969
1970 21, 22, 23, 24, 25, 13, 13, 26, 27, 28,
1971 29, 30, 31, 32, 33, 34, 35, 13, 13, 36,
1972 13, 13, 1, 37, 1, 1, 1, 1, 1, 1,
1973 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1974 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1975 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1976 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1977 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1978 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1979 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1980
1981 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1982 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1983 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1984 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1985 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1986 1, 1, 1, 1, 1
1987 } ;
1988
1989extern int zconf_flex_debug;
1990int zconf_flex_debug = 0;
1991
1992/* The intent behind this definition is that it'll catch
1993 * any uses of REJECT which flex missed.
1994 */
1995#define REJECT reject_used_but_not_detected
1996#define yymore() yymore_used_but_not_detected
1997#define YY_MORE_ADJ 0
1998#define YY_RESTORE_YY_MORE_OFFSET
1999char *zconftext;
2000
2001/*
2002 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
2003 * Released under the terms of the GNU GPL v2.0.
2004 */
2005
2006#include <limits.h>
2007#include <stdio.h>
2008#include <stdlib.h>
2009#include <string.h>
2010#include <unistd.h>
2011
2012#define LKC_DIRECT_LINK
2013#include "lkc.h"
2014
2015#define START_STRSIZE 16
2016
2017char *text;
2018static char *text_ptr;
2019static int text_size, text_asize;
2020
2021struct buffer {
2022 struct buffer *parent;
2023 YY_BUFFER_STATE state;
2024};
2025
2026struct buffer *current_buf;
2027
2028static int last_ts, first_ts;
2029
2030static void zconf_endhelp(void);
2031static struct buffer *zconf_endfile(void);
2032
2033void new_string(void)
2034{
2035 text = malloc(START_STRSIZE);
2036 text_asize = START_STRSIZE;
2037 text_ptr = text;
2038 text_size = 0;
2039 *text_ptr = 0;
2040}
2041
2042void append_string(const char *str, int size)
2043{
2044 int new_size = text_size + size + 1;
2045 if (new_size > text_asize) {
2046 text = realloc(text, new_size);
2047 text_asize = new_size;
2048 text_ptr = text + text_size;
2049 }
2050 memcpy(text_ptr, str, size);
2051 text_ptr += size;
2052 text_size += size;
2053 *text_ptr = 0;
2054}
2055
2056void alloc_string(const char *str, int size)
2057{
2058 text = malloc(size + 1);
2059 memcpy(text, str, size);
2060 text[size] = 0;
2061}
2062
2063#define INITIAL 0
2064#define COMMAND 1
2065#define HELP 2
2066#define STRING 3
2067#define PARAM 4
2068
2069/* Special case for "unistd.h", since it is non-ANSI. We include it way
2070 * down here because we want the user's section 1 to have been scanned first.
2071 * The user has a chance to override it with an option.
2072 */
2073#include <unistd.h>
2074
2075#ifndef YY_EXTRA_TYPE
2076#define YY_EXTRA_TYPE void *
2077#endif
2078
2079/* Macros after this point can all be overridden by user definitions in
2080 * section 1.
2081 */
2082
2083#ifndef YY_SKIP_YYWRAP
2084#ifdef __cplusplus
2085extern "C" int zconfwrap (void );
2086#else
2087extern int zconfwrap (void );
2088#endif
2089#endif
2090
2091 static void yyunput (int c,char *buf_ptr );
2092
2093#ifndef yytext_ptr
2094static void yy_flex_strncpy (char *,yyconst char *,int );
2095#endif
2096
2097#ifdef YY_NEED_STRLEN
2098static int yy_flex_strlen (yyconst char * );
2099#endif
2100
2101#ifndef YY_NO_INPUT
2102
2103#ifdef __cplusplus
2104static int yyinput (void );
2105#else
2106static int input (void );
2107#endif
2108
2109#endif
2110
2111/* Amount of stuff to slurp up with each read. */
2112#ifndef YY_READ_BUF_SIZE
2113#define YY_READ_BUF_SIZE 8192
2114#endif
2115
2116/* Copy whatever the last rule matched to the standard output. */
2117#ifndef ECHO
2118/* This used to be an fputs(), but since the string might contain NUL's,
2119 * we now use fwrite().
2120 */
2121#define ECHO (void) fwrite( zconftext, zconfleng, 1, zconfout )
2122#endif
2123
2124/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
2125 * is returned in "result".
2126 */
2127#ifndef YY_INPUT
2128#define YY_INPUT(buf,result,max_size) \
2129 errno=0; \
2130 while ( (result = read( fileno(zconfin), (char *) buf, max_size )) < 0 ) \
2131 { \
2132 if( errno != EINTR) \
2133 { \
2134 YY_FATAL_ERROR( "input in flex scanner failed" ); \
2135 break; \
2136 } \
2137 errno=0; \
2138 clearerr(zconfin); \
2139 }\
2140\
2141
2142#endif
2143
2144/* No semi-colon after return; correct usage is to write "yyterminate();" -
2145 * we don't want an extra ';' after the "return" because that will cause
2146 * some compilers to complain about unreachable statements.
2147 */
2148#ifndef yyterminate
2149#define yyterminate() return YY_NULL
2150#endif
2151
2152/* Number of entries by which start-condition stack grows. */
2153#ifndef YY_START_STACK_INCR
2154#define YY_START_STACK_INCR 25
2155#endif
2156
2157/* Report a fatal error. */
2158#ifndef YY_FATAL_ERROR
2159#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
2160#endif
2161
2162/* end tables serialization structures and prototypes */
2163
2164/* Default declaration of generated scanner - a define so the user can
2165 * easily add parameters.
2166 */
2167#ifndef YY_DECL
2168#define YY_DECL_IS_OURS 1
2169
2170extern int zconflex (void);
2171
2172#define YY_DECL int zconflex (void)
2173#endif /* !YY_DECL */
2174
2175/* Code executed at the beginning of each rule, after zconftext and zconfleng
2176 * have been set up.
2177 */
2178#ifndef YY_USER_ACTION
2179#define YY_USER_ACTION
2180#endif
2181
2182/* Code executed at the end of each rule. */
2183#ifndef YY_BREAK
2184#define YY_BREAK break;
2185#endif
2186
2187#define YY_RULE_SETUP \
2188 YY_USER_ACTION
2189
2190/** The main scanner function which does all the work.
2191 */
2192YY_DECL
2193{
2194 register yy_state_type yy_current_state;
2195 register char *yy_cp, *yy_bp;
2196 register int yy_act;
2197
2198 int str = 0;
2199 int ts, i;
2200
2201 if ( (yy_init) )
2202 {
2203 (yy_init) = 0;
2204
2205#ifdef YY_USER_INIT
2206 YY_USER_INIT;
2207#endif
2208
2209 if ( ! (yy_start) )
2210 (yy_start) = 1; /* first start state */
2211
2212 if ( ! zconfin )
2213 zconfin = stdin;
2214
2215 if ( ! zconfout )
2216 zconfout = stdout;
2217
2218 if ( ! YY_CURRENT_BUFFER ) {
2219 zconfensure_buffer_stack ();
2220 YY_CURRENT_BUFFER_LVALUE =
2221 zconf_create_buffer(zconfin,YY_BUF_SIZE );
2222 }
2223
2224 zconf_load_buffer_state( );
2225 }
2226
2227 while ( 1 ) /* loops until end-of-file is reached */
2228 {
2229 yy_cp = (yy_c_buf_p);
2230
2231 /* Support of zconftext. */
2232 *yy_cp = (yy_hold_char);
2233
2234 /* yy_bp points to the position in yy_ch_buf of the start of
2235 * the current run.
2236 */
2237 yy_bp = yy_cp;
2238
2239 yy_current_state = (yy_start);
2240yy_match:
2241 while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 )
2242 ++yy_cp;
2243
2244 yy_current_state = -yy_current_state;
2245
2246yy_find_action:
2247 yy_act = yy_accept[yy_current_state];
2248
2249 YY_DO_BEFORE_ACTION;
2250
2251do_action: /* This label is used only to access EOF actions. */
2252
2253 switch ( yy_act )
2254 { /* beginning of action switch */
2255case 1:
2256/* rule 1 can match eol */
2257YY_RULE_SETUP
2258current_file->lineno++;
2259 YY_BREAK
2260case 2:
2261YY_RULE_SETUP
2262
2263 YY_BREAK
2264case 3:
2265/* rule 3 can match eol */
2266YY_RULE_SETUP
2267current_file->lineno++; return T_EOL;
2268 YY_BREAK
2269case 4:
2270YY_RULE_SETUP
2271{
2272 BEGIN(COMMAND);
2273}
2274 YY_BREAK
2275case 5:
2276YY_RULE_SETUP
2277{
2278 unput(zconftext[0]);
2279 BEGIN(COMMAND);
2280}
2281 YY_BREAK
2282
2283case 6:
2284YY_RULE_SETUP
2285BEGIN(PARAM); return T_MAINMENU;
2286 YY_BREAK
2287case 7:
2288YY_RULE_SETUP
2289BEGIN(PARAM); return T_MENU;
2290 YY_BREAK
2291case 8:
2292YY_RULE_SETUP
2293BEGIN(PARAM); return T_ENDMENU;
2294 YY_BREAK
2295case 9:
2296YY_RULE_SETUP
2297BEGIN(PARAM); return T_SOURCE;
2298 YY_BREAK
2299case 10:
2300YY_RULE_SETUP
2301BEGIN(PARAM); return T_CHOICE;
2302 YY_BREAK
2303case 11:
2304YY_RULE_SETUP
2305BEGIN(PARAM); return T_ENDCHOICE;
2306 YY_BREAK
2307case 12:
2308YY_RULE_SETUP
2309BEGIN(PARAM); return T_COMMENT;
2310 YY_BREAK
2311case 13:
2312YY_RULE_SETUP
2313BEGIN(PARAM); return T_CONFIG;
2314 YY_BREAK
2315case 14:
2316YY_RULE_SETUP
2317BEGIN(PARAM); return T_MENUCONFIG;
2318 YY_BREAK
2319case 15:
2320YY_RULE_SETUP
2321BEGIN(PARAM); return T_HELP;
2322 YY_BREAK
2323case 16:
2324YY_RULE_SETUP
2325BEGIN(PARAM); return T_IF;
2326 YY_BREAK
2327case 17:
2328YY_RULE_SETUP
2329BEGIN(PARAM); return T_ENDIF;
2330 YY_BREAK
2331case 18:
2332YY_RULE_SETUP
2333BEGIN(PARAM); return T_DEPENDS;
2334 YY_BREAK
2335case 19:
2336YY_RULE_SETUP
2337BEGIN(PARAM); return T_REQUIRES;
2338 YY_BREAK
2339case 20:
2340YY_RULE_SETUP
2341BEGIN(PARAM); return T_OPTIONAL;
2342 YY_BREAK
2343case 21:
2344YY_RULE_SETUP
2345BEGIN(PARAM); return T_DEFAULT;
2346 YY_BREAK
2347case 22:
2348YY_RULE_SETUP
2349BEGIN(PARAM); return T_PROMPT;
2350 YY_BREAK
2351case 23:
2352YY_RULE_SETUP
2353BEGIN(PARAM); return T_TRISTATE;
2354 YY_BREAK
2355case 24:
2356YY_RULE_SETUP
2357BEGIN(PARAM); return T_DEF_TRISTATE;
2358 YY_BREAK
2359case 25:
2360YY_RULE_SETUP
2361BEGIN(PARAM); return T_BOOLEAN;
2362 YY_BREAK
2363case 26:
2364YY_RULE_SETUP
2365BEGIN(PARAM); return T_BOOLEAN;
2366 YY_BREAK
2367case 27:
2368YY_RULE_SETUP
2369BEGIN(PARAM); return T_DEF_BOOLEAN;
2370 YY_BREAK
2371case 28:
2372YY_RULE_SETUP
2373BEGIN(PARAM); return T_DEF_BOOLEAN;
2374 YY_BREAK
2375case 29:
2376YY_RULE_SETUP
2377BEGIN(PARAM); return T_INT;
2378 YY_BREAK
2379case 30:
2380YY_RULE_SETUP
2381BEGIN(PARAM); return T_HEX;
2382 YY_BREAK
2383case 31:
2384YY_RULE_SETUP
2385BEGIN(PARAM); return T_STRING;
2386 YY_BREAK
2387case 32:
2388YY_RULE_SETUP
2389BEGIN(PARAM); return T_SELECT;
2390 YY_BREAK
2391case 33:
2392YY_RULE_SETUP
2393BEGIN(PARAM); return T_SELECT;
2394 YY_BREAK
2395case 34:
2396YY_RULE_SETUP
2397BEGIN(PARAM); return T_RANGE;
2398 YY_BREAK
2399case 35:
2400YY_RULE_SETUP
2401{
2402 alloc_string(zconftext, zconfleng);
2403 zconflval.string = text;
2404 return T_WORD;
2405 }
2406 YY_BREAK
2407case 36:
2408YY_RULE_SETUP
2409
2410 YY_BREAK
2411case 37:
2412/* rule 37 can match eol */
2413YY_RULE_SETUP
2414current_file->lineno++; BEGIN(INITIAL);
2415 YY_BREAK
2416
2417case 38:
2418YY_RULE_SETUP
2419return T_AND;
2420 YY_BREAK
2421case 39:
2422YY_RULE_SETUP
2423return T_OR;
2424 YY_BREAK
2425case 40:
2426YY_RULE_SETUP
2427return T_OPEN_PAREN;
2428 YY_BREAK
2429case 41:
2430YY_RULE_SETUP
2431return T_CLOSE_PAREN;
2432 YY_BREAK
2433case 42:
2434YY_RULE_SETUP
2435return T_NOT;
2436 YY_BREAK
2437case 43:
2438YY_RULE_SETUP
2439return T_EQUAL;
2440 YY_BREAK
2441case 44:
2442YY_RULE_SETUP
2443return T_UNEQUAL;
2444 YY_BREAK
2445case 45:
2446YY_RULE_SETUP
2447return T_IF;
2448 YY_BREAK
2449case 46:
2450YY_RULE_SETUP
2451return T_ON;
2452 YY_BREAK
2453case 47:
2454YY_RULE_SETUP
2455{
2456 str = zconftext[0];
2457 new_string();
2458 BEGIN(STRING);
2459 }
2460 YY_BREAK
2461case 48:
2462/* rule 48 can match eol */
2463YY_RULE_SETUP
2464BEGIN(INITIAL); current_file->lineno++; return T_EOL;
2465 YY_BREAK
2466case 49:
2467YY_RULE_SETUP
2468/* ignore */
2469 YY_BREAK
2470case 50:
2471YY_RULE_SETUP
2472{
2473 alloc_string(zconftext, zconfleng);
2474 zconflval.string = text;
2475 return T_WORD;
2476 }
2477 YY_BREAK
2478case 51:
2479YY_RULE_SETUP
2480/* comment */
2481 YY_BREAK
2482case 52:
2483/* rule 52 can match eol */
2484YY_RULE_SETUP
2485current_file->lineno++;
2486 YY_BREAK
2487case 53:
2488YY_RULE_SETUP
2489
2490 YY_BREAK
2491case YY_STATE_EOF(PARAM):
2492{
2493 BEGIN(INITIAL);
2494 }
2495 YY_BREAK
2496
2497case 54:
2498/* rule 54 can match eol */
2499*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
2500(yy_c_buf_p) = yy_cp -= 1;
2501YY_DO_BEFORE_ACTION; /* set up zconftext again */
2502YY_RULE_SETUP
2503{
2504 append_string(zconftext, zconfleng);
2505 zconflval.string = text;
2506 return T_WORD_QUOTE;
2507 }
2508 YY_BREAK
2509case 55:
2510YY_RULE_SETUP
2511{
2512 append_string(zconftext, zconfleng);
2513 }
2514 YY_BREAK
2515case 56:
2516/* rule 56 can match eol */
2517*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
2518(yy_c_buf_p) = yy_cp -= 1;
2519YY_DO_BEFORE_ACTION; /* set up zconftext again */
2520YY_RULE_SETUP
2521{
2522 append_string(zconftext + 1, zconfleng - 1);
2523 zconflval.string = text;
2524 return T_WORD_QUOTE;
2525 }
2526 YY_BREAK
2527case 57:
2528YY_RULE_SETUP
2529{
2530 append_string(zconftext + 1, zconfleng - 1);
2531 }
2532 YY_BREAK
2533case 58:
2534YY_RULE_SETUP
2535{
2536 if (str == zconftext[0]) {
2537 BEGIN(PARAM);
2538 zconflval.string = text;
2539 return T_WORD_QUOTE;
2540 } else
2541 append_string(zconftext, 1);
2542 }
2543 YY_BREAK
2544case 59:
2545/* rule 59 can match eol */
2546YY_RULE_SETUP
2547{
2548 printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno());
2549 current_file->lineno++;
2550 BEGIN(INITIAL);
2551 return T_EOL;
2552 }
2553 YY_BREAK
2554case YY_STATE_EOF(STRING):
2555{
2556 BEGIN(INITIAL);
2557 }
2558 YY_BREAK
2559
2560case 60:
2561YY_RULE_SETUP
2562{
2563 ts = 0;
2564 for (i = 0; i < zconfleng; i++) {
2565 if (zconftext[i] == '\t')
2566 ts = (ts & ~7) + 8;
2567 else
2568 ts++;
2569 }
2570 last_ts = ts;
2571 if (first_ts) {
2572 if (ts < first_ts) {
2573 zconf_endhelp();
2574 return T_HELPTEXT;
2575 }
2576 ts -= first_ts;
2577 while (ts > 8) {
2578 append_string(" ", 8);
2579 ts -= 8;
2580 }
2581 append_string(" ", ts);
2582 }
2583 }
2584 YY_BREAK
2585case 61:
2586/* rule 61 can match eol */
2587*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
2588(yy_c_buf_p) = yy_cp -= 1;
2589YY_DO_BEFORE_ACTION; /* set up zconftext again */
2590YY_RULE_SETUP
2591{
2592 current_file->lineno++;
2593 zconf_endhelp();
2594 return T_HELPTEXT;
2595 }
2596 YY_BREAK
2597case 62:
2598/* rule 62 can match eol */
2599YY_RULE_SETUP
2600{
2601 current_file->lineno++;
2602 append_string("\n", 1);
2603 }
2604 YY_BREAK
2605case 63:
2606YY_RULE_SETUP
2607{
2608 append_string(zconftext, zconfleng);
2609 if (!first_ts)
2610 first_ts = last_ts;
2611 }
2612 YY_BREAK
2613case YY_STATE_EOF(HELP):
2614{
2615 zconf_endhelp();
2616 return T_HELPTEXT;
2617 }
2618 YY_BREAK
2619
2620case YY_STATE_EOF(INITIAL):
2621case YY_STATE_EOF(COMMAND):
2622{
2623 if (current_buf) {
2624 zconf_endfile();
2625 return T_EOF;
2626 }
2627 fclose(zconfin);
2628 yyterminate();
2629}
2630 YY_BREAK
2631case 64:
2632YY_RULE_SETUP
2633YY_FATAL_ERROR( "flex scanner jammed" );
2634 YY_BREAK
2635
2636 case YY_END_OF_BUFFER:
2637 {
2638 /* Amount of text matched not including the EOB char. */
2639 int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
2640
2641 /* Undo the effects of YY_DO_BEFORE_ACTION. */
2642 *yy_cp = (yy_hold_char);
2643 YY_RESTORE_YY_MORE_OFFSET
2644
2645 if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
2646 {
2647 /* We're scanning a new file or input source. It's
2648 * possible that this happened because the user
2649 * just pointed zconfin at a new source and called
2650 * zconflex(). If so, then we have to assure
2651 * consistency between YY_CURRENT_BUFFER and our
2652 * globals. Here is the right place to do so, because
2653 * this is the first action (other than possibly a
2654 * back-up) that will match for the new input source.
2655 */
2656 (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
2657 YY_CURRENT_BUFFER_LVALUE->yy_input_file = zconfin;
2658 YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
2659 }
2660
2661 /* Note that here we test for yy_c_buf_p "<=" to the position
2662 * of the first EOB in the buffer, since yy_c_buf_p will
2663 * already have been incremented past the NUL character
2664 * (since all states make transitions on EOB to the
2665 * end-of-buffer state). Contrast this with the test
2666 * in input().
2667 */
2668 if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
2669 { /* This was really a NUL. */
2670 yy_state_type yy_next_state;
2671
2672 (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
2673
2674 yy_current_state = yy_get_previous_state( );
2675
2676 /* Okay, we're now positioned to make the NUL
2677 * transition. We couldn't have
2678 * yy_get_previous_state() go ahead and do it
2679 * for us because it doesn't know how to deal
2680 * with the possibility of jamming (and we don't
2681 * want to build jamming into it because then it
2682 * will run more slowly).
2683 */
2684
2685 yy_next_state = yy_try_NUL_trans( yy_current_state );
2686
2687 yy_bp = (yytext_ptr) + YY_MORE_ADJ;
2688
2689 if ( yy_next_state )
2690 {
2691 /* Consume the NUL. */
2692 yy_cp = ++(yy_c_buf_p);
2693 yy_current_state = yy_next_state;
2694 goto yy_match;
2695 }
2696
2697 else
2698 {
2699 yy_cp = (yy_c_buf_p);
2700 goto yy_find_action;
2701 }
2702 }
2703
2704 else switch ( yy_get_next_buffer( ) )
2705 {
2706 case EOB_ACT_END_OF_FILE:
2707 {
2708 (yy_did_buffer_switch_on_eof) = 0;
2709
2710 if ( zconfwrap( ) )
2711 {
2712 /* Note: because we've taken care in
2713 * yy_get_next_buffer() to have set up
2714 * zconftext, we can now set up
2715 * yy_c_buf_p so that if some total
2716 * hoser (like flex itself) wants to
2717 * call the scanner after we return the
2718 * YY_NULL, it'll still work - another
2719 * YY_NULL will get returned.
2720 */
2721 (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
2722
2723 yy_act = YY_STATE_EOF(YY_START);
2724 goto do_action;
2725 }
2726
2727 else
2728 {
2729 if ( ! (yy_did_buffer_switch_on_eof) )
2730 YY_NEW_FILE;
2731 }
2732 break;
2733 }
2734
2735 case EOB_ACT_CONTINUE_SCAN:
2736 (yy_c_buf_p) =
2737 (yytext_ptr) + yy_amount_of_matched_text;
2738
2739 yy_current_state = yy_get_previous_state( );
2740
2741 yy_cp = (yy_c_buf_p);
2742 yy_bp = (yytext_ptr) + YY_MORE_ADJ;
2743 goto yy_match;
2744
2745 case EOB_ACT_LAST_MATCH:
2746 (yy_c_buf_p) =
2747 &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
2748
2749 yy_current_state = yy_get_previous_state( );
2750
2751 yy_cp = (yy_c_buf_p);
2752 yy_bp = (yytext_ptr) + YY_MORE_ADJ;
2753 goto yy_find_action;
2754 }
2755 break;
2756 }
2757
2758 default:
2759 YY_FATAL_ERROR(
2760 "fatal flex scanner internal error--no action found" );
2761 } /* end of action switch */
2762 } /* end of scanning one token */
2763} /* end of zconflex */
2764
2765/* yy_get_next_buffer - try to read in a new buffer
2766 *
2767 * Returns a code representing an action:
2768 * EOB_ACT_LAST_MATCH -
2769 * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
2770 * EOB_ACT_END_OF_FILE - end of file
2771 */
2772static int yy_get_next_buffer (void)
2773{
2774 register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
2775 register char *source = (yytext_ptr);
2776 register int number_to_move, i;
2777 int ret_val;
2778
2779 if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
2780 YY_FATAL_ERROR(
2781 "fatal flex scanner internal error--end of buffer missed" );
2782
2783 if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
2784 { /* Don't try to fill the buffer, so this is an EOF. */
2785 if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
2786 {
2787 /* We matched a single character, the EOB, so
2788 * treat this as a final EOF.
2789 */
2790 return EOB_ACT_END_OF_FILE;
2791 }
2792
2793 else
2794 {
2795 /* We matched some text prior to the EOB, first
2796 * process it.
2797 */
2798 return EOB_ACT_LAST_MATCH;
2799 }
2800 }
2801
2802 /* Try to read more data. */
2803
2804 /* First move last chars to start of buffer. */
2805 number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
2806
2807 for ( i = 0; i < number_to_move; ++i )
2808 *(dest++) = *(source++);
2809
2810 if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
2811 /* don't do the read, it's not guaranteed to return an EOF,
2812 * just force an EOF
2813 */
2814 YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
2815
2816 else
2817 {
2818 size_t num_to_read =
2819 YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
2820
2821 while ( num_to_read <= 0 )
2822 { /* Not enough room in the buffer - grow it. */
2823
2824 /* just a shorter name for the current buffer */
2825 YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
2826
2827 int yy_c_buf_p_offset =
2828 (int) ((yy_c_buf_p) - b->yy_ch_buf);
2829
2830 if ( b->yy_is_our_buffer )
2831 {
2832 int new_size = b->yy_buf_size * 2;
2833
2834 if ( new_size <= 0 )
2835 b->yy_buf_size += b->yy_buf_size / 8;
2836 else
2837 b->yy_buf_size *= 2;
2838
2839 b->yy_ch_buf = (char *)
2840 /* Include room in for 2 EOB chars. */
2841 zconfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
2842 }
2843 else
2844 /* Can't grow it, we don't own it. */
2845 b->yy_ch_buf = 0;
2846
2847 if ( ! b->yy_ch_buf )
2848 YY_FATAL_ERROR(
2849 "fatal error - scanner input buffer overflow" );
2850
2851 (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
2852
2853 num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
2854 number_to_move - 1;
2855
2856 }
2857
2858 if ( num_to_read > YY_READ_BUF_SIZE )
2859 num_to_read = YY_READ_BUF_SIZE;
2860
2861 /* Read in more data. */
2862 YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
2863 (yy_n_chars), num_to_read );
2864
2865 YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
2866 }
2867
2868 if ( (yy_n_chars) == 0 )
2869 {
2870 if ( number_to_move == YY_MORE_ADJ )
2871 {
2872 ret_val = EOB_ACT_END_OF_FILE;
2873 zconfrestart(zconfin );
2874 }
2875
2876 else
2877 {
2878 ret_val = EOB_ACT_LAST_MATCH;
2879 YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
2880 YY_BUFFER_EOF_PENDING;
2881 }
2882 }
2883
2884 else
2885 ret_val = EOB_ACT_CONTINUE_SCAN;
2886
2887 (yy_n_chars) += number_to_move;
2888 YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
2889 YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
2890
2891 (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
2892
2893 return ret_val;
2894}
2895
2896/* yy_get_previous_state - get the state just before the EOB char was reached */
2897
2898 static yy_state_type yy_get_previous_state (void)
2899{
2900 register yy_state_type yy_current_state;
2901 register char *yy_cp;
2902
2903 yy_current_state = (yy_start);
2904
2905 for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
2906 {
2907 yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)];
2908 }
2909
2910 return yy_current_state;
2911}
2912
2913/* yy_try_NUL_trans - try to make a transition on the NUL character
2914 *
2915 * synopsis
2916 * next_state = yy_try_NUL_trans( current_state );
2917 */
2918 static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
2919{
2920 register int yy_is_jam;
2921
2922 yy_current_state = yy_nxt[yy_current_state][1];
2923 yy_is_jam = (yy_current_state <= 0);
2924
2925 return yy_is_jam ? 0 : yy_current_state;
2926}
2927
2928 static void yyunput (int c, register char * yy_bp )
2929{
2930 register char *yy_cp;
2931
2932 yy_cp = (yy_c_buf_p);
2933
2934 /* undo effects of setting up zconftext */
2935 *yy_cp = (yy_hold_char);
2936
2937 if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
2938 { /* need to shift things up to make room */
2939 /* +2 for EOB chars. */
2940 register int number_to_move = (yy_n_chars) + 2;
2941 register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
2942 YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
2943 register char *source =
2944 &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
2945
2946 while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
2947 *--dest = *--source;
2948
2949 yy_cp += (int) (dest - source);
2950 yy_bp += (int) (dest - source);
2951 YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
2952 (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
2953
2954 if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
2955 YY_FATAL_ERROR( "flex scanner push-back overflow" );
2956 }
2957
2958 *--yy_cp = (char) c;
2959
2960 (yytext_ptr) = yy_bp;
2961 (yy_hold_char) = *yy_cp;
2962 (yy_c_buf_p) = yy_cp;
2963}
2964
2965#ifndef YY_NO_INPUT
2966#ifdef __cplusplus
2967 static int yyinput (void)
2968#else
2969 static int input (void)
2970#endif
2971
2972{
2973 int c;
2974
2975 *(yy_c_buf_p) = (yy_hold_char);
2976
2977 if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
2978 {
2979 /* yy_c_buf_p now points to the character we want to return.
2980 * If this occurs *before* the EOB characters, then it's a
2981 * valid NUL; if not, then we've hit the end of the buffer.
2982 */
2983 if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
2984 /* This was really a NUL. */
2985 *(yy_c_buf_p) = '\0';
2986
2987 else
2988 { /* need more input */
2989 int offset = (yy_c_buf_p) - (yytext_ptr);
2990 ++(yy_c_buf_p);
2991
2992 switch ( yy_get_next_buffer( ) )
2993 {
2994 case EOB_ACT_LAST_MATCH:
2995 /* This happens because yy_g_n_b()
2996 * sees that we've accumulated a
2997 * token and flags that we need to
2998 * try matching the token before
2999 * proceeding. But for input(),
3000 * there's no matching to consider.
3001 * So convert the EOB_ACT_LAST_MATCH
3002 * to EOB_ACT_END_OF_FILE.
3003 */
3004
3005 /* Reset buffer status. */
3006 zconfrestart(zconfin );
3007
3008 /*FALLTHROUGH*/
3009
3010 case EOB_ACT_END_OF_FILE:
3011 {
3012 if ( zconfwrap( ) )
3013 return EOF;
3014
3015 if ( ! (yy_did_buffer_switch_on_eof) )
3016 YY_NEW_FILE;
3017#ifdef __cplusplus
3018 return yyinput();
3019#else
3020 return input();
3021#endif
3022 }
3023
3024 case EOB_ACT_CONTINUE_SCAN:
3025 (yy_c_buf_p) = (yytext_ptr) + offset;
3026 break;
3027 }
3028 }
3029 }
3030
3031 c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
3032 *(yy_c_buf_p) = '\0'; /* preserve zconftext */
3033 (yy_hold_char) = *++(yy_c_buf_p);
3034
3035 return c;
3036}
3037#endif /* ifndef YY_NO_INPUT */
3038
3039/** Immediately switch to a different input stream.
3040 * @param input_file A readable stream.
3041 *
3042 * @note This function does not reset the start condition to @c INITIAL .
3043 */
3044 void zconfrestart (FILE * input_file )
3045{
3046
3047 if ( ! YY_CURRENT_BUFFER ){
3048 zconfensure_buffer_stack ();
3049 YY_CURRENT_BUFFER_LVALUE =
3050 zconf_create_buffer(zconfin,YY_BUF_SIZE );
3051 }
3052
3053 zconf_init_buffer(YY_CURRENT_BUFFER,input_file );
3054 zconf_load_buffer_state( );
3055}
3056
3057/** Switch to a different input buffer.
3058 * @param new_buffer The new input buffer.
3059 *
3060 */
3061 void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer )
3062{
3063
3064 /* TODO. We should be able to replace this entire function body
3065 * with
3066 * zconfpop_buffer_state();
3067 * zconfpush_buffer_state(new_buffer);
3068 */
3069 zconfensure_buffer_stack ();
3070 if ( YY_CURRENT_BUFFER == new_buffer )
3071 return;
3072
3073 if ( YY_CURRENT_BUFFER )
3074 {
3075 /* Flush out information for old buffer. */
3076 *(yy_c_buf_p) = (yy_hold_char);
3077 YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
3078 YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
3079 }
3080
3081 YY_CURRENT_BUFFER_LVALUE = new_buffer;
3082 zconf_load_buffer_state( );
3083
3084 /* We don't actually know whether we did this switch during
3085 * EOF (zconfwrap()) processing, but the only time this flag
3086 * is looked at is after zconfwrap() is called, so it's safe
3087 * to go ahead and always set it.
3088 */
3089 (yy_did_buffer_switch_on_eof) = 1;
3090}
3091
3092static void zconf_load_buffer_state (void)
3093{
3094 (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
3095 (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
3096 zconfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
3097 (yy_hold_char) = *(yy_c_buf_p);
3098}
3099
3100/** Allocate and initialize an input buffer state.
3101 * @param file A readable stream.
3102 * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
3103 *
3104 * @return the allocated buffer state.
3105 */
3106 YY_BUFFER_STATE zconf_create_buffer (FILE * file, int size )
3107{
3108 YY_BUFFER_STATE b;
3109
3110 b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) );
3111 if ( ! b )
3112 YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" );
3113
3114 b->yy_buf_size = size;
3115
3116 /* yy_ch_buf has to be 2 characters longer than the size given because
3117 * we need to put in 2 end-of-buffer characters.
3118 */
3119 b->yy_ch_buf = (char *) zconfalloc(b->yy_buf_size + 2 );
3120 if ( ! b->yy_ch_buf )
3121 YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" );
3122
3123 b->yy_is_our_buffer = 1;
3124
3125 zconf_init_buffer(b,file );
3126
3127 return b;
3128}
3129
3130/** Destroy the buffer.
3131 * @param b a buffer created with zconf_create_buffer()
3132 *
3133 */
3134 void zconf_delete_buffer (YY_BUFFER_STATE b )
3135{
3136
3137 if ( ! b )
3138 return;
3139
3140 if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
3141 YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
3142
3143 if ( b->yy_is_our_buffer )
3144 zconffree((void *) b->yy_ch_buf );
3145
3146 zconffree((void *) b );
3147}
3148
3149/* Initializes or reinitializes a buffer.
3150 * This function is sometimes called more than once on the same buffer,
3151 * such as during a zconfrestart() or at EOF.
3152 */
3153 static void zconf_init_buffer (YY_BUFFER_STATE b, FILE * file )
3154
3155{
3156 int oerrno = errno;
3157
3158 zconf_flush_buffer(b );
3159
3160 b->yy_input_file = file;
3161 b->yy_fill_buffer = 1;
3162
3163 /* If b is the current buffer, then zconf_init_buffer was _probably_
3164 * called from zconfrestart() or through yy_get_next_buffer.
3165 * In that case, we don't want to reset the lineno or column.
3166 */
3167 if (b != YY_CURRENT_BUFFER){
3168 b->yy_bs_lineno = 1;
3169 b->yy_bs_column = 0;
3170 }
3171
3172 b->yy_is_interactive = 0;
3173
3174 errno = oerrno;
3175}
3176
3177/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
3178 * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
3179 *
3180 */
3181 void zconf_flush_buffer (YY_BUFFER_STATE b )
3182{
3183 if ( ! b )
3184 return;
3185
3186 b->yy_n_chars = 0;
3187
3188 /* We always need two end-of-buffer characters. The first causes
3189 * a transition to the end-of-buffer state. The second causes
3190 * a jam in that state.
3191 */
3192 b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
3193 b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
3194
3195 b->yy_buf_pos = &b->yy_ch_buf[0];
3196
3197 b->yy_at_bol = 1;
3198 b->yy_buffer_status = YY_BUFFER_NEW;
3199
3200 if ( b == YY_CURRENT_BUFFER )
3201 zconf_load_buffer_state( );
3202}
3203
3204/** Pushes the new state onto the stack. The new state becomes
3205 * the current state. This function will allocate the stack
3206 * if necessary.
3207 * @param new_buffer The new state.
3208 *
3209 */
3210void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer )
3211{
3212 if (new_buffer == NULL)
3213 return;
3214
3215 zconfensure_buffer_stack();
3216
3217 /* This block is copied from zconf_switch_to_buffer. */
3218 if ( YY_CURRENT_BUFFER )
3219 {
3220 /* Flush out information for old buffer. */
3221 *(yy_c_buf_p) = (yy_hold_char);
3222 YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
3223 YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
3224 }
3225
3226 /* Only push if top exists. Otherwise, replace top. */
3227 if (YY_CURRENT_BUFFER)
3228 (yy_buffer_stack_top)++;
3229 YY_CURRENT_BUFFER_LVALUE = new_buffer;
3230
3231 /* copied from zconf_switch_to_buffer. */
3232 zconf_load_buffer_state( );
3233 (yy_did_buffer_switch_on_eof) = 1;
3234}
3235
3236/** Removes and deletes the top of the stack, if present.
3237 * The next element becomes the new top.
3238 *
3239 */
3240void zconfpop_buffer_state (void)
3241{
3242 if (!YY_CURRENT_BUFFER)
3243 return;
3244
3245 zconf_delete_buffer(YY_CURRENT_BUFFER );
3246 YY_CURRENT_BUFFER_LVALUE = NULL;
3247 if ((yy_buffer_stack_top) > 0)
3248 --(yy_buffer_stack_top);
3249
3250 if (YY_CURRENT_BUFFER) {
3251 zconf_load_buffer_state( );
3252 (yy_did_buffer_switch_on_eof) = 1;
3253 }
3254}
3255
3256/* Allocates the stack if it does not exist.
3257 * Guarantees space for at least one push.
3258 */
3259static void zconfensure_buffer_stack (void)
3260{
3261 int num_to_alloc;
3262
3263 if (!(yy_buffer_stack)) {
3264
3265 /* First allocation is just for 2 elements, since we don't know if this
3266 * scanner will even need a stack. We use 2 instead of 1 to avoid an
3267 * immediate realloc on the next call.
3268 */
3269 num_to_alloc = 1;
3270 (yy_buffer_stack) = (struct yy_buffer_state**)zconfalloc
3271 (num_to_alloc * sizeof(struct yy_buffer_state*)
3272 );
3273
3274 memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
3275
3276 (yy_buffer_stack_max) = num_to_alloc;
3277 (yy_buffer_stack_top) = 0;
3278 return;
3279 }
3280
3281 if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
3282
3283 /* Increase the buffer to prepare for a possible push. */
3284 int grow_size = 8 /* arbitrary grow size */;
3285
3286 num_to_alloc = (yy_buffer_stack_max) + grow_size;
3287 (yy_buffer_stack) = (struct yy_buffer_state**)zconfrealloc
3288 ((yy_buffer_stack),
3289 num_to_alloc * sizeof(struct yy_buffer_state*)
3290 );
3291
3292 /* zero only the new slots.*/
3293 memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
3294 (yy_buffer_stack_max) = num_to_alloc;
3295 }
3296}
3297
3298/** Setup the input buffer state to scan directly from a user-specified character buffer.
3299 * @param base the character buffer
3300 * @param size the size in bytes of the character buffer
3301 *
3302 * @return the newly allocated buffer state object.
3303 */
3304YY_BUFFER_STATE zconf_scan_buffer (char * base, yy_size_t size )
3305{
3306 YY_BUFFER_STATE b;
3307
3308 if ( size < 2 ||
3309 base[size-2] != YY_END_OF_BUFFER_CHAR ||
3310 base[size-1] != YY_END_OF_BUFFER_CHAR )
3311 /* They forgot to leave room for the EOB's. */
3312 return 0;
3313
3314 b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) );
3315 if ( ! b )
3316 YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_buffer()" );
3317
3318 b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
3319 b->yy_buf_pos = b->yy_ch_buf = base;
3320 b->yy_is_our_buffer = 0;
3321 b->yy_input_file = 0;
3322 b->yy_n_chars = b->yy_buf_size;
3323 b->yy_is_interactive = 0;
3324 b->yy_at_bol = 1;
3325 b->yy_fill_buffer = 0;
3326 b->yy_buffer_status = YY_BUFFER_NEW;
3327
3328 zconf_switch_to_buffer(b );
3329
3330 return b;
3331}
3332
3333/** Setup the input buffer state to scan a string. The next call to zconflex() will
3334 * scan from a @e copy of @a str.
3335 * @param str a NUL-terminated string to scan
3336 *
3337 * @return the newly allocated buffer state object.
3338 * @note If you want to scan bytes that may contain NUL values, then use
3339 * zconf_scan_bytes() instead.
3340 */
3341YY_BUFFER_STATE zconf_scan_string (yyconst char * str )
3342{
3343
3344 return zconf_scan_bytes(str,strlen(str) );
3345}
3346
3347/** Setup the input buffer state to scan the given bytes. The next call to zconflex() will
3348 * scan from a @e copy of @a bytes.
3349 * @param bytes the byte buffer to scan
3350 * @param len the number of bytes in the buffer pointed to by @a bytes.
3351 *
3352 * @return the newly allocated buffer state object.
3353 */
3354YY_BUFFER_STATE zconf_scan_bytes (yyconst char * bytes, int len )
3355{
3356 YY_BUFFER_STATE b;
3357 char *buf;
3358 yy_size_t n;
3359 int i;
3360
3361 /* Get memory for full buffer, including space for trailing EOB's. */
3362 n = len + 2;
3363 buf = (char *) zconfalloc(n );
3364 if ( ! buf )
3365 YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_bytes()" );
3366
3367 for ( i = 0; i < len; ++i )
3368 buf[i] = bytes[i];
3369
3370 buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
3371
3372 b = zconf_scan_buffer(buf,n );
3373 if ( ! b )
3374 YY_FATAL_ERROR( "bad buffer in zconf_scan_bytes()" );
3375
3376 /* It's okay to grow etc. this buffer, and we should throw it
3377 * away when we're done.
3378 */
3379 b->yy_is_our_buffer = 1;
3380
3381 return b;
3382}
3383
3384#ifndef YY_EXIT_FAILURE
3385#define YY_EXIT_FAILURE 2
3386#endif
3387
3388static void yy_fatal_error (yyconst char* msg )
3389{
3390 (void) fprintf( stderr, "%s\n", msg );
3391 exit( YY_EXIT_FAILURE );
3392}
3393
3394/* Redefine yyless() so it works in section 3 code. */
3395
3396#undef yyless
3397#define yyless(n) \
3398 do \
3399 { \
3400 /* Undo effects of setting up zconftext. */ \
3401 int yyless_macro_arg = (n); \
3402 YY_LESS_LINENO(yyless_macro_arg);\
3403 zconftext[zconfleng] = (yy_hold_char); \
3404 (yy_c_buf_p) = zconftext + yyless_macro_arg; \
3405 (yy_hold_char) = *(yy_c_buf_p); \
3406 *(yy_c_buf_p) = '\0'; \
3407 zconfleng = yyless_macro_arg; \
3408 } \
3409 while ( 0 )
3410
3411/* Accessor methods (get/set functions) to struct members. */
3412
3413/** Get the current line number.
3414 *
3415 */
3416int zconfget_lineno (void)
3417{
3418
3419 return zconflineno;
3420}
3421
3422/** Get the input stream.
3423 *
3424 */
3425FILE *zconfget_in (void)
3426{
3427 return zconfin;
3428}
3429
3430/** Get the output stream.
3431 *
3432 */
3433FILE *zconfget_out (void)
3434{
3435 return zconfout;
3436}
3437
3438/** Get the length of the current token.
3439 *
3440 */
3441int zconfget_leng (void)
3442{
3443 return zconfleng;
3444}
3445
3446/** Get the current token.
3447 *
3448 */
3449
3450char *zconfget_text (void)
3451{
3452 return zconftext;
3453}
3454
3455/** Set the current line number.
3456 * @param line_number
3457 *
3458 */
3459void zconfset_lineno (int line_number )
3460{
3461
3462 zconflineno = line_number;
3463}
3464
3465/** Set the input stream. This does not discard the current
3466 * input buffer.
3467 * @param in_str A readable stream.
3468 *
3469 * @see zconf_switch_to_buffer
3470 */
3471void zconfset_in (FILE * in_str )
3472{
3473 zconfin = in_str ;
3474}
3475
3476void zconfset_out (FILE * out_str )
3477{
3478 zconfout = out_str ;
3479}
3480
3481int zconfget_debug (void)
3482{
3483 return zconf_flex_debug;
3484}
3485
3486void zconfset_debug (int bdebug )
3487{
3488 zconf_flex_debug = bdebug ;
3489}
3490
3491/* zconflex_destroy is for both reentrant and non-reentrant scanners. */
3492int zconflex_destroy (void)
3493{
3494
3495 /* Pop the buffer stack, destroying each element. */
3496 while(YY_CURRENT_BUFFER){
3497 zconf_delete_buffer(YY_CURRENT_BUFFER );
3498 YY_CURRENT_BUFFER_LVALUE = NULL;
3499 zconfpop_buffer_state();
3500 }
3501
3502 /* Destroy the stack itself. */
3503 zconffree((yy_buffer_stack) );
3504 (yy_buffer_stack) = NULL;
3505
3506 return 0;
3507}
3508
3509/*
3510 * Internal utility routines.
3511 */
3512
3513#ifndef yytext_ptr
3514static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
3515{
3516 register int i;
3517 for ( i = 0; i < n; ++i )
3518 s1[i] = s2[i];
3519}
3520#endif
3521
3522#ifdef YY_NEED_STRLEN
3523static int yy_flex_strlen (yyconst char * s )
3524{
3525 register int n;
3526 for ( n = 0; s[n]; ++n )
3527 ;
3528
3529 return n;
3530}
3531#endif
3532
3533void *zconfalloc (yy_size_t size )
3534{
3535 return (void *) malloc( size );
3536}
3537
3538void *zconfrealloc (void * ptr, yy_size_t size )
3539{
3540 /* The cast to (char *) in the following accommodates both
3541 * implementations that use char* generic pointers, and those
3542 * that use void* generic pointers. It works with the latter
3543 * because both ANSI C and C++ allow castless assignment from
3544 * any pointer type to void*, and deal with argument conversions
3545 * as though doing an assignment.
3546 */
3547 return (void *) realloc( (char *) ptr, size );
3548}
3549
3550void zconffree (void * ptr )
3551{
3552 free( (char *) ptr ); /* see zconfrealloc() for (char *) cast */
3553}
3554
3555#define YYTABLES_NAME "yytables"
3556
3557#undef YY_NEW_FILE
3558#undef YY_FLUSH_BUFFER
3559#undef yy_set_bol
3560#undef yy_new_buffer
3561#undef yy_set_interactive
3562#undef yytext_ptr
3563#undef YY_DO_BEFORE_ACTION
3564
3565#ifdef YY_DECL_IS_OURS
3566#undef YY_DECL_IS_OURS
3567#undef YY_DECL
3568#endif
3569
3570void zconf_starthelp(void)
3571{
3572 new_string();
3573 last_ts = first_ts = 0;
3574 BEGIN(HELP);
3575}
3576
3577static void zconf_endhelp(void)
3578{
3579 zconflval.string = text;
3580 BEGIN(INITIAL);
3581}
3582
3583/*
3584 * Try to open specified file with following names:
3585 * ./name
3586 * $(srctree)/name
3587 * The latter is used when srctree is separate from objtree
3588 * when compiling the kernel.
3589 * Return NULL if file is not found.
3590 */
3591FILE *zconf_fopen(const char *name)
3592{
3593 char *env, fullname[PATH_MAX+1];
3594 FILE *f;
3595
3596 f = fopen(name, "r");
3597 if (!f && name[0] != '/') {
3598 env = getenv(SRCTREE);
3599 if (env) {
3600 sprintf(fullname, "%s/%s", env, name);
3601 f = fopen(fullname, "r");
3602 }
3603 }
3604 return f;
3605}
3606
3607void zconf_initscan(const char *name)
3608{
3609 zconfin = zconf_fopen(name);
3610 if (!zconfin) {
3611 printf("can't find file %s\n", name);
3612 exit(1);
3613 }
3614
3615 current_buf = malloc(sizeof(*current_buf));
3616 memset(current_buf, 0, sizeof(*current_buf));
3617
3618 current_file = file_lookup(name);
3619 current_file->lineno = 1;
3620 current_file->flags = FILE_BUSY;
3621}
3622
3623void zconf_nextfile(const char *name)
3624{
3625 struct file *file = file_lookup(name);
3626 struct buffer *buf = malloc(sizeof(*buf));
3627 memset(buf, 0, sizeof(*buf));
3628
3629 current_buf->state = YY_CURRENT_BUFFER;
3630 zconfin = zconf_fopen(name);
3631 if (!zconfin) {
3632 printf("%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), name);
3633 exit(1);
3634 }
3635 zconf_switch_to_buffer(zconf_create_buffer(zconfin,YY_BUF_SIZE));
3636 buf->parent = current_buf;
3637 current_buf = buf;
3638
3639 if (file->flags & FILE_BUSY) {
3640 printf("recursive scan (%s)?\n", name);
3641 exit(1);
3642 }
3643 if (file->flags & FILE_SCANNED) {
3644 printf("file %s already scanned?\n", name);
3645 exit(1);
3646 }
3647 file->flags |= FILE_BUSY;
3648 file->lineno = 1;
3649 file->parent = current_file;
3650 current_file = file;
3651}
3652
3653static struct buffer *zconf_endfile(void)
3654{
3655 struct buffer *parent;
3656
3657 current_file->flags |= FILE_SCANNED;
3658 current_file->flags &= ~FILE_BUSY;
3659 current_file = current_file->parent;
3660
3661 parent = current_buf->parent;
3662 if (parent) {
3663 fclose(zconfin);
3664 zconf_delete_buffer(YY_CURRENT_BUFFER);
3665 zconf_switch_to_buffer(parent->state);
3666 }
3667 free(current_buf);
3668 current_buf = parent;
3669
3670 return parent;
3671}
3672
3673int zconf_lineno(void)
3674{
3675 if (current_buf)
3676 return current_file->lineno - 1;
3677 else
3678 return 0;
3679}
3680
3681char *zconf_curname(void)
3682{
3683 if (current_buf)
3684 return current_file->name;
3685 else
3686 return "<none>";
3687}
3688
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 @@
1/*
2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
4 */
5
6#ifndef LKC_H
7#define LKC_H
8
9#include "expr.h"
10
11#ifdef __cplusplus
12extern "C" {
13#endif
14
15#ifdef LKC_DIRECT_LINK
16#define P(name,type,arg) extern type name arg
17#else
18#include "lkc_defs.h"
19#define P(name,type,arg) extern type (*name ## _p) arg
20#endif
21#include "lkc_proto.h"
22#undef P
23
24#define SRCTREE "srctree"
25
26int zconfparse(void);
27void zconfdump(FILE *out);
28
29extern int zconfdebug;
30void zconf_starthelp(void);
31FILE *zconf_fopen(const char *name);
32void zconf_initscan(const char *name);
33void zconf_nextfile(const char *name);
34int zconf_lineno(void);
35char *zconf_curname(void);
36
37/* confdata.c */
38extern const char conf_def_filename[];
39extern char conf_filename[];
40
41char *conf_get_default_confname(void);
42
43/* kconfig_load.c */
44void kconfig_load(void);
45
46/* menu.c */
47void menu_init(void);
48void menu_add_menu(void);
49void menu_end_menu(void);
50void menu_add_entry(struct symbol *sym);
51void menu_end_entry(void);
52void menu_add_dep(struct expr *dep);
53struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep);
54void menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep);
55void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep);
56void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep);
57void menu_finalize(struct menu *parent);
58void menu_set_type(int type);
59struct file *file_lookup(const char *name);
60int file_write_dep(const char *name);
61
62extern struct menu *current_entry;
63extern struct menu *current_menu;
64
65/* symbol.c */
66void sym_init(void);
67void sym_clear_all_valid(void);
68void sym_set_changed(struct symbol *sym);
69struct symbol *sym_check_deps(struct symbol *sym);
70struct property *prop_alloc(enum prop_type type, struct symbol *sym);
71struct symbol *prop_get_symbol(struct property *prop);
72
73static inline tristate sym_get_tristate_value(struct symbol *sym)
74{
75 return sym->curr.tri;
76}
77
78
79static inline struct symbol *sym_get_choice_value(struct symbol *sym)
80{
81 return (struct symbol *)sym->curr.val;
82}
83
84static inline bool sym_set_choice_value(struct symbol *ch, struct symbol *chval)
85{
86 return sym_set_tristate_value(chval, yes);
87}
88
89static inline bool sym_is_choice(struct symbol *sym)
90{
91 return sym->flags & SYMBOL_CHOICE ? true : false;
92}
93
94static inline bool sym_is_choice_value(struct symbol *sym)
95{
96 return sym->flags & SYMBOL_CHOICEVAL ? true : false;
97}
98
99static inline bool sym_is_optional(struct symbol *sym)
100{
101 return sym->flags & SYMBOL_OPTIONAL ? true : false;
102}
103
104static inline bool sym_has_value(struct symbol *sym)
105{
106 return sym->flags & SYMBOL_NEW ? false : true;
107}
108
109#ifdef __cplusplus
110}
111#endif
112
113#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 @@
1
2/* confdata.c */
3P(conf_parse,void,(const char *name));
4P(conf_read,int,(const char *name));
5P(conf_write,int,(const char *name));
6
7/* menu.c */
8P(rootmenu,struct menu,);
9
10P(menu_is_visible,bool,(struct menu *menu));
11P(menu_get_prompt,const char *,(struct menu *menu));
12P(menu_get_root_menu,struct menu *,(struct menu *menu));
13P(menu_get_parent_menu,struct menu *,(struct menu *menu));
14
15/* symbol.c */
16P(symbol_hash,struct symbol *,[SYMBOL_HASHSIZE]);
17P(sym_change_count,int,);
18
19P(sym_lookup,struct symbol *,(const char *name, int isconst));
20P(sym_find,struct symbol *,(const char *name));
21P(sym_type_name,const char *,(enum symbol_type type));
22P(sym_calc_value,void,(struct symbol *sym));
23P(sym_get_type,enum symbol_type,(struct symbol *sym));
24P(sym_tristate_within_range,bool,(struct symbol *sym,tristate tri));
25P(sym_set_tristate_value,bool,(struct symbol *sym,tristate tri));
26P(sym_toggle_tristate_value,tristate,(struct symbol *sym));
27P(sym_string_valid,bool,(struct symbol *sym, const char *newval));
28P(sym_string_within_range,bool,(struct symbol *sym, const char *str));
29P(sym_set_string_value,bool,(struct symbol *sym, const char *newval));
30P(sym_is_changable,bool,(struct symbol *sym));
31P(sym_get_choice_prop,struct property *,(struct symbol *sym));
32P(sym_get_default_prop,struct property *,(struct symbol *sym));
33P(sym_get_string_value,const char *,(struct symbol *sym));
34
35P(prop_get_type_name,const char *,(enum prop_type type));
36
37/* expr.c */
38P(expr_compare_type,int,(enum expr_type t1, enum expr_type t2));
39P(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 @@
1/*
2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
4 *
5 * Introduced single menu mode (show all sub-menus in one large tree).
6 * 2002-11-06 Petr Baudis <pasky@ucw.cz>
7 *
8 * Directly use liblxdialog library routines.
9 * 2002-11-14 Petr Baudis <pasky@ucw.cz>
10 */
11
12#include <sys/ioctl.h>
13#include <sys/wait.h>
14#include <sys/termios.h>
15#include <ctype.h>
16#include <errno.h>
17#include <fcntl.h>
18#include <limits.h>
19#include <signal.h>
20#include <stdarg.h>
21#include <stdlib.h>
22#include <string.h>
23#include <termios.h>
24#include <unistd.h>
25
26#include "dialog.h"
27
28#define LKC_DIRECT_LINK
29#include "lkc.h"
30
31static char menu_backtitle[128];
32static const char menu_instructions[] =
33 "Arrow keys navigate the menu. "
34 "<Enter> selects submenus --->. "
35 "Highlighted letters are hotkeys. "
36 "Pressing <Y> selectes a feature, while <N> will exclude a feature. "
37 "Press <Esc><Esc> to exit, <?> for Help. "
38 "Legend: [*] feature is selected [ ] feature is excluded",
39radiolist_instructions[] =
40 "Use the arrow keys to navigate this window or "
41 "press the hotkey of the item you wish to select "
42 "followed by the <SPACE BAR>. "
43 "Press <?> for additional information about this option.",
44inputbox_instructions_int[] =
45 "Please enter a decimal value. "
46 "Fractions will not be accepted. "
47 "Use the <TAB> key to move from the input field to the buttons below it.",
48inputbox_instructions_hex[] =
49 "Please enter a hexadecimal value. "
50 "Use the <TAB> key to move from the input field to the buttons below it.",
51inputbox_instructions_string[] =
52 "Please enter a string value. "
53 "Use the <TAB> key to move from the input field to the buttons below it.",
54setmod_text[] =
55 "This feature depends on another which has been configured as a module.\n"
56 "As a result, this feature will be built as a module.",
57nohelp_text[] =
58 "There is no help available for this option.\n",
59load_config_text[] =
60 "Enter the name of the configuration file you wish to load. "
61 "Accept the name shown to restore the configuration you "
62 "last retrieved. Leave blank to abort.",
63load_config_help[] =
64 "\n"
65 "For various reasons, one may wish to keep several different BusyBox\n"
66 "configurations available on a single machine.\n"
67 "\n"
68 "If you have saved a previous configuration in a file other than the\n"
69 "BusyBox's default, entering the name of the file here will allow you\n"
70 "to modify that configuration.\n"
71 "\n"
72 "If you are uncertain, then you have probably never used alternate\n"
73 "configuration files. You should therefor leave this blank to abort.\n",
74save_config_text[] =
75 "Enter a filename to which this configuration should be saved "
76 "as an alternate. Leave blank to abort.",
77save_config_help[] =
78 "\n"
79 "For various reasons, one may wish to keep different BusyBox\n"
80 "configurations available on a single machine.\n"
81 "\n"
82 "Entering a file name here will allow you to later retrieve, modify\n"
83 "and use the current configuration as an alternate to whatever\n"
84 "configuration options you have selected at that time.\n"
85 "\n"
86 "If you are uncertain what all this means then you should probably\n"
87 "leave this blank.\n",
88top_menu_help[] =
89 "\n"
90 "Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
91 "you wish to change or submenu wish to select and press <Enter>.\n"
92 "Submenus are designated by \"--->\".\n"
93 "\n"
94 "Shortcut: Press the option's highlighted letter (hotkey).\n"
95 "\n"
96 "You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
97 "unseen options into view.\n"
98;
99
100static char filename[PATH_MAX+1] = ".config";
101static int indent = 0;
102static struct termios ios_org;
103static int rows, cols;
104struct menu *current_menu;
105static int child_count;
106static int single_menu_mode;
107
108static struct dialog_list_item *items[16384]; /* FIXME: This ought to be dynamic. */
109static int item_no;
110
111static void conf(struct menu *menu);
112static void conf_choice(struct menu *menu);
113static void conf_string(struct menu *menu);
114static void conf_load(void);
115static void conf_save(void);
116static void show_textbox(const char *title, const char *text, int r, int c);
117static void show_helptext(const char *title, const char *text);
118static void show_help(struct menu *menu);
119static void show_readme(void);
120
121static void init_wsize(void)
122{
123 struct winsize ws;
124 char *env;
125
126 if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
127 rows = 24;
128 cols = 80;
129 } else {
130 rows = ws.ws_row;
131 cols = ws.ws_col;
132 if (!rows) {
133 env = getenv("LINES");
134 if (env)
135 rows = atoi(env);
136 if (!rows)
137 rows = 24;
138 }
139 if (!cols) {
140 env = getenv("COLUMNS");
141 if (env)
142 cols = atoi(env);
143 if (!cols)
144 cols = 80;
145 }
146 }
147
148 if (rows < 19 || cols < 80) {
149 fprintf(stderr, "Your display is too small to run Menuconfig!\n");
150 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
151 exit(1);
152 }
153
154 rows -= 4;
155 cols -= 5;
156}
157
158static void cinit(void)
159{
160 item_no = 0;
161}
162
163static void cmake(void)
164{
165 items[item_no] = malloc(sizeof(struct dialog_list_item));
166 memset(items[item_no], 0, sizeof(struct dialog_list_item));
167 items[item_no]->tag = malloc(32); items[item_no]->tag[0] = 0;
168 items[item_no]->name = malloc(512); items[item_no]->name[0] = 0;
169 items[item_no]->namelen = 0;
170 item_no++;
171}
172
173static int cprint_name(const char *fmt, ...)
174{
175 va_list ap;
176 int res;
177
178 if (!item_no)
179 cmake();
180 va_start(ap, fmt);
181 res = vsnprintf(items[item_no - 1]->name + items[item_no - 1]->namelen,
182 512 - items[item_no - 1]->namelen, fmt, ap);
183 if (res > 0)
184 items[item_no - 1]->namelen += res;
185 va_end(ap);
186
187 return res;
188}
189
190static int cprint_tag(const char *fmt, ...)
191{
192 va_list ap;
193 int res;
194
195 if (!item_no)
196 cmake();
197 va_start(ap, fmt);
198 res = vsnprintf(items[item_no - 1]->tag, 32, fmt, ap);
199 va_end(ap);
200
201 return res;
202}
203
204static void cdone(void)
205{
206 int i;
207
208 for (i = 0; i < item_no; i++) {
209 free(items[i]->tag);
210 free(items[i]->name);
211 free(items[i]);
212 }
213
214 item_no = 0;
215}
216
217static void build_conf(struct menu *menu)
218{
219 struct symbol *sym;
220 struct property *prop;
221 struct menu *child;
222 int type, tmp, doint = 2;
223 tristate val;
224 char ch;
225
226 if (!menu_is_visible(menu))
227 return;
228
229 sym = menu->sym;
230 prop = menu->prompt;
231 if (!sym) {
232 if (prop && menu != current_menu) {
233 const char *prompt = menu_get_prompt(menu);
234 switch (prop->type) {
235 case P_MENU:
236 child_count++;
237 cmake();
238 cprint_tag("m%p", menu);
239
240 if (single_menu_mode) {
241 cprint_name("%s%*c%s",
242 menu->data ? "-->" : "++>",
243 indent + 1, ' ', prompt);
244 } else {
245 cprint_name(" %*c%s --->", indent + 1, ' ', prompt);
246 }
247
248 if (single_menu_mode && menu->data)
249 goto conf_childs;
250 return;
251 default:
252 if (prompt) {
253 child_count++;
254 cmake();
255 cprint_tag(":%p", menu);
256 cprint_name("---%*c%s", indent + 1, ' ', prompt);
257 }
258 }
259 } else
260 doint = 0;
261 goto conf_childs;
262 }
263
264 cmake();
265 type = sym_get_type(sym);
266 if (sym_is_choice(sym)) {
267 struct symbol *def_sym = sym_get_choice_value(sym);
268 struct menu *def_menu = NULL;
269
270 child_count++;
271 for (child = menu->list; child; child = child->next) {
272 if (menu_is_visible(child) && child->sym == def_sym)
273 def_menu = child;
274 }
275
276 val = sym_get_tristate_value(sym);
277 if (sym_is_changable(sym)) {
278 cprint_tag("t%p", menu);
279 switch (type) {
280 case S_BOOLEAN:
281 cprint_name("[%c]", val == no ? ' ' : '*');
282 break;
283 case S_TRISTATE:
284 switch (val) {
285 case yes: ch = '*'; break;
286 case mod: ch = 'M'; break;
287 default: ch = ' '; break;
288 }
289 cprint_name("<%c>", ch);
290 break;
291 }
292 } else {
293 cprint_tag("%c%p", def_menu ? 't' : ':', menu);
294 cprint_name(" ");
295 }
296
297 cprint_name("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
298 if (val == yes) {
299 if (def_menu) {
300 cprint_name(" (%s)", menu_get_prompt(def_menu));
301 cprint_name(" --->");
302 if (def_menu->list) {
303 indent += 2;
304 build_conf(def_menu);
305 indent -= 2;
306 }
307 }
308 return;
309 }
310 } else {
311 child_count++;
312 val = sym_get_tristate_value(sym);
313 if (sym_is_choice_value(sym) && val == yes) {
314 cprint_tag(":%p", menu);
315 cprint_name(" ");
316 } else {
317 switch (type) {
318 case S_BOOLEAN:
319 cprint_tag("t%p", menu);
320 if (sym_is_changable(sym))
321 cprint_name("[%c]", val == no ? ' ' : '*');
322 else
323 cprint_name("---");
324 break;
325 case S_TRISTATE:
326 cprint_tag("t%p", menu);
327 switch (val) {
328 case yes: ch = '*'; break;
329 case mod: ch = 'M'; break;
330 default: ch = ' '; break;
331 }
332 if (sym_is_changable(sym))
333 cprint_name("<%c>", ch);
334 else
335 cprint_name("---");
336 break;
337 default:
338 cprint_tag("s%p", menu);
339 tmp = cprint_name("(%s)", sym_get_string_value(sym));
340 tmp = indent - tmp + 4;
341 if (tmp < 0)
342 tmp = 0;
343 cprint_name("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
344 (sym_has_value(sym) || !sym_is_changable(sym)) ?
345 "" : " (NEW)");
346 goto conf_childs;
347 }
348 }
349 cprint_name("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
350 (sym_has_value(sym) || !sym_is_changable(sym)) ?
351 "" : " (NEW)");
352 if (menu->prompt->type == P_MENU) {
353 cprint_name(" --->");
354 return;
355 }
356 }
357
358conf_childs:
359 indent += doint;
360 for (child = menu->list; child; child = child->next)
361 build_conf(child);
362 indent -= doint;
363}
364
365static void conf(struct menu *menu)
366{
367 struct dialog_list_item *active_item = NULL;
368 struct menu *submenu;
369 const char *prompt = menu_get_prompt(menu);
370 struct symbol *sym;
371 char active_entry[40];
372 int stat, type;
373
374 unlink("lxdialog.scrltmp");
375 active_entry[0] = 0;
376 while (1) {
377 indent = 0;
378 child_count = 0;
379 current_menu = menu;
380 cdone(); cinit();
381 build_conf(menu);
382 if (!child_count)
383 break;
384 if (menu == &rootmenu) {
385 cmake(); cprint_tag(":"); cprint_name("--- ");
386 cmake(); cprint_tag("L"); cprint_name("Load an Alternate Configuration File");
387 cmake(); cprint_tag("S"); cprint_name("Save Configuration to an Alternate File");
388 }
389 dialog_clear();
390 stat = dialog_menu(prompt ? prompt : "Main Menu",
391 menu_instructions, rows, cols, rows - 10,
392 active_entry, item_no, items);
393 if (stat < 0)
394 return;
395
396 if (stat == 1 || stat == 255)
397 break;
398
399 active_item = first_sel_item(item_no, items);
400 if (!active_item)
401 continue;
402 active_item->selected = 0;
403 strncpy(active_entry, active_item->tag, sizeof(active_entry));
404 active_entry[sizeof(active_entry)-1] = 0;
405 type = active_entry[0];
406 if (!type)
407 continue;
408
409 sym = NULL;
410 submenu = NULL;
411 if (sscanf(active_entry + 1, "%p", &submenu) == 1)
412 sym = submenu->sym;
413
414 switch (stat) {
415 case 0:
416 switch (type) {
417 case 'm':
418 if (single_menu_mode)
419 submenu->data = (void *) (long) !submenu->data;
420 else
421 conf(submenu);
422 break;
423 case 't':
424 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
425 conf_choice(submenu);
426 else if (submenu->prompt->type == P_MENU)
427 conf(submenu);
428 break;
429 case 's':
430 conf_string(submenu);
431 break;
432 case 'L':
433 conf_load();
434 break;
435 case 'S':
436 conf_save();
437 break;
438 }
439 break;
440 case 2:
441 if (sym)
442 show_help(submenu);
443 else
444 show_readme();
445 break;
446 case 3:
447 if (type == 't') {
448 if (sym_set_tristate_value(sym, yes))
449 break;
450 if (sym_set_tristate_value(sym, mod))
451 show_textbox(NULL, setmod_text, 6, 74);
452 }
453 break;
454 case 4:
455 if (type == 't')
456 sym_set_tristate_value(sym, no);
457 break;
458 case 5:
459 if (type == 't')
460 sym_set_tristate_value(sym, mod);
461 break;
462 case 6:
463 if (type == 't')
464 sym_toggle_tristate_value(sym);
465 else if (type == 'm')
466 conf(submenu);
467 break;
468 }
469 }
470}
471
472static void show_textbox(const char *title, const char *text, int r, int c)
473{
474 int fd;
475
476 fd = creat(".help.tmp", 0777);
477 write(fd, text, strlen(text));
478 close(fd);
479 while (dialog_textbox(title, ".help.tmp", r, c) < 0)
480 ;
481 unlink(".help.tmp");
482}
483
484static void show_helptext(const char *title, const char *text)
485{
486 show_textbox(title, text, rows, cols);
487}
488
489static void show_help(struct menu *menu)
490{
491 const char *help;
492 char *helptext;
493 struct symbol *sym = menu->sym;
494
495 help = sym->help;
496 if (!help)
497 help = nohelp_text;
498 if (sym->name) {
499 helptext = malloc(strlen(sym->name) + strlen(help) + 16);
500 sprintf(helptext, "%s:\n\n%s", sym->name, help);
501 show_helptext(menu_get_prompt(menu), helptext);
502 free(helptext);
503 } else
504 show_helptext(menu_get_prompt(menu), help);
505}
506
507static void show_readme(void)
508{
509 show_helptext("Help", top_menu_help);
510}
511
512static void conf_choice(struct menu *menu)
513{
514 const char *prompt = menu_get_prompt(menu);
515 struct menu *child;
516 struct symbol *active;
517
518 active = sym_get_choice_value(menu->sym);
519 while (1) {
520 current_menu = menu;
521 cdone(); cinit();
522 for (child = menu->list; child; child = child->next) {
523 if (!menu_is_visible(child))
524 continue;
525 cmake();
526 cprint_tag("%p", child);
527 cprint_name("%s", menu_get_prompt(child));
528 if (child->sym == sym_get_choice_value(menu->sym))
529 items[item_no - 1]->selected = 1; /* ON */
530 else if (child->sym == active)
531 items[item_no - 1]->selected = 2; /* SELECTED */
532 else
533 items[item_no - 1]->selected = 0; /* OFF */
534 }
535
536 switch (dialog_checklist(prompt ? prompt : "Main Menu",
537 radiolist_instructions, 15, 70, 6,
538 item_no, items, FLAG_RADIO)) {
539 case 0:
540 if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) != 1)
541 break;
542 sym_set_tristate_value(child->sym, yes);
543 return;
544 case 1:
545 if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) == 1) {
546 show_help(child);
547 active = child->sym;
548 } else
549 show_help(menu);
550 break;
551 case 255:
552 return;
553 }
554 }
555}
556
557static void conf_string(struct menu *menu)
558{
559 const char *prompt = menu_get_prompt(menu);
560
561 while (1) {
562 char *heading;
563
564 switch (sym_get_type(menu->sym)) {
565 case S_INT:
566 heading = (char *) inputbox_instructions_int;
567 break;
568 case S_HEX:
569 heading = (char *) inputbox_instructions_hex;
570 break;
571 case S_STRING:
572 heading = (char *) inputbox_instructions_string;
573 break;
574 default:
575 heading = "Internal mconf error!";
576 /* panic? */;
577 }
578
579 switch (dialog_inputbox(prompt ? prompt : "Main Menu",
580 heading, 10, 75,
581 sym_get_string_value(menu->sym))) {
582 case 0:
583 if (sym_set_string_value(menu->sym, dialog_input_result))
584 return;
585 show_textbox(NULL, "You have made an invalid entry.", 5, 43);
586 break;
587 case 1:
588 show_help(menu);
589 break;
590 case 255:
591 return;
592 }
593 }
594}
595
596static void conf_load(void)
597{
598 while (1) {
599 switch (dialog_inputbox(NULL, load_config_text, 11, 55,
600 filename)) {
601 case 0:
602 if (!dialog_input_result[0])
603 return;
604 if (!conf_read(dialog_input_result))
605 return;
606 show_textbox(NULL, "File does not exist!", 5, 38);
607 break;
608 case 1:
609 show_helptext("Load Alternate Configuration", load_config_help);
610 break;
611 case 255:
612 return;
613 }
614 }
615}
616
617static void conf_save(void)
618{
619 while (1) {
620 switch (dialog_inputbox(NULL, save_config_text, 11, 55,
621 filename)) {
622 case 0:
623 if (!dialog_input_result[0])
624 return;
625 if (!conf_write(dialog_input_result))
626 return;
627 show_textbox(NULL, "Can't create file! Probably a nonexistent directory.", 5, 60);
628 break;
629 case 1:
630 show_helptext("Save Alternate Configuration", save_config_help);
631 break;
632 case 255:
633 return;
634 }
635 }
636}
637
638static void conf_cleanup(void)
639{
640 tcsetattr(1, TCSAFLUSH, &ios_org);
641 unlink(".help.tmp");
642}
643
644static void winch_handler(int sig)
645{
646 struct winsize ws;
647
648 if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
649 rows = 24;
650 cols = 80;
651 } else {
652 rows = ws.ws_row;
653 cols = ws.ws_col;
654 }
655
656 if (rows < 19 || cols < 80) {
657 end_dialog();
658 fprintf(stderr, "Your display is too small to run Menuconfig!\n");
659 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
660 exit(1);
661 }
662
663 rows -= 4;
664 cols -= 5;
665
666}
667
668int main(int ac, char **av)
669{
670 int stat;
671 char *mode;
672 struct symbol *sym;
673
674 conf_parse(av[1]);
675 conf_read(NULL);
676
677 sym = sym_lookup("VERSION", 0);
678 sym_calc_value(sym);
679 snprintf(menu_backtitle, 128, "BusyBox v%s Configuration",
680 sym_get_string_value(sym));
681
682 mode = getenv("MENUCONFIG_MODE");
683 if (mode) {
684 if (!strcasecmp(mode, "single_menu"))
685 single_menu_mode = 1;
686 }
687
688 tcgetattr(1, &ios_org);
689 atexit(conf_cleanup);
690 init_wsize();
691 init_dialog();
692 signal(SIGWINCH, winch_handler);
693 conf(&rootmenu);
694 end_dialog();
695
696 /* Restart dialog to act more like when lxdialog was still separate */
697 init_dialog();
698 do {
699 stat = dialog_yesno(NULL,
700 "Do you wish to save your new BusyBox configuration?", 5, 60);
701 } while (stat < 0);
702 end_dialog();
703
704 if (stat == 0) {
705 conf_write(NULL);
706 printf("\n\n"
707 "*** End of BusyBox configuration.\n"
708 "*** Check the top-level Makefile for additional configuration options.\n\n");
709 } else
710 printf("\n\nYour BusyBox configuration changes were NOT saved.\n\n");
711
712 return 0;
713}
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 @@
1/*
2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
4 */
5
6#include <stdlib.h>
7#include <string.h>
8
9#define LKC_DIRECT_LINK
10#include "lkc.h"
11
12struct menu rootmenu;
13struct menu *current_menu, *current_entry;
14static struct menu **last_entry_ptr;
15
16struct file *file_list;
17struct file *current_file;
18
19static void menu_warn(struct menu *menu, const char *fmt, ...)
20{
21 va_list ap;
22 va_start(ap, fmt);
23 fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno);
24 vfprintf(stderr, fmt, ap);
25 fprintf(stderr, "\n");
26 va_end(ap);
27}
28
29static void prop_warn(struct property *prop, const char *fmt, ...)
30{
31 va_list ap;
32 va_start(ap, fmt);
33 fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno);
34 vfprintf(stderr, fmt, ap);
35 fprintf(stderr, "\n");
36 va_end(ap);
37}
38
39void menu_init(void)
40{
41 current_entry = current_menu = &rootmenu;
42 last_entry_ptr = &rootmenu.list;
43}
44
45void menu_add_entry(struct symbol *sym)
46{
47 struct menu *menu;
48
49 menu = malloc(sizeof(*menu));
50 memset(menu, 0, sizeof(*menu));
51 menu->sym = sym;
52 menu->parent = current_menu;
53 menu->file = current_file;
54 menu->lineno = zconf_lineno();
55
56 *last_entry_ptr = menu;
57 last_entry_ptr = &menu->next;
58 current_entry = menu;
59}
60
61void menu_end_entry(void)
62{
63}
64
65void menu_add_menu(void)
66{
67 current_menu = current_entry;
68 last_entry_ptr = &current_entry->list;
69}
70
71void menu_end_menu(void)
72{
73 last_entry_ptr = &current_menu->next;
74 current_menu = current_menu->parent;
75}
76
77struct expr *menu_check_dep(struct expr *e)
78{
79 if (!e)
80 return e;
81
82 switch (e->type) {
83 case E_NOT:
84 e->left.expr = menu_check_dep(e->left.expr);
85 break;
86 case E_OR:
87 case E_AND:
88 e->left.expr = menu_check_dep(e->left.expr);
89 e->right.expr = menu_check_dep(e->right.expr);
90 break;
91 case E_SYMBOL:
92 /* change 'm' into 'm' && MODULES */
93 if (e->left.sym == &symbol_mod)
94 return expr_alloc_and(e, expr_alloc_symbol(modules_sym));
95 break;
96 default:
97 break;
98 }
99 return e;
100}
101
102void menu_add_dep(struct expr *dep)
103{
104 current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep));
105}
106
107void menu_set_type(int type)
108{
109 struct symbol *sym = current_entry->sym;
110
111 if (sym->type == type)
112 return;
113 if (sym->type == S_UNKNOWN) {
114 sym->type = type;
115 return;
116 }
117 menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'\n",
118 sym->name ? sym->name : "<choice>",
119 sym_type_name(sym->type), sym_type_name(type));
120}
121
122struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep)
123{
124 struct property *prop = prop_alloc(type, current_entry->sym);
125
126 prop->menu = current_entry;
127 prop->text = prompt;
128 prop->expr = expr;
129 prop->visible.expr = menu_check_dep(dep);
130
131 if (prompt) {
132 if (current_entry->prompt)
133 menu_warn(current_entry, "prompt redefined\n");
134 current_entry->prompt = prop;
135 }
136
137 return prop;
138}
139
140void menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep)
141{
142 menu_add_prop(type, prompt, NULL, dep);
143}
144
145void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep)
146{
147 menu_add_prop(type, NULL, expr, dep);
148}
149
150void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep)
151{
152 menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep);
153}
154
155void sym_check_prop(struct symbol *sym)
156{
157 struct property *prop;
158 struct symbol *sym2;
159 for (prop = sym->prop; prop; prop = prop->next) {
160 switch (prop->type) {
161 case P_DEFAULT:
162 if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) &&
163 prop->expr->type != E_SYMBOL)
164 prop_warn(prop,
165 "default for config symbol '%'"
166 " must be a single symbol", sym->name);
167 break;
168 case P_SELECT:
169 sym2 = prop_get_symbol(prop);
170 if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE)
171 prop_warn(prop,
172 "config symbol '%s' uses select, but is "
173 "not boolean or tristate", sym->name);
174 else if (sym2->type == S_UNKNOWN)
175 prop_warn(prop,
176 "'select' used by config symbol '%s' "
177 "refer to undefined symbol '%s'",
178 sym->name, sym2->name);
179 else if (sym2->type != S_BOOLEAN && sym2->type != S_TRISTATE)
180 prop_warn(prop,
181 "'%s' has wrong type. 'select' only "
182 "accept arguments of boolean and "
183 "tristate type", sym2->name);
184 break;
185 case P_RANGE:
186 if (sym->type != S_INT && sym->type != S_HEX)
187 prop_warn(prop, "range is only allowed "
188 "for int or hex symbols");
189 if (!sym_string_valid(sym, prop->expr->left.sym->name) ||
190 !sym_string_valid(sym, prop->expr->right.sym->name))
191 prop_warn(prop, "range is invalid");
192 break;
193 default:
194 ;
195 }
196 }
197}
198
199void menu_finalize(struct menu *parent)
200{
201 struct menu *menu, *last_menu;
202 struct symbol *sym;
203 struct property *prop;
204 struct expr *parentdep, *basedep, *dep, *dep2, **ep;
205
206 sym = parent->sym;
207 if (parent->list) {
208 if (sym && sym_is_choice(sym)) {
209 /* find the first choice value and find out choice type */
210 for (menu = parent->list; menu; menu = menu->next) {
211 if (menu->sym) {
212 current_entry = parent;
213 menu_set_type(menu->sym->type);
214 current_entry = menu;
215 menu_set_type(sym->type);
216 break;
217 }
218 }
219 parentdep = expr_alloc_symbol(sym);
220 } else if (parent->prompt)
221 parentdep = parent->prompt->visible.expr;
222 else
223 parentdep = parent->dep;
224
225 for (menu = parent->list; menu; menu = menu->next) {
226 basedep = expr_transform(menu->dep);
227 basedep = expr_alloc_and(expr_copy(parentdep), basedep);
228 basedep = expr_eliminate_dups(basedep);
229 menu->dep = basedep;
230 if (menu->sym)
231 prop = menu->sym->prop;
232 else
233 prop = menu->prompt;
234 for (; prop; prop = prop->next) {
235 if (prop->menu != menu)
236 continue;
237 dep = expr_transform(prop->visible.expr);
238 dep = expr_alloc_and(expr_copy(basedep), dep);
239 dep = expr_eliminate_dups(dep);
240 if (menu->sym && menu->sym->type != S_TRISTATE)
241 dep = expr_trans_bool(dep);
242 prop->visible.expr = dep;
243 if (prop->type == P_SELECT) {
244 struct symbol *es = prop_get_symbol(prop);
245 es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
246 expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
247 }
248 }
249 }
250 for (menu = parent->list; menu; menu = menu->next)
251 menu_finalize(menu);
252 } else if (sym) {
253 basedep = parent->prompt ? parent->prompt->visible.expr : NULL;
254 basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
255 basedep = expr_eliminate_dups(expr_transform(basedep));
256 last_menu = NULL;
257 for (menu = parent->next; menu; menu = menu->next) {
258 dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;
259 if (!expr_contains_symbol(dep, sym))
260 break;
261 if (expr_depends_symbol(dep, sym))
262 goto next;
263 dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
264 dep = expr_eliminate_dups(expr_transform(dep));
265 dep2 = expr_copy(basedep);
266 expr_eliminate_eq(&dep, &dep2);
267 expr_free(dep);
268 if (!expr_is_yes(dep2)) {
269 expr_free(dep2);
270 break;
271 }
272 expr_free(dep2);
273 next:
274 menu_finalize(menu);
275 menu->parent = parent;
276 last_menu = menu;
277 }
278 if (last_menu) {
279 parent->list = parent->next;
280 parent->next = last_menu->next;
281 last_menu->next = NULL;
282 }
283 }
284 for (menu = parent->list; menu; menu = menu->next) {
285 if (sym && sym_is_choice(sym) && menu->sym) {
286 menu->sym->flags |= SYMBOL_CHOICEVAL;
287 if (!menu->prompt)
288 menu_warn(menu, "choice value must have a prompt");
289 for (prop = menu->sym->prop; prop; prop = prop->next) {
290 if (prop->type == P_PROMPT && prop->menu != menu) {
291 prop_warn(prop, "choice values "
292 "currently only support a "
293 "single prompt");
294 }
295 if (prop->type == P_DEFAULT)
296 prop_warn(prop, "defaults for choice "
297 "values not supported");
298 }
299 current_entry = menu;
300 menu_set_type(sym->type);
301 menu_add_symbol(P_CHOICE, sym, NULL);
302 prop = sym_get_choice_prop(sym);
303 for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
304 ;
305 *ep = expr_alloc_one(E_CHOICE, NULL);
306 (*ep)->right.sym = menu->sym;
307 }
308 if (menu->list && (!menu->prompt || !menu->prompt->text)) {
309 for (last_menu = menu->list; ; last_menu = last_menu->next) {
310 last_menu->parent = parent;
311 if (!last_menu->next)
312 break;
313 }
314 last_menu->next = menu->next;
315 menu->next = menu->list;
316 menu->list = NULL;
317 }
318 }
319
320 if (sym && !(sym->flags & SYMBOL_WARNED)) {
321 if (sym->type == S_UNKNOWN)
322 menu_warn(parent, "config symbol defined "
323 "without type\n");
324
325 if (sym_is_choice(sym) && !parent->prompt)
326 menu_warn(parent, "choice must have a prompt\n");
327
328 /* Check properties connected to this symbol */
329 sym_check_prop(sym);
330 sym->flags |= SYMBOL_WARNED;
331 }
332
333 if (sym && !sym_is_optional(sym) && parent->prompt) {
334 sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
335 expr_alloc_and(parent->prompt->visible.expr,
336 expr_alloc_symbol(&symbol_mod)));
337 }
338}
339
340bool menu_is_visible(struct menu *menu)
341{
342 struct menu *child;
343 struct symbol *sym;
344 tristate visible;
345
346 if (!menu->prompt)
347 return false;
348 sym = menu->sym;
349 if (sym) {
350 sym_calc_value(sym);
351 visible = menu->prompt->visible.tri;
352 } else
353 visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr);
354
355 if (visible != no)
356 return true;
357 if (!sym || sym_get_tristate_value(menu->sym) == no)
358 return false;
359
360 for (child = menu->list; child; child = child->next)
361 if (menu_is_visible(child))
362 return true;
363 return false;
364}
365
366const char *menu_get_prompt(struct menu *menu)
367{
368 if (menu->prompt)
369 return menu->prompt->text;
370 else if (menu->sym)
371 return menu->sym->name;
372 return NULL;
373}
374
375struct menu *menu_get_root_menu(struct menu *menu)
376{
377 return &rootmenu;
378}
379
380struct menu *menu_get_parent_menu(struct menu *menu)
381{
382 enum prop_type type;
383
384 for (; menu != &rootmenu; menu = menu->parent) {
385 type = menu->prompt ? menu->prompt->type : 0;
386 if (type == P_MENU)
387 break;
388 }
389 return menu;
390}
391
392struct file *file_lookup(const char *name)
393{
394 struct file *file;
395
396 for (file = file_list; file; file = file->next) {
397 if (!strcmp(name, file->name))
398 return file;
399 }
400
401 file = malloc(sizeof(*file));
402 memset(file, 0, sizeof(*file));
403 file->name = strdup(name);
404 file->next = file_list;
405 file_list = file;
406 return file;
407}
408
409int file_write_dep(const char *name)
410{
411 struct file *file;
412 FILE *out;
413
414 if (!name)
415 name = ".config.cmd";
416 out = fopen(".config.tmp", "w");
417 if (!out)
418 return 1;
419 fprintf(out, "deps_config := \\\n");
420 for (file = file_list; file; file = file->next) {
421 if (file->next)
422 fprintf(out, "\t%s \\\n", file->name);
423 else
424 fprintf(out, "\t%s\n", file->name);
425 }
426 fprintf(out, "\n.config include/config.h: $(deps_config)\n\n$(deps_config):\n");
427 fclose(out);
428 rename(".config.tmp", name);
429 return 0;
430}
431
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 @@
1/*
2 * menubox.c -- implements the menu box
3 *
4 * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
5 * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22/*
23 * Changes by Clifford Wolf (god@clifford.at)
24 *
25 * [ 1998-06-13 ]
26 *
27 * *) A bugfix for the Page-Down problem
28 *
29 * *) Formerly when I used Page Down and Page Up, the cursor would be set
30 * to the first position in the menu box. Now lxdialog is a bit
31 * smarter and works more like other menu systems (just have a look at
32 * it).
33 *
34 * *) Formerly if I selected something my scrolling would be broken because
35 * lxdialog is re-invoked by the Menuconfig shell script, can't
36 * remember the last scrolling position, and just sets it so that the
37 * cursor is at the bottom of the box. Now it writes the temporary file
38 * lxdialog.scrltmp which contains this information. The file is
39 * deleted by lxdialog if the user leaves a submenu or enters a new
40 * one, but it would be nice if Menuconfig could make another "rm -f"
41 * just to be sure. Just try it out - you will recognise a difference!
42 *
43 * [ 1998-06-14 ]
44 *
45 * *) Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files
46 * and menus change their size on the fly.
47 *
48 * *) If for some reason the last scrolling position is not saved by
49 * lxdialog, it sets the scrolling so that the selected item is in the
50 * middle of the menu box, not at the bottom.
51 *
52 * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net)
53 * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus.
54 * This fixes a bug in Menuconfig where using ' ' to descend into menus
55 * would leave mis-synchronized lxdialog.scrltmp files lying around,
56 * fscanf would read in 'scroll', and eventually that value would get used.
57 */
58
59#include "dialog.h"
60
61static int menu_width, item_x;
62
63/*
64 * Print menu item
65 */
66static void
67print_item (WINDOW * win, const char *item, int choice, int selected, int hotkey)
68{
69 int j;
70 char menu_item[menu_width+1];
71
72 strncpy(menu_item, item, menu_width);
73 menu_item[menu_width] = 0;
74 j = first_alpha(menu_item, "YyNnMm");
75
76 /* Clear 'residue' of last item */
77 wattrset (win, menubox_attr);
78 wmove (win, choice, 0);
79#if OLD_NCURSES
80 {
81 int i;
82 for (i = 0; i < menu_width; i++)
83 waddch (win, ' ');
84 }
85#else
86 wclrtoeol(win);
87#endif
88 wattrset (win, selected ? item_selected_attr : item_attr);
89 mvwaddstr (win, choice, item_x, menu_item);
90 if (hotkey) {
91 wattrset (win, selected ? tag_key_selected_attr : tag_key_attr);
92 mvwaddch(win, choice, item_x+j, menu_item[j]);
93 }
94 if (selected) {
95 wmove (win, choice, item_x+1);
96 wrefresh (win);
97 }
98}
99
100/*
101 * Print the scroll indicators.
102 */
103static void
104print_arrows (WINDOW * win, int item_no, int scroll,
105 int y, int x, int height)
106{
107 int cur_y, cur_x;
108
109 getyx(win, cur_y, cur_x);
110
111 wmove(win, y, x);
112
113 if (scroll > 0) {
114 wattrset (win, uarrow_attr);
115 waddch (win, ACS_UARROW);
116 waddstr (win, "(-)");
117 }
118 else {
119 wattrset (win, menubox_attr);
120 waddch (win, ACS_HLINE);
121 waddch (win, ACS_HLINE);
122 waddch (win, ACS_HLINE);
123 waddch (win, ACS_HLINE);
124 }
125
126 y = y + height + 1;
127 wmove(win, y, x);
128
129 if ((height < item_no) && (scroll + height < item_no)) {
130 wattrset (win, darrow_attr);
131 waddch (win, ACS_DARROW);
132 waddstr (win, "(+)");
133 }
134 else {
135 wattrset (win, menubox_border_attr);
136 waddch (win, ACS_HLINE);
137 waddch (win, ACS_HLINE);
138 waddch (win, ACS_HLINE);
139 waddch (win, ACS_HLINE);
140 }
141
142 wmove(win, cur_y, cur_x);
143}
144
145/*
146 * Display the termination buttons.
147 */
148static void
149print_buttons (WINDOW *win, int height, int width, int selected)
150{
151 int x = width / 2 - 16;
152 int y = height - 2;
153
154 print_button (win, "Select", y, x, selected == 0);
155 print_button (win, " Exit ", y, x + 12, selected == 1);
156 print_button (win, " Help ", y, x + 24, selected == 2);
157
158 wmove(win, y, x+1+12*selected);
159 wrefresh (win);
160}
161
162/*
163 * Display a menu for choosing among a number of options
164 */
165int
166dialog_menu (const char *title, const char *prompt, int height, int width,
167 int menu_height, const char *current, int item_no,
168 struct dialog_list_item ** items)
169{
170 int i, j, x, y, box_x, box_y;
171 int key = 0, button = 0, scroll = 0, choice = 0, first_item = 0, max_choice;
172 WINDOW *dialog, *menu;
173 FILE *f;
174
175 max_choice = MIN (menu_height, item_no);
176
177 /* center dialog box on screen */
178 x = (COLS - width) / 2;
179 y = (LINES - height) / 2;
180
181 draw_shadow (stdscr, y, x, height, width);
182
183 dialog = newwin (height, width, y, x);
184 keypad (dialog, TRUE);
185
186 draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
187 wattrset (dialog, border_attr);
188 mvwaddch (dialog, height - 3, 0, ACS_LTEE);
189 for (i = 0; i < width - 2; i++)
190 waddch (dialog, ACS_HLINE);
191 wattrset (dialog, dialog_attr);
192 wbkgdset (dialog, dialog_attr & A_COLOR);
193 waddch (dialog, ACS_RTEE);
194
195 if (title != NULL && strlen(title) >= width-2 ) {
196 /* truncate long title -- mec */
197 char * title2 = malloc(width-2+1);
198 memcpy( title2, title, width-2 );
199 title2[width-2] = '\0';
200 title = title2;
201 }
202
203 if (title != NULL) {
204 wattrset (dialog, title_attr);
205 mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
206 waddstr (dialog, (char *)title);
207 waddch (dialog, ' ');
208 }
209
210 wattrset (dialog, dialog_attr);
211 print_autowrap (dialog, prompt, width - 2, 1, 3);
212
213 menu_width = width - 6;
214 box_y = height - menu_height - 5;
215 box_x = (width - menu_width) / 2 - 1;
216
217 /* create new window for the menu */
218 menu = subwin (dialog, menu_height, menu_width,
219 y + box_y + 1, x + box_x + 1);
220 keypad (menu, TRUE);
221
222 /* draw a box around the menu items */
223 draw_box (dialog, box_y, box_x, menu_height + 2, menu_width + 2,
224 menubox_border_attr, menubox_attr);
225
226 /*
227 * Find length of longest item in order to center menu.
228 * Set 'choice' to default item.
229 */
230 item_x = 0;
231 for (i = 0; i < item_no; i++) {
232 item_x = MAX (item_x, MIN(menu_width, strlen (items[i]->name) + 2));
233 if (strcmp(current, items[i]->tag) == 0) choice = i;
234 }
235
236 item_x = (menu_width - item_x) / 2;
237
238 /* get the scroll info from the temp file */
239 if ( (f=fopen("lxdialog.scrltmp","r")) != NULL ) {
240 if ( (fscanf(f,"%d\n",&scroll) == 1) && (scroll <= choice) &&
241 (scroll+max_choice > choice) && (scroll >= 0) &&
242 (scroll+max_choice <= item_no) ) {
243 first_item = scroll;
244 choice = choice - scroll;
245 fclose(f);
246 } else {
247 scroll=0;
248 remove("lxdialog.scrltmp");
249 fclose(f);
250 f=NULL;
251 }
252 }
253 if ( (choice >= max_choice) || (f==NULL && choice >= max_choice/2) ) {
254 if (choice >= item_no-max_choice/2)
255 scroll = first_item = item_no-max_choice;
256 else
257 scroll = first_item = choice - max_choice/2;
258 choice = choice - scroll;
259 }
260
261 /* Print the menu */
262 for (i=0; i < max_choice; i++) {
263 print_item (menu, items[first_item + i]->name, i, i == choice,
264 (items[first_item + i]->tag[0] != ':'));
265 }
266
267 wnoutrefresh (menu);
268
269 print_arrows(dialog, item_no, scroll,
270 box_y, box_x+item_x+1, menu_height);
271
272 print_buttons (dialog, height, width, 0);
273 wmove (menu, choice, item_x+1);
274 wrefresh (menu);
275
276 while (key != ESC) {
277 key = wgetch(menu);
278
279 if (key < 256 && isalpha(key)) key = tolower(key);
280
281 if (strchr("ynm", key))
282 i = max_choice;
283 else {
284 for (i = choice+1; i < max_choice; i++) {
285 j = first_alpha(items[scroll + i]->name, "YyNnMm>");
286 if (key == tolower(items[scroll + i]->name[j]))
287 break;
288 }
289 if (i == max_choice)
290 for (i = 0; i < max_choice; i++) {
291 j = first_alpha(items[scroll + i]->name, "YyNnMm>");
292 if (key == tolower(items[scroll + i]->name[j]))
293 break;
294 }
295 }
296
297 if (i < max_choice ||
298 key == KEY_UP || key == KEY_DOWN ||
299 key == '-' || key == '+' ||
300 key == KEY_PPAGE || key == KEY_NPAGE) {
301
302 print_item (menu, items[scroll + choice]->name, choice, FALSE,
303 (items[scroll + choice]->tag[0] != ':'));
304
305 if (key == KEY_UP || key == '-') {
306 if (choice < 2 && scroll) {
307 /* Scroll menu down */
308 scrollok (menu, TRUE);
309 wscrl (menu, -1);
310 scrollok (menu, FALSE);
311
312 scroll--;
313
314 print_item (menu, items[scroll]->name, 0, FALSE,
315 (items[scroll]->tag[0] != ':'));
316 } else
317 choice = MAX(choice - 1, 0);
318
319 } else if (key == KEY_DOWN || key == '+') {
320
321 print_item (menu, items[scroll + choice]->name, choice, FALSE,
322 (items[scroll + choice]->tag[0] != ':'));
323
324 if ((choice > max_choice-3) &&
325 (scroll + max_choice < item_no)
326 ) {
327 /* Scroll menu up */
328 scrollok (menu, TRUE);
329 scroll (menu);
330 scrollok (menu, FALSE);
331
332 scroll++;
333
334 print_item (menu, items[scroll + max_choice - 1]->name,
335 max_choice-1, FALSE,
336 (items[scroll + max_choice - 1]->tag[0] != ':'));
337 } else
338 choice = MIN(choice+1, max_choice-1);
339
340 } else if (key == KEY_PPAGE) {
341 scrollok (menu, TRUE);
342 for (i=0; (i < max_choice); i++) {
343 if (scroll > 0) {
344 wscrl (menu, -1);
345 scroll--;
346 print_item (menu, items[scroll]->name, 0, FALSE,
347 (items[scroll]->tag[0] != ':'));
348 } else {
349 if (choice > 0)
350 choice--;
351 }
352 }
353 scrollok (menu, FALSE);
354
355 } else if (key == KEY_NPAGE) {
356 for (i=0; (i < max_choice); i++) {
357 if (scroll+max_choice < item_no) {
358 scrollok (menu, TRUE);
359 scroll(menu);
360 scrollok (menu, FALSE);
361 scroll++;
362 print_item (menu, items[scroll + max_choice - 1]->name,
363 max_choice-1, FALSE,
364 (items[scroll + max_choice - 1]->tag[0] != ':'));
365 } else {
366 if (choice+1 < max_choice)
367 choice++;
368 }
369 }
370
371 } else
372 choice = i;
373
374 print_item (menu, items[scroll + choice]->name, choice, TRUE,
375 (items[scroll + choice]->tag[0] != ':'));
376
377 print_arrows(dialog, item_no, scroll,
378 box_y, box_x+item_x+1, menu_height);
379
380 wnoutrefresh (dialog);
381 wrefresh (menu);
382
383 continue; /* wait for another key press */
384 }
385
386 switch (key) {
387 case KEY_LEFT:
388 case TAB:
389 case KEY_RIGHT:
390 button = ((key == KEY_LEFT ? --button : ++button) < 0)
391 ? 2 : (button > 2 ? 0 : button);
392
393 print_buttons(dialog, height, width, button);
394 wrefresh (menu);
395 break;
396 case ' ':
397 case 's':
398 case 'y':
399 case 'n':
400 case 'm':
401 /* save scroll info */
402 if ( (f=fopen("lxdialog.scrltmp","w")) != NULL ) {
403 fprintf(f,"%d\n",scroll);
404 fclose(f);
405 }
406 delwin (dialog);
407 items[scroll + choice]->selected = 1;
408 switch (key) {
409 case 's': return 3;
410 case 'y': return 3;
411 case 'n': return 4;
412 case 'm': return 5;
413 case ' ': return 6;
414 }
415 return 0;
416 case 'h':
417 case '?':
418 button = 2;
419 case '\n':
420 delwin (dialog);
421 items[scroll + choice]->selected = 1;
422
423 remove("lxdialog.scrltmp");
424 return button;
425 case 'e':
426 case 'x':
427 key = ESC;
428 case ESC:
429 break;
430 }
431 }
432
433 delwin (dialog);
434 remove("lxdialog.scrltmp");
435 return -1; /* ESC pressed */
436}
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 @@
1/*
2 * msgbox.c -- implements the message box and info box
3 *
4 * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
5 * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include "dialog.h"
23
24/*
25 * Display a message box. Program will pause and display an "OK" button
26 * if the parameter 'pause' is non-zero.
27 */
28int
29dialog_msgbox (const char *title, const char *prompt, int height, int width,
30 int pause)
31{
32 int i, x, y, key = 0;
33 WINDOW *dialog;
34
35 /* center dialog box on screen */
36 x = (COLS - width) / 2;
37 y = (LINES - height) / 2;
38
39 draw_shadow (stdscr, y, x, height, width);
40
41 dialog = newwin (height, width, y, x);
42 keypad (dialog, TRUE);
43
44 draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
45
46 if (title != NULL && strlen(title) >= width-2 ) {
47 /* truncate long title -- mec */
48 char * title2 = malloc(width-2+1);
49 memcpy( title2, title, width-2 );
50 title2[width-2] = '\0';
51 title = title2;
52 }
53
54 if (title != NULL) {
55 wattrset (dialog, title_attr);
56 mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
57 waddstr (dialog, (char *)title);
58 waddch (dialog, ' ');
59 }
60 wattrset (dialog, dialog_attr);
61 print_autowrap (dialog, prompt, width - 2, 1, 2);
62
63 if (pause) {
64 wattrset (dialog, border_attr);
65 mvwaddch (dialog, height - 3, 0, ACS_LTEE);
66 for (i = 0; i < width - 2; i++)
67 waddch (dialog, ACS_HLINE);
68 wattrset (dialog, dialog_attr);
69 waddch (dialog, ACS_RTEE);
70
71 print_button (dialog, " Ok ",
72 height - 2, width / 2 - 4, TRUE);
73
74 wrefresh (dialog);
75 while (key != ESC && key != '\n' && key != ' ' &&
76 key != 'O' && key != 'o' && key != 'X' && key != 'x')
77 key = wgetch (dialog);
78 } else {
79 key = '\n';
80 wrefresh (dialog);
81 }
82
83 delwin (dialog);
84 return key == ESC ? -1 : 0;
85}
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 @@
1/*
2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
4 */
5
6#include <ctype.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/utsname.h>
10
11#define LKC_DIRECT_LINK
12#include "lkc.h"
13
14struct symbol symbol_yes = {
15 .name = "y",
16 .curr = { "y", yes },
17 .flags = SYMBOL_YES|SYMBOL_VALID,
18}, symbol_mod = {
19 .name = "m",
20 .curr = { "m", mod },
21 .flags = SYMBOL_MOD|SYMBOL_VALID,
22}, symbol_no = {
23 .name = "n",
24 .curr = { "n", no },
25 .flags = SYMBOL_NO|SYMBOL_VALID,
26}, symbol_empty = {
27 .name = "",
28 .curr = { "", no },
29 .flags = SYMBOL_VALID,
30};
31
32int sym_change_count;
33struct symbol *modules_sym;
34tristate modules_val;
35
36void sym_add_default(struct symbol *sym, const char *def)
37{
38 struct property *prop = prop_alloc(P_DEFAULT, sym);
39
40 prop->expr = expr_alloc_symbol(sym_lookup(def, 1));
41}
42
43void sym_init(void)
44{
45 struct symbol *sym;
46 char *p;
47 static bool inited = false;
48
49 if (inited)
50 return;
51 inited = true;
52
53 sym = sym_lookup("VERSION", 0);
54 sym->type = S_STRING;
55 sym->flags |= SYMBOL_AUTO;
56 p = getenv("VERSION");
57 if (p)
58 sym_add_default(sym, p);
59
60 sym = sym_lookup("TARGET_ARCH", 0);
61 sym->type = S_STRING;
62 sym->flags |= SYMBOL_AUTO;
63 p = getenv("TARGET_ARCH");
64 if (p)
65 sym_add_default(sym, p);
66
67}
68
69enum symbol_type sym_get_type(struct symbol *sym)
70{
71 enum symbol_type type = sym->type;
72
73 if (type == S_TRISTATE) {
74 if (sym_is_choice_value(sym) && sym->visible == yes)
75 type = S_BOOLEAN;
76 else if (modules_val == no)
77 type = S_BOOLEAN;
78 }
79 return type;
80}
81
82const char *sym_type_name(enum symbol_type type)
83{
84 switch (type) {
85 case S_BOOLEAN:
86 return "boolean";
87 case S_TRISTATE:
88 return "tristate";
89 case S_INT:
90 return "integer";
91 case S_HEX:
92 return "hex";
93 case S_STRING:
94 return "string";
95 case S_UNKNOWN:
96 return "unknown";
97 case S_OTHER:
98 break;
99 }
100 return "???";
101}
102
103struct property *sym_get_choice_prop(struct symbol *sym)
104{
105 struct property *prop;
106
107 for_all_choices(sym, prop)
108 return prop;
109 return NULL;
110}
111
112struct property *sym_get_default_prop(struct symbol *sym)
113{
114 struct property *prop;
115
116 for_all_defaults(sym, prop) {
117 prop->visible.tri = expr_calc_value(prop->visible.expr);
118 if (prop->visible.tri != no)
119 return prop;
120 }
121 return NULL;
122}
123
124struct property *sym_get_range_prop(struct symbol *sym)
125{
126 struct property *prop;
127
128 for_all_properties(sym, prop, P_RANGE) {
129 prop->visible.tri = expr_calc_value(prop->visible.expr);
130 if (prop->visible.tri != no)
131 return prop;
132 }
133 return NULL;
134}
135
136static void sym_calc_visibility(struct symbol *sym)
137{
138 struct property *prop;
139 tristate tri;
140
141 /* any prompt visible? */
142 tri = no;
143 for_all_prompts(sym, prop) {
144 prop->visible.tri = expr_calc_value(prop->visible.expr);
145 tri = E_OR(tri, prop->visible.tri);
146 }
147 if (tri == mod && (sym->type != S_TRISTATE || modules_val == no))
148 tri = yes;
149 if (sym->visible != tri) {
150 sym->visible = tri;
151 sym_set_changed(sym);
152 }
153 if (sym_is_choice_value(sym))
154 return;
155 tri = no;
156 if (sym->rev_dep.expr)
157 tri = expr_calc_value(sym->rev_dep.expr);
158 if (tri == mod && sym_get_type(sym) == S_BOOLEAN)
159 tri = yes;
160 if (sym->rev_dep.tri != tri) {
161 sym->rev_dep.tri = tri;
162 sym_set_changed(sym);
163 }
164}
165
166static struct symbol *sym_calc_choice(struct symbol *sym)
167{
168 struct symbol *def_sym;
169 struct property *prop;
170 struct expr *e;
171
172 /* is the user choice visible? */
173 def_sym = sym->user.val;
174 if (def_sym) {
175 sym_calc_visibility(def_sym);
176 if (def_sym->visible != no)
177 return def_sym;
178 }
179
180 /* any of the defaults visible? */
181 for_all_defaults(sym, prop) {
182 prop->visible.tri = expr_calc_value(prop->visible.expr);
183 if (prop->visible.tri == no)
184 continue;
185 def_sym = prop_get_symbol(prop);
186 sym_calc_visibility(def_sym);
187 if (def_sym->visible != no)
188 return def_sym;
189 }
190
191 /* just get the first visible value */
192 prop = sym_get_choice_prop(sym);
193 for (e = prop->expr; e; e = e->left.expr) {
194 def_sym = e->right.sym;
195 sym_calc_visibility(def_sym);
196 if (def_sym->visible != no)
197 return def_sym;
198 }
199
200 /* no choice? reset tristate value */
201 sym->curr.tri = no;
202 return NULL;
203}
204
205void sym_calc_value(struct symbol *sym)
206{
207 struct symbol_value newval, oldval;
208 struct property *prop;
209 struct expr *e;
210
211 if (!sym)
212 return;
213
214 if (sym->flags & SYMBOL_VALID)
215 return;
216 sym->flags |= SYMBOL_VALID;
217
218 oldval = sym->curr;
219
220 switch (sym->type) {
221 case S_INT:
222 case S_HEX:
223 case S_STRING:
224 newval = symbol_empty.curr;
225 break;
226 case S_BOOLEAN:
227 case S_TRISTATE:
228 newval = symbol_no.curr;
229 break;
230 default:
231 sym->curr.val = sym->name;
232 sym->curr.tri = no;
233 return;
234 }
235 if (!sym_is_choice_value(sym))
236 sym->flags &= ~SYMBOL_WRITE;
237
238 sym_calc_visibility(sym);
239
240 /* set default if recursively called */
241 sym->curr = newval;
242
243 switch (sym_get_type(sym)) {
244 case S_BOOLEAN:
245 case S_TRISTATE:
246 if (sym_is_choice_value(sym) && sym->visible == yes) {
247 prop = sym_get_choice_prop(sym);
248 newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no;
249 } else if (E_OR(sym->visible, sym->rev_dep.tri) != no) {
250 sym->flags |= SYMBOL_WRITE;
251 if (sym_has_value(sym))
252 newval.tri = sym->user.tri;
253 else if (!sym_is_choice(sym)) {
254 prop = sym_get_default_prop(sym);
255 if (prop)
256 newval.tri = expr_calc_value(prop->expr);
257 }
258 newval.tri = E_OR(E_AND(newval.tri, sym->visible), sym->rev_dep.tri);
259 } else if (!sym_is_choice(sym)) {
260 prop = sym_get_default_prop(sym);
261 if (prop) {
262 sym->flags |= SYMBOL_WRITE;
263 newval.tri = expr_calc_value(prop->expr);
264 }
265 }
266 if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN)
267 newval.tri = yes;
268 break;
269 case S_STRING:
270 case S_HEX:
271 case S_INT:
272 if (sym->visible != no) {
273 sym->flags |= SYMBOL_WRITE;
274 if (sym_has_value(sym)) {
275 newval.val = sym->user.val;
276 break;
277 }
278 }
279 prop = sym_get_default_prop(sym);
280 if (prop) {
281 struct symbol *ds = prop_get_symbol(prop);
282 if (ds) {
283 sym->flags |= SYMBOL_WRITE;
284 sym_calc_value(ds);
285 newval.val = ds->curr.val;
286 }
287 }
288 break;
289 default:
290 ;
291 }
292
293 sym->curr = newval;
294 if (sym_is_choice(sym) && newval.tri == yes)
295 sym->curr.val = sym_calc_choice(sym);
296
297 if (memcmp(&oldval, &sym->curr, sizeof(oldval)))
298 sym_set_changed(sym);
299 if (modules_sym == sym)
300 modules_val = modules_sym->curr.tri;
301
302 if (sym_is_choice(sym)) {
303 int flags = sym->flags & (SYMBOL_CHANGED | SYMBOL_WRITE);
304 prop = sym_get_choice_prop(sym);
305 for (e = prop->expr; e; e = e->left.expr) {
306 e->right.sym->flags |= flags;
307 if (flags & SYMBOL_CHANGED)
308 sym_set_changed(e->right.sym);
309 }
310 }
311}
312
313void sym_clear_all_valid(void)
314{
315 struct symbol *sym;
316 int i;
317
318 for_all_symbols(i, sym)
319 sym->flags &= ~SYMBOL_VALID;
320 sym_change_count++;
321 if (modules_sym)
322 sym_calc_value(modules_sym);
323}
324
325void sym_set_changed(struct symbol *sym)
326{
327 struct property *prop;
328
329 sym->flags |= SYMBOL_CHANGED;
330 for (prop = sym->prop; prop; prop = prop->next) {
331 if (prop->menu)
332 prop->menu->flags |= MENU_CHANGED;
333 }
334}
335
336void sym_set_all_changed(void)
337{
338 struct symbol *sym;
339 int i;
340
341 for_all_symbols(i, sym)
342 sym_set_changed(sym);
343}
344
345bool sym_tristate_within_range(struct symbol *sym, tristate val)
346{
347 int type = sym_get_type(sym);
348
349 if (sym->visible == no)
350 return false;
351
352 if (type != S_BOOLEAN && type != S_TRISTATE)
353 return false;
354
355 if (type == S_BOOLEAN && val == mod)
356 return false;
357 if (sym->visible <= sym->rev_dep.tri)
358 return false;
359 if (sym_is_choice_value(sym) && sym->visible == yes)
360 return val == yes;
361 return val >= sym->rev_dep.tri && val <= sym->visible;
362}
363
364bool sym_set_tristate_value(struct symbol *sym, tristate val)
365{
366 tristate oldval = sym_get_tristate_value(sym);
367
368 if (oldval != val && !sym_tristate_within_range(sym, val))
369 return false;
370
371 if (sym->flags & SYMBOL_NEW) {
372 sym->flags &= ~SYMBOL_NEW;
373 sym_set_changed(sym);
374 }
375 if (sym_is_choice_value(sym) && val == yes) {
376 struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
377
378 cs->user.val = sym;
379 cs->flags &= ~SYMBOL_NEW;
380 }
381
382 sym->user.tri = val;
383 if (oldval != val) {
384 sym_clear_all_valid();
385 if (sym == modules_sym)
386 sym_set_all_changed();
387 }
388
389 return true;
390}
391
392tristate sym_toggle_tristate_value(struct symbol *sym)
393{
394 tristate oldval, newval;
395
396 oldval = newval = sym_get_tristate_value(sym);
397 do {
398 switch (newval) {
399 case no:
400 newval = mod;
401 break;
402 case mod:
403 newval = yes;
404 break;
405 case yes:
406 newval = no;
407 break;
408 }
409 if (sym_set_tristate_value(sym, newval))
410 break;
411 } while (oldval != newval);
412 return newval;
413}
414
415bool sym_string_valid(struct symbol *sym, const char *str)
416{
417 char ch;
418
419 switch (sym->type) {
420 case S_STRING:
421 return true;
422 case S_INT:
423 ch = *str++;
424 if (ch == '-')
425 ch = *str++;
426 if (!isdigit(ch))
427 return false;
428 if (ch == '0' && *str != 0)
429 return false;
430 while ((ch = *str++)) {
431 if (!isdigit(ch))
432 return false;
433 }
434 return true;
435 case S_HEX:
436 if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
437 str += 2;
438 ch = *str++;
439 do {
440 if (!isxdigit(ch))
441 return false;
442 } while ((ch = *str++));
443 return true;
444 case S_BOOLEAN:
445 case S_TRISTATE:
446 switch (str[0]) {
447 case 'y': case 'Y':
448 case 'm': case 'M':
449 case 'n': case 'N':
450 return true;
451 }
452 return false;
453 default:
454 return false;
455 }
456}
457
458bool sym_string_within_range(struct symbol *sym, const char *str)
459{
460 struct property *prop;
461 int val;
462
463 switch (sym->type) {
464 case S_STRING:
465 return sym_string_valid(sym, str);
466 case S_INT:
467 if (!sym_string_valid(sym, str))
468 return false;
469 prop = sym_get_range_prop(sym);
470 if (!prop)
471 return true;
472 val = strtol(str, NULL, 10);
473 return val >= strtol(prop->expr->left.sym->name, NULL, 10) &&
474 val <= strtol(prop->expr->right.sym->name, NULL, 10);
475 case S_HEX:
476 if (!sym_string_valid(sym, str))
477 return false;
478 prop = sym_get_range_prop(sym);
479 if (!prop)
480 return true;
481 val = strtol(str, NULL, 16);
482 return val >= strtol(prop->expr->left.sym->name, NULL, 16) &&
483 val <= strtol(prop->expr->right.sym->name, NULL, 16);
484 case S_BOOLEAN:
485 case S_TRISTATE:
486 switch (str[0]) {
487 case 'y': case 'Y':
488 return sym_tristate_within_range(sym, yes);
489 case 'm': case 'M':
490 return sym_tristate_within_range(sym, mod);
491 case 'n': case 'N':
492 return sym_tristate_within_range(sym, no);
493 }
494 return false;
495 default:
496 return false;
497 }
498}
499
500bool sym_set_string_value(struct symbol *sym, const char *newval)
501{
502 const char *oldval;
503 char *val;
504 int size;
505
506 switch (sym->type) {
507 case S_BOOLEAN:
508 case S_TRISTATE:
509 switch (newval[0]) {
510 case 'y': case 'Y':
511 return sym_set_tristate_value(sym, yes);
512 case 'm': case 'M':
513 return sym_set_tristate_value(sym, mod);
514 case 'n': case 'N':
515 return sym_set_tristate_value(sym, no);
516 }
517 return false;
518 default:
519 ;
520 }
521
522 if (!sym_string_within_range(sym, newval))
523 return false;
524
525 if (sym->flags & SYMBOL_NEW) {
526 sym->flags &= ~SYMBOL_NEW;
527 sym_set_changed(sym);
528 }
529
530 oldval = sym->user.val;
531 size = strlen(newval) + 1;
532 if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) {
533 size += 2;
534 sym->user.val = val = malloc(size);
535 *val++ = '0';
536 *val++ = 'x';
537 } else if (!oldval || strcmp(oldval, newval))
538 sym->user.val = val = malloc(size);
539 else
540 return true;
541
542 strcpy(val, newval);
543 free((void *)oldval);
544 sym_clear_all_valid();
545
546 return true;
547}
548
549const char *sym_get_string_value(struct symbol *sym)
550{
551 tristate val;
552
553 switch (sym->type) {
554 case S_BOOLEAN:
555 case S_TRISTATE:
556 val = sym_get_tristate_value(sym);
557 switch (val) {
558 case no:
559 return "n";
560 case mod:
561 return "m";
562 case yes:
563 return "y";
564 }
565 break;
566 default:
567 ;
568 }
569 return (const char *)sym->curr.val;
570}
571
572bool sym_is_changable(struct symbol *sym)
573{
574 return sym->visible > sym->rev_dep.tri;
575}
576
577struct symbol *sym_lookup(const char *name, int isconst)
578{
579 struct symbol *symbol;
580 const char *ptr;
581 char *new_name;
582 int hash = 0;
583
584 if (name) {
585 if (name[0] && !name[1]) {
586 switch (name[0]) {
587 case 'y': return &symbol_yes;
588 case 'm': return &symbol_mod;
589 case 'n': return &symbol_no;
590 }
591 }
592 for (ptr = name; *ptr; ptr++)
593 hash += *ptr;
594 hash &= 0xff;
595
596 for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
597 if (!strcmp(symbol->name, name)) {
598 if ((isconst && symbol->flags & SYMBOL_CONST) ||
599 (!isconst && !(symbol->flags & SYMBOL_CONST)))
600 return symbol;
601 }
602 }
603 new_name = strdup(name);
604 } else {
605 new_name = NULL;
606 hash = 256;
607 }
608
609 symbol = malloc(sizeof(*symbol));
610 memset(symbol, 0, sizeof(*symbol));
611 symbol->name = new_name;
612 symbol->type = S_UNKNOWN;
613 symbol->flags = SYMBOL_NEW;
614 if (isconst)
615 symbol->flags |= SYMBOL_CONST;
616
617 symbol->next = symbol_hash[hash];
618 symbol_hash[hash] = symbol;
619
620 return symbol;
621}
622
623struct symbol *sym_find(const char *name)
624{
625 struct symbol *symbol = NULL;
626 const char *ptr;
627 int hash = 0;
628
629 if (!name)
630 return NULL;
631
632 if (name[0] && !name[1]) {
633 switch (name[0]) {
634 case 'y': return &symbol_yes;
635 case 'm': return &symbol_mod;
636 case 'n': return &symbol_no;
637 }
638 }
639 for (ptr = name; *ptr; ptr++)
640 hash += *ptr;
641 hash &= 0xff;
642
643 for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
644 if (!strcmp(symbol->name, name) &&
645 !(symbol->flags & SYMBOL_CONST))
646 break;
647 }
648
649 return symbol;
650}
651
652struct symbol *sym_check_deps(struct symbol *sym);
653
654static struct symbol *sym_check_expr_deps(struct expr *e)
655{
656 struct symbol *sym;
657
658 if (!e)
659 return NULL;
660 switch (e->type) {
661 case E_OR:
662 case E_AND:
663 sym = sym_check_expr_deps(e->left.expr);
664 if (sym)
665 return sym;
666 return sym_check_expr_deps(e->right.expr);
667 case E_NOT:
668 return sym_check_expr_deps(e->left.expr);
669 case E_EQUAL:
670 case E_UNEQUAL:
671 sym = sym_check_deps(e->left.sym);
672 if (sym)
673 return sym;
674 return sym_check_deps(e->right.sym);
675 case E_SYMBOL:
676 return sym_check_deps(e->left.sym);
677 default:
678 break;
679 }
680 printf("Oops! How to check %d?\n", e->type);
681 return NULL;
682}
683
684struct symbol *sym_check_deps(struct symbol *sym)
685{
686 struct symbol *sym2;
687 struct property *prop;
688
689 if (sym->flags & SYMBOL_CHECK_DONE)
690 return NULL;
691 if (sym->flags & SYMBOL_CHECK) {
692 printf("Warning! Found recursive dependency: %s", sym->name);
693 return sym;
694 }
695
696 sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);
697 sym2 = sym_check_expr_deps(sym->rev_dep.expr);
698 if (sym2)
699 goto out;
700
701 for (prop = sym->prop; prop; prop = prop->next) {
702 if (prop->type == P_CHOICE || prop->type == P_SELECT)
703 continue;
704 sym2 = sym_check_expr_deps(prop->visible.expr);
705 if (sym2)
706 goto out;
707 if (prop->type != P_DEFAULT || sym_is_choice(sym))
708 continue;
709 sym2 = sym_check_expr_deps(prop->expr);
710 if (sym2)
711 goto out;
712 }
713out:
714 if (sym2)
715 printf(" %s", sym->name);
716 sym->flags &= ~SYMBOL_CHECK;
717 return sym2;
718}
719
720struct property *prop_alloc(enum prop_type type, struct symbol *sym)
721{
722 struct property *prop;
723 struct property **propp;
724
725 prop = malloc(sizeof(*prop));
726 memset(prop, 0, sizeof(*prop));
727 prop->type = type;
728 prop->sym = sym;
729 prop->file = current_file;
730 prop->lineno = zconf_lineno();
731
732 /* append property to the prop list of symbol */
733 if (sym) {
734 for (propp = &sym->prop; *propp; propp = &(*propp)->next)
735 ;
736 *propp = prop;
737 }
738
739 return prop;
740}
741
742struct symbol *prop_get_symbol(struct property *prop)
743{
744 if (prop->expr && (prop->expr->type == E_SYMBOL ||
745 prop->expr->type == E_CHOICE))
746 return prop->expr->left.sym;
747 return NULL;
748}
749
750const char *prop_get_type_name(enum prop_type type)
751{
752 switch (type) {
753 case P_PROMPT:
754 return "prompt";
755 case P_COMMENT:
756 return "comment";
757 case P_MENU:
758 return "menu";
759 case P_DEFAULT:
760 return "default";
761 case P_CHOICE:
762 return "choice";
763 case P_SELECT:
764 return "select";
765 case P_RANGE:
766 return "range";
767 case P_UNKNOWN:
768 break;
769 }
770 return "unknown";
771}
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 @@
1/*
2 * textbox.c -- implements the text box
3 *
4 * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
5 * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include "dialog.h"
23
24static void back_lines (int n);
25static void print_page (WINDOW * win, int height, int width);
26static void print_line (WINDOW * win, int row, int width);
27static char *get_line (void);
28static void print_position (WINDOW * win, int height, int width);
29
30static int hscroll, fd, file_size, bytes_read;
31static int begin_reached = 1, end_reached, page_length;
32static char *buf, *page;
33
34/*
35 * Display text from a file in a dialog box.
36 */
37int
38dialog_textbox (const char *title, const char *file, int height, int width)
39{
40 int i, x, y, cur_x, cur_y, fpos, key = 0;
41 int passed_end;
42 char search_term[MAX_LEN + 1];
43 WINDOW *dialog, *text;
44
45 search_term[0] = '\0'; /* no search term entered yet */
46
47 /* Open input file for reading */
48 if ((fd = open (file, O_RDONLY)) == -1) {
49 endwin ();
50 fprintf (stderr,
51 "\nCan't open input file in dialog_textbox().\n");
52 exit (-1);
53 }
54 /* Get file size. Actually, 'file_size' is the real file size - 1,
55 since it's only the last byte offset from the beginning */
56 if ((file_size = lseek (fd, 0, SEEK_END)) == -1) {
57 endwin ();
58 fprintf (stderr, "\nError getting file size in dialog_textbox().\n");
59 exit (-1);
60 }
61 /* Restore file pointer to beginning of file after getting file size */
62 if (lseek (fd, 0, SEEK_SET) == -1) {
63 endwin ();
64 fprintf (stderr, "\nError moving file pointer in dialog_textbox().\n");
65 exit (-1);
66 }
67 /* Allocate space for read buffer */
68 if ((buf = malloc (BUF_SIZE + 1)) == NULL) {
69 endwin ();
70 fprintf (stderr, "\nCan't allocate memory in dialog_textbox().\n");
71 exit (-1);
72 }
73 if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
74 endwin ();
75 fprintf (stderr, "\nError reading file in dialog_textbox().\n");
76 exit (-1);
77 }
78 buf[bytes_read] = '\0'; /* mark end of valid data */
79 page = buf; /* page is pointer to start of page to be displayed */
80
81 /* center dialog box on screen */
82 x = (COLS - width) / 2;
83 y = (LINES - height) / 2;
84
85
86 draw_shadow (stdscr, y, x, height, width);
87
88 dialog = newwin (height, width, y, x);
89 keypad (dialog, TRUE);
90
91 /* Create window for text region, used for scrolling text */
92 text = subwin (dialog, height - 4, width - 2, y + 1, x + 1);
93 wattrset (text, dialog_attr);
94 wbkgdset (text, dialog_attr & A_COLOR);
95
96 keypad (text, TRUE);
97
98 /* register the new window, along with its borders */
99 draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
100
101 wattrset (dialog, border_attr);
102 mvwaddch (dialog, height-3, 0, ACS_LTEE);
103 for (i = 0; i < width - 2; i++)
104 waddch (dialog, ACS_HLINE);
105 wattrset (dialog, dialog_attr);
106 wbkgdset (dialog, dialog_attr & A_COLOR);
107 waddch (dialog, ACS_RTEE);
108
109 if (title != NULL && strlen(title) >= width-2 ) {
110 /* truncate long title -- mec */
111 char * title2 = malloc(width-2+1);
112 memcpy( title2, title, width-2 );
113 title2[width-2] = '\0';
114 title = title2;
115 }
116
117 if (title != NULL) {
118 wattrset (dialog, title_attr);
119 mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
120 waddstr (dialog, (char *)title);
121 waddch (dialog, ' ');
122 }
123 print_button (dialog, " Exit ", height - 2, width / 2 - 4, TRUE);
124 wnoutrefresh (dialog);
125 getyx (dialog, cur_y, cur_x); /* Save cursor position */
126
127 /* Print first page of text */
128 attr_clear (text, height - 4, width - 2, dialog_attr);
129 print_page (text, height - 4, width - 2);
130 print_position (dialog, height, width);
131 wmove (dialog, cur_y, cur_x); /* Restore cursor position */
132 wrefresh (dialog);
133
134 while ((key != ESC) && (key != '\n')) {
135 key = wgetch (dialog);
136 switch (key) {
137 case 'E': /* Exit */
138 case 'e':
139 case 'X':
140 case 'x':
141 delwin (dialog);
142 free (buf);
143 close (fd);
144 return 0;
145 case 'g': /* First page */
146 case KEY_HOME:
147 if (!begin_reached) {
148 begin_reached = 1;
149 /* First page not in buffer? */
150 if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
151 endwin ();
152 fprintf (stderr,
153 "\nError moving file pointer in dialog_textbox().\n");
154 exit (-1);
155 }
156 if (fpos > bytes_read) { /* Yes, we have to read it in */
157 if (lseek (fd, 0, SEEK_SET) == -1) {
158 endwin ();
159 fprintf (stderr, "\nError moving file pointer in "
160 "dialog_textbox().\n");
161 exit (-1);
162 }
163 if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
164 endwin ();
165 fprintf (stderr,
166 "\nError reading file in dialog_textbox().\n");
167 exit (-1);
168 }
169 buf[bytes_read] = '\0';
170 }
171 page = buf;
172 print_page (text, height - 4, width - 2);
173 print_position (dialog, height, width);
174 wmove (dialog, cur_y, cur_x); /* Restore cursor position */
175 wrefresh (dialog);
176 }
177 break;
178 case 'G': /* Last page */
179 case KEY_END:
180
181 end_reached = 1;
182 /* Last page not in buffer? */
183 if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
184 endwin ();
185 fprintf (stderr,
186 "\nError moving file pointer in dialog_textbox().\n");
187 exit (-1);
188 }
189 if (fpos < file_size) { /* Yes, we have to read it in */
190 if (lseek (fd, -BUF_SIZE, SEEK_END) == -1) {
191 endwin ();
192 fprintf (stderr,
193 "\nError moving file pointer in dialog_textbox().\n");
194 exit (-1);
195 }
196 if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
197 endwin ();
198 fprintf (stderr,
199 "\nError reading file in dialog_textbox().\n");
200 exit (-1);
201 }
202 buf[bytes_read] = '\0';
203 }
204 page = buf + bytes_read;
205 back_lines (height - 4);
206 print_page (text, height - 4, width - 2);
207 print_position (dialog, height, width);
208 wmove (dialog, cur_y, cur_x); /* Restore cursor position */
209 wrefresh (dialog);
210 break;
211 case 'K': /* Previous line */
212 case 'k':
213 case KEY_UP:
214 if (!begin_reached) {
215 back_lines (page_length + 1);
216
217 /* We don't call print_page() here but use scrolling to ensure
218 faster screen update. However, 'end_reached' and
219 'page_length' should still be updated, and 'page' should
220 point to start of next page. This is done by calling
221 get_line() in the following 'for' loop. */
222 scrollok (text, TRUE);
223 wscrl (text, -1); /* Scroll text region down one line */
224 scrollok (text, FALSE);
225 page_length = 0;
226 passed_end = 0;
227 for (i = 0; i < height - 4; i++) {
228 if (!i) {
229 /* print first line of page */
230 print_line (text, 0, width - 2);
231 wnoutrefresh (text);
232 } else
233 /* Called to update 'end_reached' and 'page' */
234 get_line ();
235 if (!passed_end)
236 page_length++;
237 if (end_reached && !passed_end)
238 passed_end = 1;
239 }
240
241 print_position (dialog, height, width);
242 wmove (dialog, cur_y, cur_x); /* Restore cursor position */
243 wrefresh (dialog);
244 }
245 break;
246 case 'B': /* Previous page */
247 case 'b':
248 case KEY_PPAGE:
249 if (begin_reached)
250 break;
251 back_lines (page_length + height - 4);
252 print_page (text, height - 4, width - 2);
253 print_position (dialog, height, width);
254 wmove (dialog, cur_y, cur_x);
255 wrefresh (dialog);
256 break;
257 case 'J': /* Next line */
258 case 'j':
259 case KEY_DOWN:
260 if (!end_reached) {
261 begin_reached = 0;
262 scrollok (text, TRUE);
263 scroll (text); /* Scroll text region up one line */
264 scrollok (text, FALSE);
265 print_line (text, height - 5, width - 2);
266 wnoutrefresh (text);
267 print_position (dialog, height, width);
268 wmove (dialog, cur_y, cur_x); /* Restore cursor position */
269 wrefresh (dialog);
270 }
271 break;
272 case KEY_NPAGE: /* Next page */
273 case ' ':
274 if (end_reached)
275 break;
276
277 begin_reached = 0;
278 print_page (text, height - 4, width - 2);
279 print_position (dialog, height, width);
280 wmove (dialog, cur_y, cur_x);
281 wrefresh (dialog);
282 break;
283 case '0': /* Beginning of line */
284 case 'H': /* Scroll left */
285 case 'h':
286 case KEY_LEFT:
287 if (hscroll <= 0)
288 break;
289
290 if (key == '0')
291 hscroll = 0;
292 else
293 hscroll--;
294 /* Reprint current page to scroll horizontally */
295 back_lines (page_length);
296 print_page (text, height - 4, width - 2);
297 wmove (dialog, cur_y, cur_x);
298 wrefresh (dialog);
299 break;
300 case 'L': /* Scroll right */
301 case 'l':
302 case KEY_RIGHT:
303 if (hscroll >= MAX_LEN)
304 break;
305 hscroll++;
306 /* Reprint current page to scroll horizontally */
307 back_lines (page_length);
308 print_page (text, height - 4, width - 2);
309 wmove (dialog, cur_y, cur_x);
310 wrefresh (dialog);
311 break;
312 case ESC:
313 break;
314 }
315 }
316
317 delwin (dialog);
318 free (buf);
319 close (fd);
320 return 1; /* ESC pressed */
321}
322
323/*
324 * Go back 'n' lines in text file. Called by dialog_textbox().
325 * 'page' will be updated to point to the desired line in 'buf'.
326 */
327static void
328back_lines (int n)
329{
330 int i, fpos;
331
332 begin_reached = 0;
333 /* We have to distinguish between end_reached and !end_reached
334 since at end of file, the line is not ended by a '\n'.
335 The code inside 'if' basically does a '--page' to move one
336 character backward so as to skip '\n' of the previous line */
337 if (!end_reached) {
338 /* Either beginning of buffer or beginning of file reached? */
339 if (page == buf) {
340 if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
341 endwin ();
342 fprintf (stderr, "\nError moving file pointer in "
343 "back_lines().\n");
344 exit (-1);
345 }
346 if (fpos > bytes_read) { /* Not beginning of file yet */
347 /* We've reached beginning of buffer, but not beginning of
348 file yet, so read previous part of file into buffer.
349 Note that we only move backward for BUF_SIZE/2 bytes,
350 but not BUF_SIZE bytes to avoid re-reading again in
351 print_page() later */
352 /* Really possible to move backward BUF_SIZE/2 bytes? */
353 if (fpos < BUF_SIZE / 2 + bytes_read) {
354 /* No, move less then */
355 if (lseek (fd, 0, SEEK_SET) == -1) {
356 endwin ();
357 fprintf (stderr, "\nError moving file pointer in "
358 "back_lines().\n");
359 exit (-1);
360 }
361 page = buf + fpos - bytes_read;
362 } else { /* Move backward BUF_SIZE/2 bytes */
363 if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR)
364 == -1) {
365 endwin ();
366 fprintf (stderr, "\nError moving file pointer "
367 "in back_lines().\n");
368 exit (-1);
369 }
370 page = buf + BUF_SIZE / 2;
371 }
372 if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
373 endwin ();
374 fprintf (stderr, "\nError reading file in back_lines().\n");
375 exit (-1);
376 }
377 buf[bytes_read] = '\0';
378 } else { /* Beginning of file reached */
379 begin_reached = 1;
380 return;
381 }
382 }
383 if (*(--page) != '\n') { /* '--page' here */
384 /* Something's wrong... */
385 endwin ();
386 fprintf (stderr, "\nInternal error in back_lines().\n");
387 exit (-1);
388 }
389 }
390 /* Go back 'n' lines */
391 for (i = 0; i < n; i++)
392 do {
393 if (page == buf) {
394 if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
395 endwin ();
396 fprintf (stderr,
397 "\nError moving file pointer in back_lines().\n");
398 exit (-1);
399 }
400 if (fpos > bytes_read) {
401 /* Really possible to move backward BUF_SIZE/2 bytes? */
402 if (fpos < BUF_SIZE / 2 + bytes_read) {
403 /* No, move less then */
404 if (lseek (fd, 0, SEEK_SET) == -1) {
405 endwin ();
406 fprintf (stderr, "\nError moving file pointer "
407 "in back_lines().\n");
408 exit (-1);
409 }
410 page = buf + fpos - bytes_read;
411 } else { /* Move backward BUF_SIZE/2 bytes */
412 if (lseek (fd, -(BUF_SIZE / 2 + bytes_read),
413 SEEK_CUR) == -1) {
414 endwin ();
415 fprintf (stderr, "\nError moving file pointer"
416 " in back_lines().\n");
417 exit (-1);
418 }
419 page = buf + BUF_SIZE / 2;
420 }
421 if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
422 endwin ();
423 fprintf (stderr, "\nError reading file in "
424 "back_lines().\n");
425 exit (-1);
426 }
427 buf[bytes_read] = '\0';
428 } else { /* Beginning of file reached */
429 begin_reached = 1;
430 return;
431 }
432 }
433 } while (*(--page) != '\n');
434 page++;
435}
436
437/*
438 * Print a new page of text. Called by dialog_textbox().
439 */
440static void
441print_page (WINDOW * win, int height, int width)
442{
443 int i, passed_end = 0;
444
445 page_length = 0;
446 for (i = 0; i < height; i++) {
447 print_line (win, i, width);
448 if (!passed_end)
449 page_length++;
450 if (end_reached && !passed_end)
451 passed_end = 1;
452 }
453 wnoutrefresh (win);
454}
455
456/*
457 * Print a new line of text. Called by dialog_textbox() and print_page().
458 */
459static void
460print_line (WINDOW * win, int row, int width)
461{
462 int y, x;
463 char *line;
464
465 line = get_line ();
466 line += MIN (strlen (line), hscroll); /* Scroll horizontally */
467 wmove (win, row, 0); /* move cursor to correct line */
468 waddch (win, ' ');
469 waddnstr (win, line, MIN (strlen (line), width - 2));
470
471 getyx (win, y, x);
472 /* Clear 'residue' of previous line */
473#if OLD_NCURSES
474 {
475 int i;
476 for (i = 0; i < width - x; i++)
477 waddch (win, ' ');
478 }
479#else
480 wclrtoeol(win);
481#endif
482}
483
484/*
485 * Return current line of text. Called by dialog_textbox() and print_line().
486 * 'page' should point to start of current line before calling, and will be
487 * updated to point to start of next line.
488 */
489static char *
490get_line (void)
491{
492 int i = 0, fpos;
493 static char line[MAX_LEN + 1];
494
495 end_reached = 0;
496 while (*page != '\n') {
497 if (*page == '\0') {
498 /* Either end of file or end of buffer reached */
499 if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
500 endwin ();
501 fprintf (stderr, "\nError moving file pointer in "
502 "get_line().\n");
503 exit (-1);
504 }
505 if (fpos < file_size) { /* Not end of file yet */
506 /* We've reached end of buffer, but not end of file yet,
507 so read next part of file into buffer */
508 if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
509 endwin ();
510 fprintf (stderr, "\nError reading file in get_line().\n");
511 exit (-1);
512 }
513 buf[bytes_read] = '\0';
514 page = buf;
515 } else {
516 if (!end_reached)
517 end_reached = 1;
518 break;
519 }
520 } else if (i < MAX_LEN)
521 line[i++] = *(page++);
522 else {
523 /* Truncate lines longer than MAX_LEN characters */
524 if (i == MAX_LEN)
525 line[i++] = '\0';
526 page++;
527 }
528 }
529 if (i <= MAX_LEN)
530 line[i] = '\0';
531 if (!end_reached)
532 page++; /* move pass '\n' */
533
534 return line;
535}
536
537/*
538 * Print current position
539 */
540static void
541print_position (WINDOW * win, int height, int width)
542{
543 int fpos, percent;
544
545 if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
546 endwin ();
547 fprintf (stderr, "\nError moving file pointer in print_position().\n");
548 exit (-1);
549 }
550 wattrset (win, position_indicator_attr);
551 wbkgdset (win, position_indicator_attr & A_COLOR);
552 percent = !file_size ?
553 100 : ((fpos - bytes_read + page - buf) * 100) / file_size;
554 wmove (win, height - 3, width - 9);
555 wprintw (win, "(%3d%%)", percent);
556}
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 @@
1/*
2 * util.c
3 *
4 * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
5 * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include "dialog.h"
23
24
25/* use colors by default? */
26bool use_colors = 1;
27
28char *backtitle = NULL;
29
30const char *dialog_result;
31
32/*
33 * Attribute values, default is for mono display
34 */
35chtype attributes[] =
36{
37 A_NORMAL, /* screen_attr */
38 A_NORMAL, /* shadow_attr */
39 A_NORMAL, /* dialog_attr */
40 A_BOLD, /* title_attr */
41 A_NORMAL, /* border_attr */
42 A_REVERSE, /* button_active_attr */
43 A_DIM, /* button_inactive_attr */
44 A_REVERSE, /* button_key_active_attr */
45 A_BOLD, /* button_key_inactive_attr */
46 A_REVERSE, /* button_label_active_attr */
47 A_NORMAL, /* button_label_inactive_attr */
48 A_NORMAL, /* inputbox_attr */
49 A_NORMAL, /* inputbox_border_attr */
50 A_NORMAL, /* searchbox_attr */
51 A_BOLD, /* searchbox_title_attr */
52 A_NORMAL, /* searchbox_border_attr */
53 A_BOLD, /* position_indicator_attr */
54 A_NORMAL, /* menubox_attr */
55 A_NORMAL, /* menubox_border_attr */
56 A_NORMAL, /* item_attr */
57 A_REVERSE, /* item_selected_attr */
58 A_BOLD, /* tag_attr */
59 A_REVERSE, /* tag_selected_attr */
60 A_BOLD, /* tag_key_attr */
61 A_REVERSE, /* tag_key_selected_attr */
62 A_BOLD, /* check_attr */
63 A_REVERSE, /* check_selected_attr */
64 A_BOLD, /* uarrow_attr */
65 A_BOLD /* darrow_attr */
66};
67
68
69#include "colors.h"
70
71/*
72 * Table of color values
73 */
74int color_table[][3] =
75{
76 {SCREEN_FG, SCREEN_BG, SCREEN_HL},
77 {SHADOW_FG, SHADOW_BG, SHADOW_HL},
78 {DIALOG_FG, DIALOG_BG, DIALOG_HL},
79 {TITLE_FG, TITLE_BG, TITLE_HL},
80 {BORDER_FG, BORDER_BG, BORDER_HL},
81 {BUTTON_ACTIVE_FG, BUTTON_ACTIVE_BG, BUTTON_ACTIVE_HL},
82 {BUTTON_INACTIVE_FG, BUTTON_INACTIVE_BG, BUTTON_INACTIVE_HL},
83 {BUTTON_KEY_ACTIVE_FG, BUTTON_KEY_ACTIVE_BG, BUTTON_KEY_ACTIVE_HL},
84 {BUTTON_KEY_INACTIVE_FG, BUTTON_KEY_INACTIVE_BG, BUTTON_KEY_INACTIVE_HL},
85 {BUTTON_LABEL_ACTIVE_FG, BUTTON_LABEL_ACTIVE_BG, BUTTON_LABEL_ACTIVE_HL},
86 {BUTTON_LABEL_INACTIVE_FG, BUTTON_LABEL_INACTIVE_BG,
87 BUTTON_LABEL_INACTIVE_HL},
88 {INPUTBOX_FG, INPUTBOX_BG, INPUTBOX_HL},
89 {INPUTBOX_BORDER_FG, INPUTBOX_BORDER_BG, INPUTBOX_BORDER_HL},
90 {SEARCHBOX_FG, SEARCHBOX_BG, SEARCHBOX_HL},
91 {SEARCHBOX_TITLE_FG, SEARCHBOX_TITLE_BG, SEARCHBOX_TITLE_HL},
92 {SEARCHBOX_BORDER_FG, SEARCHBOX_BORDER_BG, SEARCHBOX_BORDER_HL},
93 {POSITION_INDICATOR_FG, POSITION_INDICATOR_BG, POSITION_INDICATOR_HL},
94 {MENUBOX_FG, MENUBOX_BG, MENUBOX_HL},
95 {MENUBOX_BORDER_FG, MENUBOX_BORDER_BG, MENUBOX_BORDER_HL},
96 {ITEM_FG, ITEM_BG, ITEM_HL},
97 {ITEM_SELECTED_FG, ITEM_SELECTED_BG, ITEM_SELECTED_HL},
98 {TAG_FG, TAG_BG, TAG_HL},
99 {TAG_SELECTED_FG, TAG_SELECTED_BG, TAG_SELECTED_HL},
100 {TAG_KEY_FG, TAG_KEY_BG, TAG_KEY_HL},
101 {TAG_KEY_SELECTED_FG, TAG_KEY_SELECTED_BG, TAG_KEY_SELECTED_HL},
102 {CHECK_FG, CHECK_BG, CHECK_HL},
103 {CHECK_SELECTED_FG, CHECK_SELECTED_BG, CHECK_SELECTED_HL},
104 {UARROW_FG, UARROW_BG, UARROW_HL},
105 {DARROW_FG, DARROW_BG, DARROW_HL},
106}; /* color_table */
107
108/*
109 * Set window to attribute 'attr'
110 */
111void
112attr_clear (WINDOW * win, int height, int width, chtype attr)
113{
114 int i, j;
115
116 wattrset (win, attr);
117 for (i = 0; i < height; i++) {
118 wmove (win, i, 0);
119 for (j = 0; j < width; j++)
120 waddch (win, ' ');
121 }
122 touchwin (win);
123}
124
125void dialog_clear (void)
126{
127 attr_clear (stdscr, LINES, COLS, screen_attr);
128 /* Display background title if it exists ... - SLH */
129 if (backtitle != NULL) {
130 int i;
131
132 wattrset (stdscr, screen_attr);
133 mvwaddstr (stdscr, 0, 1, (char *)backtitle);
134 wmove (stdscr, 1, 1);
135 for (i = 1; i < COLS - 1; i++)
136 waddch (stdscr, ACS_HLINE);
137 }
138 wnoutrefresh (stdscr);
139}
140
141/*
142 * Do some initialization for dialog
143 */
144void
145init_dialog (void)
146{
147 initscr (); /* Init curses */
148 keypad (stdscr, TRUE);
149 cbreak ();
150 noecho ();
151
152
153 if (use_colors) /* Set up colors */
154 color_setup ();
155
156
157 dialog_clear ();
158}
159
160/*
161 * Setup for color display
162 */
163void
164color_setup (void)
165{
166 int i;
167
168 if (has_colors ()) { /* Terminal supports color? */
169 start_color ();
170
171 /* Initialize color pairs */
172 for (i = 0; i < ATTRIBUTE_COUNT; i++)
173 init_pair (i + 1, color_table[i][0], color_table[i][1]);
174
175 /* Setup color attributes */
176 for (i = 0; i < ATTRIBUTE_COUNT; i++)
177 attributes[i] = C_ATTR (color_table[i][2], i + 1);
178 }
179}
180
181/*
182 * End using dialog functions.
183 */
184void
185end_dialog (void)
186{
187 endwin ();
188}
189
190
191/*
192 * Print a string of text in a window, automatically wrap around to the
193 * next line if the string is too long to fit on one line. Newline
194 * characters '\n' are replaced by spaces. We start on a new line
195 * if there is no room for at least 4 nonblanks following a double-space.
196 */
197void
198print_autowrap (WINDOW * win, const char *prompt, int width, int y, int x)
199{
200 int newl, cur_x, cur_y;
201 int i, prompt_len, room, wlen;
202 char tempstr[MAX_LEN + 1], *word, *sp, *sp2;
203
204 strcpy (tempstr, prompt);
205
206 prompt_len = strlen(tempstr);
207
208 /*
209 * Remove newlines
210 */
211 for(i=0; i<prompt_len; i++) {
212 if(tempstr[i] == '\n') tempstr[i] = ' ';
213 }
214
215 if (prompt_len <= width - x * 2) { /* If prompt is short */
216 wmove (win, y, (width - prompt_len) / 2);
217 waddstr (win, tempstr);
218 } else {
219 cur_x = x;
220 cur_y = y;
221 newl = 1;
222 word = tempstr;
223 while (word && *word) {
224 sp = index(word, ' ');
225 if (sp)
226 *sp++ = 0;
227
228 /* Wrap to next line if either the word does not fit,
229 or it is the first word of a new sentence, and it is
230 short, and the next word does not fit. */
231 room = width - cur_x;
232 wlen = strlen(word);
233 if (wlen > room ||
234 (newl && wlen < 4 && sp && wlen+1+strlen(sp) > room
235 && (!(sp2 = index(sp, ' ')) || wlen+1+(sp2-sp) > room))) {
236 cur_y++;
237 cur_x = x;
238 }
239 wmove (win, cur_y, cur_x);
240 waddstr (win, word);
241 getyx (win, cur_y, cur_x);
242 cur_x++;
243 if (sp && *sp == ' ') {
244 cur_x++; /* double space */
245 while (*++sp == ' ');
246 newl = 1;
247 } else
248 newl = 0;
249 word = sp;
250 }
251 }
252}
253
254/*
255 * Print a button
256 */
257void
258print_button (WINDOW * win, const char *label, int y, int x, int selected)
259{
260 int i, temp;
261
262 wmove (win, y, x);
263 wattrset (win, selected ? button_active_attr : button_inactive_attr);
264 waddstr (win, "<");
265 temp = strspn (label, " ");
266 label += temp;
267 wattrset (win, selected ? button_label_active_attr
268 : button_label_inactive_attr);
269 for (i = 0; i < temp; i++)
270 waddch (win, ' ');
271 wattrset (win, selected ? button_key_active_attr
272 : button_key_inactive_attr);
273 waddch (win, label[0]);
274 wattrset (win, selected ? button_label_active_attr
275 : button_label_inactive_attr);
276 waddstr (win, (char *)label + 1);
277 wattrset (win, selected ? button_active_attr : button_inactive_attr);
278 waddstr (win, ">");
279 wmove (win, y, x + temp + 1);
280}
281
282/*
283 * Draw a rectangular box with line drawing characters
284 */
285void
286draw_box (WINDOW * win, int y, int x, int height, int width,
287 chtype box, chtype border)
288{
289 int i, j;
290
291 wattrset (win, 0);
292 for (i = 0; i < height; i++) {
293 wmove (win, y + i, x);
294 for (j = 0; j < width; j++)
295 if (!i && !j)
296 waddch (win, border | ACS_ULCORNER);
297 else if (i == height - 1 && !j)
298 waddch (win, border | ACS_LLCORNER);
299 else if (!i && j == width - 1)
300 waddch (win, box | ACS_URCORNER);
301 else if (i == height - 1 && j == width - 1)
302 waddch (win, box | ACS_LRCORNER);
303 else if (!i)
304 waddch (win, border | ACS_HLINE);
305 else if (i == height - 1)
306 waddch (win, box | ACS_HLINE);
307 else if (!j)
308 waddch (win, border | ACS_VLINE);
309 else if (j == width - 1)
310 waddch (win, box | ACS_VLINE);
311 else
312 waddch (win, box | ' ');
313 }
314}
315
316/*
317 * Draw shadows along the right and bottom edge to give a more 3D look
318 * to the boxes
319 */
320void
321draw_shadow (WINDOW * win, int y, int x, int height, int width)
322{
323 int i;
324
325 if (has_colors ()) { /* Whether terminal supports color? */
326 wattrset (win, shadow_attr);
327 wmove (win, y + height, x + 2);
328 for (i = 0; i < width; i++)
329 waddch (win, winch (win) & A_CHARTEXT);
330 for (i = y + 1; i < y + height + 1; i++) {
331 wmove (win, i, x + width);
332 waddch (win, winch (win) & A_CHARTEXT);
333 waddch (win, winch (win) & A_CHARTEXT);
334 }
335 wnoutrefresh (win);
336 }
337}
338
339/*
340 * Return the position of the first alphabetic character in a string.
341 */
342int
343first_alpha(const char *string, const char *exempt)
344{
345 int i, in_paren=0, c;
346
347 for (i = 0; i < strlen(string); i++) {
348 c = tolower(string[i]);
349
350 if (strchr("<[(", c)) ++in_paren;
351 if (strchr(">])", c) && in_paren > 0) --in_paren;
352
353 if ((! in_paren) && isalpha(c) &&
354 strchr(exempt, c) == 0)
355 return i;
356 }
357
358 return 0;
359}
360
361/*
362 * Get the first selected item in the dialog_list_item list.
363 */
364struct dialog_list_item *
365first_sel_item(int item_no, struct dialog_list_item ** items)
366{
367 int i;
368
369 for (i = 0; i < item_no; i++) {
370 if (items[i]->selected)
371 return items[i];
372 }
373
374 return NULL;
375}
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 @@
1/*
2 * yesno.c -- implements the yes/no box
3 *
4 * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
5 * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include "dialog.h"
23
24/*
25 * Display termination buttons
26 */
27static void
28print_buttons(WINDOW *dialog, int height, int width, int selected)
29{
30 int x = width / 2 - 10;
31 int y = height - 2;
32
33 print_button (dialog, " Yes ", y, x, selected == 0);
34 print_button (dialog, " No ", y, x + 13, selected == 1);
35
36 wmove(dialog, y, x+1 + 13*selected );
37 wrefresh (dialog);
38}
39
40/*
41 * Display a dialog box with two buttons - Yes and No
42 */
43int
44dialog_yesno (const char *title, const char *prompt, int height, int width)
45{
46 int i, x, y, key = 0, button = 0;
47 WINDOW *dialog;
48
49 /* center dialog box on screen */
50 x = (COLS - width) / 2;
51 y = (LINES - height) / 2;
52
53 draw_shadow (stdscr, y, x, height, width);
54
55 dialog = newwin (height, width, y, x);
56 keypad (dialog, TRUE);
57
58 draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
59 wattrset (dialog, border_attr);
60 mvwaddch (dialog, height-3, 0, ACS_LTEE);
61 for (i = 0; i < width - 2; i++)
62 waddch (dialog, ACS_HLINE);
63 wattrset (dialog, dialog_attr);
64 waddch (dialog, ACS_RTEE);
65
66 if (title != NULL && strlen(title) >= width-2 ) {
67 /* truncate long title -- mec */
68 char * title2 = malloc(width-2+1);
69 memcpy( title2, title, width-2 );
70 title2[width-2] = '\0';
71 title = title2;
72 }
73
74 if (title != NULL) {
75 wattrset (dialog, title_attr);
76 mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
77 waddstr (dialog, (char *)title);
78 waddch (dialog, ' ');
79 }
80
81 wattrset (dialog, dialog_attr);
82 print_autowrap (dialog, prompt, width - 2, 1, 3);
83
84 print_buttons(dialog, height, width, 0);
85
86 while (key != ESC) {
87 key = wgetch (dialog);
88 switch (key) {
89 case 'Y':
90 case 'y':
91 delwin (dialog);
92 return 0;
93 case 'N':
94 case 'n':
95 delwin (dialog);
96 return 1;
97
98 case TAB:
99 case KEY_LEFT:
100 case KEY_RIGHT:
101 button = ((key == KEY_LEFT ? --button : ++button) < 0)
102 ? 1 : (button > 1 ? 0 : button);
103
104 print_buttons(dialog, height, width, button);
105 wrefresh (dialog);
106 break;
107 case ' ':
108 case '\n':
109 delwin (dialog);
110 return button;
111 case ESC:
112 break;
113 }
114 }
115
116 delwin (dialog);
117 return -1; /* ESC pressed */
118}
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 @@
1%option backup nostdinit noyywrap never-interactive full ecs
2%option 8bit backup nodefault perf-report perf-report
3%x COMMAND HELP STRING PARAM
4%{
5/*
6 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
7 * Released under the terms of the GNU GPL v2.0.
8 */
9
10#include <limits.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <unistd.h>
15
16#define LKC_DIRECT_LINK
17#include "lkc.h"
18
19#define START_STRSIZE 16
20
21char *text;
22static char *text_ptr;
23static int text_size, text_asize;
24
25struct buffer {
26 struct buffer *parent;
27 YY_BUFFER_STATE state;
28};
29
30struct buffer *current_buf;
31
32static int last_ts, first_ts;
33
34static void zconf_endhelp(void);
35static struct buffer *zconf_endfile(void);
36
37void new_string(void)
38{
39 text = malloc(START_STRSIZE);
40 text_asize = START_STRSIZE;
41 text_ptr = text;
42 text_size = 0;
43 *text_ptr = 0;
44}
45
46void append_string(const char *str, int size)
47{
48 int new_size = text_size + size + 1;
49 if (new_size > text_asize) {
50 text = realloc(text, new_size);
51 text_asize = new_size;
52 text_ptr = text + text_size;
53 }
54 memcpy(text_ptr, str, size);
55 text_ptr += size;
56 text_size += size;
57 *text_ptr = 0;
58}
59
60void alloc_string(const char *str, int size)
61{
62 text = malloc(size + 1);
63 memcpy(text, str, size);
64 text[size] = 0;
65}
66%}
67
68ws [ \n\t]
69n [A-Za-z0-9_]
70
71%%
72 int str = 0;
73 int ts, i;
74
75[ \t]*#.*\n current_file->lineno++;
76[ \t]*#.*
77
78[ \t]*\n current_file->lineno++; return T_EOL;
79
80[ \t]+ {
81 BEGIN(COMMAND);
82}
83
84. {
85 unput(yytext[0]);
86 BEGIN(COMMAND);
87}
88
89
90<COMMAND>{
91 "mainmenu" BEGIN(PARAM); return T_MAINMENU;
92 "menu" BEGIN(PARAM); return T_MENU;
93 "endmenu" BEGIN(PARAM); return T_ENDMENU;
94 "source" BEGIN(PARAM); return T_SOURCE;
95 "choice" BEGIN(PARAM); return T_CHOICE;
96 "endchoice" BEGIN(PARAM); return T_ENDCHOICE;
97 "comment" BEGIN(PARAM); return T_COMMENT;
98 "config" BEGIN(PARAM); return T_CONFIG;
99 "menuconfig" BEGIN(PARAM); return T_MENUCONFIG;
100 "help" BEGIN(PARAM); return T_HELP;
101 "if" BEGIN(PARAM); return T_IF;
102 "endif" BEGIN(PARAM); return T_ENDIF;
103 "depends" BEGIN(PARAM); return T_DEPENDS;
104 "requires" BEGIN(PARAM); return T_REQUIRES;
105 "optional" BEGIN(PARAM); return T_OPTIONAL;
106 "default" BEGIN(PARAM); return T_DEFAULT;
107 "prompt" BEGIN(PARAM); return T_PROMPT;
108 "tristate" BEGIN(PARAM); return T_TRISTATE;
109 "def_tristate" BEGIN(PARAM); return T_DEF_TRISTATE;
110 "bool" BEGIN(PARAM); return T_BOOLEAN;
111 "boolean" BEGIN(PARAM); return T_BOOLEAN;
112 "def_bool" BEGIN(PARAM); return T_DEF_BOOLEAN;
113 "def_boolean" BEGIN(PARAM); return T_DEF_BOOLEAN;
114 "int" BEGIN(PARAM); return T_INT;
115 "hex" BEGIN(PARAM); return T_HEX;
116 "string" BEGIN(PARAM); return T_STRING;
117 "select" BEGIN(PARAM); return T_SELECT;
118 "enable" BEGIN(PARAM); return T_SELECT;
119 "range" BEGIN(PARAM); return T_RANGE;
120 {n}+ {
121 alloc_string(yytext, yyleng);
122 zconflval.string = text;
123 return T_WORD;
124 }
125 .
126 \n current_file->lineno++; BEGIN(INITIAL);
127}
128
129<PARAM>{
130 "&&" return T_AND;
131 "||" return T_OR;
132 "(" return T_OPEN_PAREN;
133 ")" return T_CLOSE_PAREN;
134 "!" return T_NOT;
135 "=" return T_EQUAL;
136 "!=" return T_UNEQUAL;
137 "if" return T_IF;
138 "on" return T_ON;
139 \"|\' {
140 str = yytext[0];
141 new_string();
142 BEGIN(STRING);
143 }
144 \n BEGIN(INITIAL); current_file->lineno++; return T_EOL;
145 --- /* ignore */
146 ({n}|[-/.])+ {
147 alloc_string(yytext, yyleng);
148 zconflval.string = text;
149 return T_WORD;
150 }
151 #.* /* comment */
152 \\\n current_file->lineno++;
153 .
154 <<EOF>> {
155 BEGIN(INITIAL);
156 }
157}
158
159<STRING>{
160 [^'"\\\n]+/\n {
161 append_string(yytext, yyleng);
162 zconflval.string = text;
163 return T_WORD_QUOTE;
164 }
165 [^'"\\\n]+ {
166 append_string(yytext, yyleng);
167 }
168 \\.?/\n {
169 append_string(yytext + 1, yyleng - 1);
170 zconflval.string = text;
171 return T_WORD_QUOTE;
172 }
173 \\.? {
174 append_string(yytext + 1, yyleng - 1);
175 }
176 \'|\" {
177 if (str == yytext[0]) {
178 BEGIN(PARAM);
179 zconflval.string = text;
180 return T_WORD_QUOTE;
181 } else
182 append_string(yytext, 1);
183 }
184 \n {
185 printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno());
186 current_file->lineno++;
187 BEGIN(INITIAL);
188 return T_EOL;
189 }
190 <<EOF>> {
191 BEGIN(INITIAL);
192 }
193}
194
195<HELP>{
196 [ \t]+ {
197 ts = 0;
198 for (i = 0; i < yyleng; i++) {
199 if (yytext[i] == '\t')
200 ts = (ts & ~7) + 8;
201 else
202 ts++;
203 }
204 last_ts = ts;
205 if (first_ts) {
206 if (ts < first_ts) {
207 zconf_endhelp();
208 return T_HELPTEXT;
209 }
210 ts -= first_ts;
211 while (ts > 8) {
212 append_string(" ", 8);
213 ts -= 8;
214 }
215 append_string(" ", ts);
216 }
217 }
218 [ \t]*\n/[^ \t\n] {
219 current_file->lineno++;
220 zconf_endhelp();
221 return T_HELPTEXT;
222 }
223 [ \t]*\n {
224 current_file->lineno++;
225 append_string("\n", 1);
226 }
227 [^ \t\n].* {
228 append_string(yytext, yyleng);
229 if (!first_ts)
230 first_ts = last_ts;
231 }
232 <<EOF>> {
233 zconf_endhelp();
234 return T_HELPTEXT;
235 }
236}
237
238<<EOF>> {
239 if (current_buf) {
240 zconf_endfile();
241 return T_EOF;
242 }
243 fclose(yyin);
244 yyterminate();
245}
246
247%%
248void zconf_starthelp(void)
249{
250 new_string();
251 last_ts = first_ts = 0;
252 BEGIN(HELP);
253}
254
255static void zconf_endhelp(void)
256{
257 zconflval.string = text;
258 BEGIN(INITIAL);
259}
260
261
262/*
263 * Try to open specified file with following names:
264 * ./name
265 * $(srctree)/name
266 * The latter is used when srctree is separate from objtree
267 * when compiling the kernel.
268 * Return NULL if file is not found.
269 */
270FILE *zconf_fopen(const char *name)
271{
272 char *env, fullname[PATH_MAX+1];
273 FILE *f;
274
275 f = fopen(name, "r");
276 if (!f && name[0] != '/') {
277 env = getenv(SRCTREE);
278 if (env) {
279 sprintf(fullname, "%s/%s", env, name);
280 f = fopen(fullname, "r");
281 }
282 }
283 return f;
284}
285
286void zconf_initscan(const char *name)
287{
288 yyin = zconf_fopen(name);
289 if (!yyin) {
290 printf("can't find file %s\n", name);
291 exit(1);
292 }
293
294 current_buf = malloc(sizeof(*current_buf));
295 memset(current_buf, 0, sizeof(*current_buf));
296
297 current_file = file_lookup(name);
298 current_file->lineno = 1;
299 current_file->flags = FILE_BUSY;
300}
301
302void zconf_nextfile(const char *name)
303{
304 struct file *file = file_lookup(name);
305 struct buffer *buf = malloc(sizeof(*buf));
306 memset(buf, 0, sizeof(*buf));
307
308 current_buf->state = YY_CURRENT_BUFFER;
309 yyin = zconf_fopen(name);
310 if (!yyin) {
311 printf("%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), name);
312 exit(1);
313 }
314 yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
315 buf->parent = current_buf;
316 current_buf = buf;
317
318 if (file->flags & FILE_BUSY) {
319 printf("recursive scan (%s)?\n", name);
320 exit(1);
321 }
322 if (file->flags & FILE_SCANNED) {
323 printf("file %s already scanned?\n", name);
324 exit(1);
325 }
326 file->flags |= FILE_BUSY;
327 file->lineno = 1;
328 file->parent = current_file;
329 current_file = file;
330}
331
332static struct buffer *zconf_endfile(void)
333{
334 struct buffer *parent;
335
336 current_file->flags |= FILE_SCANNED;
337 current_file->flags &= ~FILE_BUSY;
338 current_file = current_file->parent;
339
340 parent = current_buf->parent;
341 if (parent) {
342 fclose(yyin);
343 yy_delete_buffer(YY_CURRENT_BUFFER);
344 yy_switch_to_buffer(parent->state);
345 }
346 free(current_buf);
347 current_buf = parent;
348
349 return parent;
350}
351
352int zconf_lineno(void)
353{
354 if (current_buf)
355 return current_file->lineno - 1;
356 else
357 return 0;
358}
359
360char *zconf_curname(void)
361{
362 if (current_buf)
363 return current_file->name;
364 else
365 return "<none>";
366}
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 @@
1/* A Bison parser, made by GNU Bison 1.875a. */
2
3/* Skeleton parser for Yacc-like parsing with Bison,
4 Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21/* As a special exception, when this file is copied by Bison into a
22 Bison output file, you may use that output file without restriction.
23 This special exception was added by the Free Software Foundation
24 in version 1.24 of Bison. */
25
26/* Written by Richard Stallman by simplifying the original so called
27 ``semantic'' parser. */
28
29/* All symbols defined below should begin with yy or YY, to avoid
30 infringing on user name space. This should be done even for local
31 variables, as they might otherwise be expanded by user macros.
32 There are some unavoidable exceptions within include files to
33 define necessary library symbols; they are noted "INFRINGES ON
34 USER NAME SPACE" below. */
35
36/* Identify Bison output. */
37#define YYBISON 1
38
39/* Skeleton name. */
40#define YYSKELETON_NAME "yacc.c"
41
42/* Pure parsers. */
43#define YYPURE 0
44
45/* Using locations. */
46#define YYLSP_NEEDED 0
47
48/* If NAME_PREFIX is specified substitute the variables and functions
49 names. */
50#define yyparse zconfparse
51#define yylex zconflex
52#define yyerror zconferror
53#define yylval zconflval
54#define yychar zconfchar
55#define yydebug zconfdebug
56#define yynerrs zconfnerrs
57
58
59/* Tokens. */
60#ifndef YYTOKENTYPE
61# define YYTOKENTYPE
62 /* Put the tokens into the symbol table, so that GDB and other debuggers
63 know about them. */
64 enum yytokentype {
65 T_MAINMENU = 258,
66 T_MENU = 259,
67 T_ENDMENU = 260,
68 T_SOURCE = 261,
69 T_CHOICE = 262,
70 T_ENDCHOICE = 263,
71 T_COMMENT = 264,
72 T_CONFIG = 265,
73 T_MENUCONFIG = 266,
74 T_HELP = 267,
75 T_HELPTEXT = 268,
76 T_IF = 269,
77 T_ENDIF = 270,
78 T_DEPENDS = 271,
79 T_REQUIRES = 272,
80 T_OPTIONAL = 273,
81 T_PROMPT = 274,
82 T_DEFAULT = 275,
83 T_TRISTATE = 276,
84 T_DEF_TRISTATE = 277,
85 T_BOOLEAN = 278,
86 T_DEF_BOOLEAN = 279,
87 T_STRING = 280,
88 T_INT = 281,
89 T_HEX = 282,
90 T_WORD = 283,
91 T_WORD_QUOTE = 284,
92 T_UNEQUAL = 285,
93 T_EOF = 286,
94 T_EOL = 287,
95 T_CLOSE_PAREN = 288,
96 T_OPEN_PAREN = 289,
97 T_ON = 290,
98 T_SELECT = 291,
99 T_RANGE = 292,
100 T_OR = 293,
101 T_AND = 294,
102 T_EQUAL = 295,
103 T_NOT = 296
104 };
105#endif
106#define T_MAINMENU 258
107#define T_MENU 259
108#define T_ENDMENU 260
109#define T_SOURCE 261
110#define T_CHOICE 262
111#define T_ENDCHOICE 263
112#define T_COMMENT 264
113#define T_CONFIG 265
114#define T_MENUCONFIG 266
115#define T_HELP 267
116#define T_HELPTEXT 268
117#define T_IF 269
118#define T_ENDIF 270
119#define T_DEPENDS 271
120#define T_REQUIRES 272
121#define T_OPTIONAL 273
122#define T_PROMPT 274
123#define T_DEFAULT 275
124#define T_TRISTATE 276
125#define T_DEF_TRISTATE 277
126#define T_BOOLEAN 278
127#define T_DEF_BOOLEAN 279
128#define T_STRING 280
129#define T_INT 281
130#define T_HEX 282
131#define T_WORD 283
132#define T_WORD_QUOTE 284
133#define T_UNEQUAL 285
134#define T_EOF 286
135#define T_EOL 287
136#define T_CLOSE_PAREN 288
137#define T_OPEN_PAREN 289
138#define T_ON 290
139#define T_SELECT 291
140#define T_RANGE 292
141#define T_OR 293
142#define T_AND 294
143#define T_EQUAL 295
144#define T_NOT 296
145
146
147
148
149/* Copy the first part of user declarations. */
150
151
152/*
153 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
154 * Released under the terms of the GNU GPL v2.0.
155 */
156
157#include <ctype.h>
158#include <stdarg.h>
159#include <stdio.h>
160#include <stdlib.h>
161#include <string.h>
162#include <stdbool.h>
163
164#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
165
166#define PRINTD 0x0001
167#define DEBUG_PARSE 0x0002
168
169int cdebug = PRINTD;
170
171extern int zconflex(void);
172static void zconfprint(const char *err, ...);
173static void zconferror(const char *err);
174static bool zconf_endtoken(int token, int starttoken, int endtoken);
175
176struct symbol *symbol_hash[257];
177
178#define YYERROR_VERBOSE
179
180
181/* Enabling traces. */
182#ifndef YYDEBUG
183# define YYDEBUG 0
184#endif
185
186/* Enabling verbose error messages. */
187#ifdef YYERROR_VERBOSE
188# undef YYERROR_VERBOSE
189# define YYERROR_VERBOSE 1
190#else
191# define YYERROR_VERBOSE 0
192#endif
193
194#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
195
196typedef union YYSTYPE {
197 int token;
198 char *string;
199 struct symbol *symbol;
200 struct expr *expr;
201 struct menu *menu;
202} YYSTYPE;
203/* Line 191 of yacc.c. */
204
205# define yystype YYSTYPE /* obsolescent; will be withdrawn */
206# define YYSTYPE_IS_DECLARED 1
207# define YYSTYPE_IS_TRIVIAL 1
208#endif
209
210
211
212/* Copy the second part of user declarations. */
213
214
215#define LKC_DIRECT_LINK
216#include "lkc.h"
217
218
219/* Line 214 of yacc.c. */
220
221
222#if ! defined (yyoverflow) || YYERROR_VERBOSE
223
224/* The parser invokes alloca or malloc; define the necessary symbols. */
225
226# if YYSTACK_USE_ALLOCA
227# define YYSTACK_ALLOC alloca
228# else
229# ifndef YYSTACK_USE_ALLOCA
230# if defined (alloca) || defined (_ALLOCA_H)
231# define YYSTACK_ALLOC alloca
232# else
233# ifdef __GNUC__
234# define YYSTACK_ALLOC __builtin_alloca
235# endif
236# endif
237# endif
238# endif
239
240# ifdef YYSTACK_ALLOC
241 /* Pacify GCC's `empty if-body' warning. */
242# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
243# else
244# if defined (__STDC__) || defined (__cplusplus)
245# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
246# define YYSIZE_T size_t
247# endif
248# define YYSTACK_ALLOC malloc
249# define YYSTACK_FREE free
250# endif
251#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
252
253
254#if (! defined (yyoverflow) \
255 && (! defined (__cplusplus) \
256 || (YYSTYPE_IS_TRIVIAL)))
257
258/* A type that is properly aligned for any stack member. */
259union yyalloc
260{
261 short yyss;
262 YYSTYPE yyvs;
263 };
264
265/* The size of the maximum gap between one aligned stack and the next. */
266# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
267
268/* The size of an array large to enough to hold all stacks, each with
269 N elements. */
270# define YYSTACK_BYTES(N) \
271 ((N) * (sizeof (short) + sizeof (YYSTYPE)) \
272 + YYSTACK_GAP_MAXIMUM)
273
274/* Copy COUNT objects from FROM to TO. The source and destination do
275 not overlap. */
276# ifndef YYCOPY
277# if 1 < __GNUC__
278# define YYCOPY(To, From, Count) \
279 __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
280# else
281# define YYCOPY(To, From, Count) \
282 do \
283 { \
284 register YYSIZE_T yyi; \
285 for (yyi = 0; yyi < (Count); yyi++) \
286 (To)[yyi] = (From)[yyi]; \
287 } \
288 while (0)
289# endif
290# endif
291
292/* Relocate STACK from its old location to the new one. The
293 local variables YYSIZE and YYSTACKSIZE give the old and new number of
294 elements in the stack, and YYPTR gives the new location of the
295 stack. Advance YYPTR to a properly aligned location for the next
296 stack. */
297# define YYSTACK_RELOCATE(Stack) \
298 do \
299 { \
300 YYSIZE_T yynewbytes; \
301 YYCOPY (&yyptr->Stack, Stack, yysize); \
302 Stack = &yyptr->Stack; \
303 yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
304 yyptr += yynewbytes / sizeof (*yyptr); \
305 } \
306 while (0)
307
308#endif
309
310#if defined (__STDC__) || defined (__cplusplus)
311 typedef signed char yysigned_char;
312#else
313 typedef short yysigned_char;
314#endif
315
316/* YYFINAL -- State number of the termination state. */
317#define YYFINAL 2
318/* YYLAST -- Last index in YYTABLE. */
319#define YYLAST 201
320
321/* YYNTOKENS -- Number of terminals. */
322#define YYNTOKENS 42
323/* YYNNTS -- Number of nonterminals. */
324#define YYNNTS 41
325/* YYNRULES -- Number of rules. */
326#define YYNRULES 104
327/* YYNRULES -- Number of states. */
328#define YYNSTATES 182
329
330/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
331#define YYUNDEFTOK 2
332#define YYMAXUTOK 296
333
334#define YYTRANSLATE(YYX) \
335 ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
336
337/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
338static const unsigned char yytranslate[] =
339{
340 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
341 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
342 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
343 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
344 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
345 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
346 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
347 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
348 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
349 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
350 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
351 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
352 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
353 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
354 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
355 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
356 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
357 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
358 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
359 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
360 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
361 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
362 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
363 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
364 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
365 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
366 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
367 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
368 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
369 35, 36, 37, 38, 39, 40, 41
370};
371
372#if YYDEBUG
373/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
374 YYRHS. */
375static const unsigned short yyprhs[] =
376{
377 0, 0, 3, 4, 7, 9, 11, 13, 17, 19,
378 21, 23, 26, 28, 30, 32, 34, 36, 38, 42,
379 45, 49, 52, 53, 56, 59, 62, 65, 69, 74,
380 78, 83, 87, 91, 95, 100, 105, 110, 116, 119,
381 122, 124, 128, 131, 132, 135, 138, 141, 144, 149,
382 153, 157, 160, 165, 166, 169, 173, 175, 179, 182,
383 183, 186, 189, 192, 196, 199, 201, 205, 208, 209,
384 212, 215, 218, 222, 226, 228, 232, 235, 238, 241,
385 242, 245, 248, 253, 257, 261, 262, 265, 267, 269,
386 272, 275, 278, 280, 282, 283, 286, 288, 292, 296,
387 300, 303, 307, 311, 313
388};
389
390/* YYRHS -- A `-1'-separated list of the rules' RHS. */
391static const yysigned_char yyrhs[] =
392{
393 43, 0, -1, -1, 43, 44, -1, 45, -1, 55,
394 -1, 66, -1, 3, 77, 79, -1, 5, -1, 15,
395 -1, 8, -1, 1, 79, -1, 61, -1, 71, -1,
396 47, -1, 49, -1, 69, -1, 79, -1, 10, 28,
397 32, -1, 46, 50, -1, 11, 28, 32, -1, 48,
398 50, -1, -1, 50, 51, -1, 50, 75, -1, 50,
399 73, -1, 50, 32, -1, 21, 76, 32, -1, 22,
400 81, 80, 32, -1, 23, 76, 32, -1, 24, 81,
401 80, 32, -1, 26, 76, 32, -1, 27, 76, 32,
402 -1, 25, 76, 32, -1, 19, 77, 80, 32, -1,
403 20, 81, 80, 32, -1, 36, 28, 80, 32, -1,
404 37, 82, 82, 80, 32, -1, 7, 32, -1, 52,
405 56, -1, 78, -1, 53, 58, 54, -1, 53, 58,
406 -1, -1, 56, 57, -1, 56, 75, -1, 56, 73,
407 -1, 56, 32, -1, 19, 77, 80, 32, -1, 21,
408 76, 32, -1, 23, 76, 32, -1, 18, 32, -1,
409 20, 28, 80, 32, -1, -1, 58, 45, -1, 14,
410 81, 32, -1, 78, -1, 59, 62, 60, -1, 59,
411 62, -1, -1, 62, 45, -1, 62, 66, -1, 62,
412 55, -1, 4, 77, 32, -1, 63, 74, -1, 78,
413 -1, 64, 67, 65, -1, 64, 67, -1, -1, 67,
414 45, -1, 67, 66, -1, 67, 55, -1, 67, 1,
415 32, -1, 6, 77, 32, -1, 68, -1, 9, 77,
416 32, -1, 70, 74, -1, 12, 32, -1, 72, 13,
417 -1, -1, 74, 75, -1, 74, 32, -1, 16, 35,
418 81, 32, -1, 16, 81, 32, -1, 17, 81, 32,
419 -1, -1, 77, 80, -1, 28, -1, 29, -1, 5,
420 79, -1, 8, 79, -1, 15, 79, -1, 32, -1,
421 31, -1, -1, 14, 81, -1, 82, -1, 82, 40,
422 82, -1, 82, 30, 82, -1, 34, 81, 33, -1,
423 41, 81, -1, 81, 38, 81, -1, 81, 39, 81,
424 -1, 28, -1, 29, -1
425};
426
427/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
428static const unsigned short yyrline[] =
429{
430 0, 94, 94, 95, 98, 99, 100, 101, 102, 103,
431 104, 105, 109, 110, 111, 112, 113, 114, 120, 128,
432 134, 142, 152, 154, 155, 156, 157, 160, 166, 173,
433 179, 186, 192, 198, 204, 210, 216, 222, 230, 239,
434 245, 254, 255, 261, 263, 264, 265, 266, 269, 275,
435 281, 287, 293, 299, 301, 306, 315, 324, 325, 331,
436 333, 334, 335, 340, 347, 353, 362, 363, 369, 371,
437 372, 373, 374, 377, 383, 390, 397, 404, 410, 417,
438 418, 419, 422, 427, 432, 440, 442, 447, 448, 451,
439 452, 453, 457, 457, 459, 460, 463, 464, 465, 466,
440 467, 468, 469, 472, 473
441};
442#endif
443
444#if YYDEBUG || YYERROR_VERBOSE
445/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
446 First, the terminals, then, starting at YYNTOKENS, nonterminals. */
447static const char *const yytname[] =
448{
449 "$end", "error", "$undefined", "T_MAINMENU", "T_MENU", "T_ENDMENU",
450 "T_SOURCE", "T_CHOICE", "T_ENDCHOICE", "T_COMMENT", "T_CONFIG",
451 "T_MENUCONFIG", "T_HELP", "T_HELPTEXT", "T_IF", "T_ENDIF", "T_DEPENDS",
452 "T_REQUIRES", "T_OPTIONAL", "T_PROMPT", "T_DEFAULT", "T_TRISTATE",
453 "T_DEF_TRISTATE", "T_BOOLEAN", "T_DEF_BOOLEAN", "T_STRING", "T_INT",
454 "T_HEX", "T_WORD", "T_WORD_QUOTE", "T_UNEQUAL", "T_EOF", "T_EOL",
455 "T_CLOSE_PAREN", "T_OPEN_PAREN", "T_ON", "T_SELECT", "T_RANGE", "T_OR",
456 "T_AND", "T_EQUAL", "T_NOT", "$accept", "input", "block",
457 "common_block", "config_entry_start", "config_stmt",
458 "menuconfig_entry_start", "menuconfig_stmt", "config_option_list",
459 "config_option", "choice", "choice_entry", "choice_end", "choice_stmt",
460 "choice_option_list", "choice_option", "choice_block", "if", "if_end",
461 "if_stmt", "if_block", "menu", "menu_entry", "menu_end", "menu_stmt",
462 "menu_block", "source", "source_stmt", "comment", "comment_stmt",
463 "help_start", "help", "depends_list", "depends", "prompt_stmt_opt",
464 "prompt", "end", "nl_or_eof", "if_expr", "expr", "symbol", 0
465};
466#endif
467
468# ifdef YYPRINT
469/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
470 token YYLEX-NUM. */
471static const unsigned short yytoknum[] =
472{
473 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
474 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
475 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
476 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
477 295, 296
478};
479# endif
480
481/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
482static const unsigned char yyr1[] =
483{
484 0, 42, 43, 43, 44, 44, 44, 44, 44, 44,
485 44, 44, 45, 45, 45, 45, 45, 45, 46, 47,
486 48, 49, 50, 50, 50, 50, 50, 51, 51, 51,
487 51, 51, 51, 51, 51, 51, 51, 51, 52, 53,
488 54, 55, 55, 56, 56, 56, 56, 56, 57, 57,
489 57, 57, 57, 58, 58, 59, 60, 61, 61, 62,
490 62, 62, 62, 63, 64, 65, 66, 66, 67, 67,
491 67, 67, 67, 68, 69, 70, 71, 72, 73, 74,
492 74, 74, 75, 75, 75, 76, 76, 77, 77, 78,
493 78, 78, 79, 79, 80, 80, 81, 81, 81, 81,
494 81, 81, 81, 82, 82
495};
496
497/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
498static const unsigned char yyr2[] =
499{
500 0, 2, 0, 2, 1, 1, 1, 3, 1, 1,
501 1, 2, 1, 1, 1, 1, 1, 1, 3, 2,
502 3, 2, 0, 2, 2, 2, 2, 3, 4, 3,
503 4, 3, 3, 3, 4, 4, 4, 5, 2, 2,
504 1, 3, 2, 0, 2, 2, 2, 2, 4, 3,
505 3, 2, 4, 0, 2, 3, 1, 3, 2, 0,
506 2, 2, 2, 3, 2, 1, 3, 2, 0, 2,
507 2, 2, 3, 3, 1, 3, 2, 2, 2, 0,
508 2, 2, 4, 3, 3, 0, 2, 1, 1, 2,
509 2, 2, 1, 1, 0, 2, 1, 3, 3, 3,
510 2, 3, 3, 1, 1
511};
512
513/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
514 STATE-NUM when YYTABLE doesn't specify something else to do. Zero
515 means the default is an error. */
516static const unsigned char yydefact[] =
517{
518 2, 0, 1, 0, 0, 0, 8, 0, 0, 10,
519 0, 0, 0, 0, 9, 93, 92, 3, 4, 22,
520 14, 22, 15, 43, 53, 5, 59, 12, 79, 68,
521 6, 74, 16, 79, 13, 17, 11, 87, 88, 0,
522 0, 0, 38, 0, 0, 0, 103, 104, 0, 0,
523 0, 96, 19, 21, 39, 42, 58, 64, 0, 76,
524 7, 63, 73, 75, 18, 20, 0, 100, 55, 0,
525 0, 0, 0, 0, 0, 0, 0, 0, 85, 0,
526 85, 0, 85, 85, 85, 26, 0, 0, 23, 0,
527 25, 24, 0, 0, 0, 85, 85, 47, 44, 46,
528 45, 0, 0, 0, 54, 41, 40, 60, 62, 57,
529 61, 56, 81, 80, 0, 69, 71, 66, 70, 65,
530 99, 101, 102, 98, 97, 77, 0, 0, 0, 94,
531 94, 0, 94, 94, 0, 94, 0, 0, 0, 94,
532 0, 78, 51, 94, 94, 0, 0, 89, 90, 91,
533 72, 0, 83, 84, 0, 0, 0, 27, 86, 0,
534 29, 0, 33, 31, 32, 0, 94, 0, 0, 49,
535 50, 82, 95, 34, 35, 28, 30, 36, 0, 48,
536 52, 37
537};
538
539/* YYDEFGOTO[NTERM-NUM]. */
540static const short yydefgoto[] =
541{
542 -1, 1, 17, 18, 19, 20, 21, 22, 52, 88,
543 23, 24, 105, 25, 54, 98, 55, 26, 109, 27,
544 56, 28, 29, 117, 30, 58, 31, 32, 33, 34,
545 89, 90, 57, 91, 131, 132, 106, 35, 155, 50,
546 51
547};
548
549/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
550 STATE-NUM. */
551#define YYPACT_NINF -99
552static const short yypact[] =
553{
554 -99, 48, -99, 38, 46, 46, -99, 46, -29, -99,
555 46, -17, -3, -11, -99, -99, -99, -99, -99, -99,
556 -99, -99, -99, -99, -99, -99, -99, -99, -99, -99,
557 -99, -99, -99, -99, -99, -99, -99, -99, -99, 38,
558 12, 15, -99, 18, 51, 62, -99, -99, -11, -11,
559 4, -24, 138, 138, 160, 121, 110, -4, 81, -4,
560 -99, -99, -99, -99, -99, -99, -19, -99, -99, -11,
561 -11, 70, 70, 73, 32, -11, 46, -11, 46, -11,
562 46, -11, 46, 46, 46, -99, 36, 70, -99, 95,
563 -99, -99, 96, 46, 106, 46, 46, -99, -99, -99,
564 -99, 38, 38, 38, -99, -99, -99, -99, -99, -99,
565 -99, -99, -99, -99, 112, -99, -99, -99, -99, -99,
566 -99, 117, -99, -99, -99, -99, -11, 33, 65, 131,
567 1, 119, 131, 1, 136, 1, 153, 154, 155, 131,
568 70, -99, -99, 131, 131, 156, 157, -99, -99, -99,
569 -99, 101, -99, -99, -11, 158, 159, -99, -99, 161,
570 -99, 162, -99, -99, -99, 163, 131, 164, 165, -99,
571 -99, -99, 99, -99, -99, -99, -99, -99, 166, -99,
572 -99, -99
573};
574
575/* YYPGOTO[NTERM-NUM]. */
576static const short yypgoto[] =
577{
578 -99, -99, -99, 111, -99, -99, -99, -99, 178, -99,
579 -99, -99, -99, 91, -99, -99, -99, -99, -99, -99,
580 -99, -99, -99, -99, 115, -99, -99, -99, -99, -99,
581 -99, 146, 168, 89, 27, 0, 126, -1, -98, -48,
582 -63
583};
584
585/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
586 positive, shift that token. If negative, reduce the rule which
587 number is the opposite. If zero, do what YYDEFACT says.
588 If YYTABLE_NINF, syntax error. */
589#define YYTABLE_NINF -68
590static const short yytable[] =
591{
592 66, 67, 36, 42, 39, 40, 71, 41, 123, 124,
593 43, 44, 74, 75, 120, 154, 72, 46, 47, 69,
594 70, 121, 122, 48, 140, 45, 127, 128, 112, 130,
595 49, 133, 156, 135, 158, 159, 68, 161, 60, 69,
596 70, 165, 69, 70, 61, 167, 168, 62, 2, 3,
597 63, 4, 5, 6, 7, 8, 9, 10, 11, 12,
598 46, 47, 13, 14, 139, 152, 48, 126, 178, 15,
599 16, 69, 70, 49, 37, 38, 129, 166, 151, 15,
600 16, -67, 114, 64, -67, 5, 101, 7, 8, 102,
601 10, 11, 12, 143, 65, 13, 103, 153, 46, 47,
602 147, 148, 149, 69, 70, 125, 172, 134, 141, 136,
603 137, 138, 15, 16, 5, 101, 7, 8, 102, 10,
604 11, 12, 145, 146, 13, 103, 101, 7, 142, 102,
605 10, 11, 12, 171, 144, 13, 103, 69, 70, 69,
606 70, 15, 16, 100, 150, 154, 113, 108, 113, 116,
607 73, 157, 15, 16, 74, 75, 70, 76, 77, 78,
608 79, 80, 81, 82, 83, 84, 104, 107, 160, 115,
609 85, 110, 73, 118, 86, 87, 74, 75, 92, 93,
610 94, 95, 111, 96, 119, 162, 163, 164, 169, 170,
611 173, 174, 97, 175, 176, 177, 179, 180, 181, 53,
612 99, 59
613};
614
615static const unsigned char yycheck[] =
616{
617 48, 49, 3, 32, 4, 5, 30, 7, 71, 72,
618 10, 28, 16, 17, 33, 14, 40, 28, 29, 38,
619 39, 69, 70, 34, 87, 28, 74, 75, 32, 77,
620 41, 79, 130, 81, 132, 133, 32, 135, 39, 38,
621 39, 139, 38, 39, 32, 143, 144, 32, 0, 1,
622 32, 3, 4, 5, 6, 7, 8, 9, 10, 11,
623 28, 29, 14, 15, 28, 32, 34, 35, 166, 31,
624 32, 38, 39, 41, 28, 29, 76, 140, 126, 31,
625 32, 0, 1, 32, 3, 4, 5, 6, 7, 8,
626 9, 10, 11, 93, 32, 14, 15, 32, 28, 29,
627 101, 102, 103, 38, 39, 32, 154, 80, 13, 82,
628 83, 84, 31, 32, 4, 5, 6, 7, 8, 9,
629 10, 11, 95, 96, 14, 15, 5, 6, 32, 8,
630 9, 10, 11, 32, 28, 14, 15, 38, 39, 38,
631 39, 31, 32, 54, 32, 14, 57, 56, 59, 58,
632 12, 32, 31, 32, 16, 17, 39, 19, 20, 21,
633 22, 23, 24, 25, 26, 27, 55, 56, 32, 58,
634 32, 56, 12, 58, 36, 37, 16, 17, 18, 19,
635 20, 21, 56, 23, 58, 32, 32, 32, 32, 32,
636 32, 32, 32, 32, 32, 32, 32, 32, 32, 21,
637 54, 33
638};
639
640/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
641 symbol of state STATE-NUM. */
642static const unsigned char yystos[] =
643{
644 0, 43, 0, 1, 3, 4, 5, 6, 7, 8,
645 9, 10, 11, 14, 15, 31, 32, 44, 45, 46,
646 47, 48, 49, 52, 53, 55, 59, 61, 63, 64,
647 66, 68, 69, 70, 71, 79, 79, 28, 29, 77,
648 77, 77, 32, 77, 28, 28, 28, 29, 34, 41,
649 81, 82, 50, 50, 56, 58, 62, 74, 67, 74,
650 79, 32, 32, 32, 32, 32, 81, 81, 32, 38,
651 39, 30, 40, 12, 16, 17, 19, 20, 21, 22,
652 23, 24, 25, 26, 27, 32, 36, 37, 51, 72,
653 73, 75, 18, 19, 20, 21, 23, 32, 57, 73,
654 75, 5, 8, 15, 45, 54, 78, 45, 55, 60,
655 66, 78, 32, 75, 1, 45, 55, 65, 66, 78,
656 33, 81, 81, 82, 82, 32, 35, 81, 81, 77,
657 81, 76, 77, 81, 76, 81, 76, 76, 76, 28,
658 82, 13, 32, 77, 28, 76, 76, 79, 79, 79,
659 32, 81, 32, 32, 14, 80, 80, 32, 80, 80,
660 32, 80, 32, 32, 32, 80, 82, 80, 80, 32,
661 32, 32, 81, 32, 32, 32, 32, 32, 80, 32,
662 32, 32
663};
664
665#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
666# define YYSIZE_T __SIZE_TYPE__
667#endif
668#if ! defined (YYSIZE_T) && defined (size_t)
669# define YYSIZE_T size_t
670#endif
671#if ! defined (YYSIZE_T)
672# if defined (__STDC__) || defined (__cplusplus)
673# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
674# define YYSIZE_T size_t
675# endif
676#endif
677#if ! defined (YYSIZE_T)
678# define YYSIZE_T unsigned int
679#endif
680
681#define yyerrok (yyerrstatus = 0)
682#define yyclearin (yychar = YYEMPTY)
683#define YYEMPTY (-2)
684#define YYEOF 0
685
686#define YYACCEPT goto yyacceptlab
687#define YYABORT goto yyabortlab
688#define YYERROR goto yyerrlab1
689
690
691/* Like YYERROR except do call yyerror. This remains here temporarily
692 to ease the transition to the new meaning of YYERROR, for GCC.
693 Once GCC version 2 has supplanted version 1, this can go. */
694
695#define YYFAIL goto yyerrlab
696
697#define YYRECOVERING() (!!yyerrstatus)
698
699#define YYBACKUP(Token, Value) \
700do \
701 if (yychar == YYEMPTY && yylen == 1) \
702 { \
703 yychar = (Token); \
704 yylval = (Value); \
705 yytoken = YYTRANSLATE (yychar); \
706 YYPOPSTACK; \
707 goto yybackup; \
708 } \
709 else \
710 { \
711 yyerror ("syntax error: cannot back up");\
712 YYERROR; \
713 } \
714while (0)
715
716#define YYTERROR 1
717#define YYERRCODE 256
718
719/* YYLLOC_DEFAULT -- Compute the default location (before the actions
720 are run). */
721
722#ifndef YYLLOC_DEFAULT
723# define YYLLOC_DEFAULT(Current, Rhs, N) \
724 Current.first_line = Rhs[1].first_line; \
725 Current.first_column = Rhs[1].first_column; \
726 Current.last_line = Rhs[N].last_line; \
727 Current.last_column = Rhs[N].last_column;
728#endif
729
730/* YYLEX -- calling `yylex' with the right arguments. */
731
732#ifdef YYLEX_PARAM
733# define YYLEX yylex (YYLEX_PARAM)
734#else
735# define YYLEX yylex ()
736#endif
737
738/* Enable debugging if requested. */
739#if YYDEBUG
740
741# ifndef YYFPRINTF
742# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
743# define YYFPRINTF fprintf
744# endif
745
746# define YYDPRINTF(Args) \
747do { \
748 if (yydebug) \
749 YYFPRINTF Args; \
750} while (0)
751
752# define YYDSYMPRINT(Args) \
753do { \
754 if (yydebug) \
755 yysymprint Args; \
756} while (0)
757
758# define YYDSYMPRINTF(Title, Token, Value, Location) \
759do { \
760 if (yydebug) \
761 { \
762 YYFPRINTF (stderr, "%s ", Title); \
763 yysymprint (stderr, \
764 Token, Value); \
765 YYFPRINTF (stderr, "\n"); \
766 } \
767} while (0)
768
769/*------------------------------------------------------------------.
770| yy_stack_print -- Print the state stack from its BOTTOM up to its |
771| TOP (cinluded). |
772`------------------------------------------------------------------*/
773
774#if defined (__STDC__) || defined (__cplusplus)
775static void
776yy_stack_print (short *bottom, short *top)
777#else
778static void
779yy_stack_print (bottom, top)
780 short *bottom;
781 short *top;
782#endif
783{
784 YYFPRINTF (stderr, "Stack now");
785 for (/* Nothing. */; bottom <= top; ++bottom)
786 YYFPRINTF (stderr, " %d", *bottom);
787 YYFPRINTF (stderr, "\n");
788}
789
790# define YY_STACK_PRINT(Bottom, Top) \
791do { \
792 if (yydebug) \
793 yy_stack_print ((Bottom), (Top)); \
794} while (0)
795
796
797/*------------------------------------------------.
798| Report that the YYRULE is going to be reduced. |
799`------------------------------------------------*/
800
801#if defined (__STDC__) || defined (__cplusplus)
802static void
803yy_reduce_print (int yyrule)
804#else
805static void
806yy_reduce_print (yyrule)
807 int yyrule;
808#endif
809{
810 int yyi;
811 unsigned int yylineno = yyrline[yyrule];
812 YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
813 yyrule - 1, yylineno);
814 /* Print the symbols being reduced, and their result. */
815 for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
816 YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
817 YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]);
818}
819
820# define YY_REDUCE_PRINT(Rule) \
821do { \
822 if (yydebug) \
823 yy_reduce_print (Rule); \
824} while (0)
825
826/* Nonzero means print parse trace. It is left uninitialized so that
827 multiple parsers can coexist. */
828int yydebug;
829#else /* !YYDEBUG */
830# define YYDPRINTF(Args)
831# define YYDSYMPRINT(Args)
832# define YYDSYMPRINTF(Title, Token, Value, Location)
833# define YY_STACK_PRINT(Bottom, Top)
834# define YY_REDUCE_PRINT(Rule)
835#endif /* !YYDEBUG */
836
837
838/* YYINITDEPTH -- initial size of the parser's stacks. */
839#ifndef YYINITDEPTH
840# define YYINITDEPTH 200
841#endif
842
843/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
844 if the built-in stack extension method is used).
845
846 Do not make this value too large; the results are undefined if
847 SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
848 evaluated with infinite-precision integer arithmetic. */
849
850#if YYMAXDEPTH == 0
851# undef YYMAXDEPTH
852#endif
853
854#ifndef YYMAXDEPTH
855# define YYMAXDEPTH 10000
856#endif
857
858
859
860#if YYERROR_VERBOSE
861
862# ifndef yystrlen
863# if defined (__GLIBC__) && defined (_STRING_H)
864# define yystrlen strlen
865# else
866/* Return the length of YYSTR. */
867static YYSIZE_T
868# if defined (__STDC__) || defined (__cplusplus)
869yystrlen (const char *yystr)
870# else
871yystrlen (yystr)
872 const char *yystr;
873# endif
874{
875 register const char *yys = yystr;
876
877 while (*yys++ != '\0')
878 continue;
879
880 return yys - yystr - 1;
881}
882# endif
883# endif
884
885# ifndef yystpcpy
886# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
887# define yystpcpy stpcpy
888# else
889/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
890 YYDEST. */
891static char *
892# if defined (__STDC__) || defined (__cplusplus)
893yystpcpy (char *yydest, const char *yysrc)
894# else
895yystpcpy (yydest, yysrc)
896 char *yydest;
897 const char *yysrc;
898# endif
899{
900 register char *yyd = yydest;
901 register const char *yys = yysrc;
902
903 while ((*yyd++ = *yys++) != '\0')
904 continue;
905
906 return yyd - 1;
907}
908# endif
909# endif
910
911#endif /* !YYERROR_VERBOSE */
912
913
914
915#if YYDEBUG
916/*--------------------------------.
917| Print this symbol on YYOUTPUT. |
918`--------------------------------*/
919
920#if defined (__STDC__) || defined (__cplusplus)
921static void
922yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
923#else
924static void
925yysymprint (yyoutput, yytype, yyvaluep)
926 FILE *yyoutput;
927 int yytype;
928 YYSTYPE *yyvaluep;
929#endif
930{
931 /* Pacify ``unused variable'' warnings. */
932 (void) yyvaluep;
933
934 if (yytype < YYNTOKENS)
935 {
936 YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
937# ifdef YYPRINT
938 YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
939# endif
940 }
941 else
942 YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
943
944 switch (yytype)
945 {
946 default:
947 break;
948 }
949 YYFPRINTF (yyoutput, ")");
950}
951
952#endif /* ! YYDEBUG */
953/*-----------------------------------------------.
954| Release the memory associated to this symbol. |
955`-----------------------------------------------*/
956
957#if defined (__STDC__) || defined (__cplusplus)
958static void
959yydestruct (int yytype, YYSTYPE *yyvaluep)
960#else
961static void
962yydestruct (yytype, yyvaluep)
963 int yytype;
964 YYSTYPE *yyvaluep;
965#endif
966{
967 /* Pacify ``unused variable'' warnings. */
968 (void) yyvaluep;
969
970 switch (yytype)
971 {
972
973 default:
974 break;
975 }
976}
977
978
979/* Prevent warnings from -Wmissing-prototypes. */
980
981#ifdef YYPARSE_PARAM
982# if defined (__STDC__) || defined (__cplusplus)
983int yyparse (void *YYPARSE_PARAM);
984# else
985int yyparse ();
986# endif
987#else /* ! YYPARSE_PARAM */
988#if defined (__STDC__) || defined (__cplusplus)
989int yyparse (void);
990#else
991int yyparse ();
992#endif
993#endif /* ! YYPARSE_PARAM */
994
995
996
997/* The lookahead symbol. */
998int yychar;
999
1000/* The semantic value of the lookahead symbol. */
1001YYSTYPE yylval;
1002
1003/* Number of syntax errors so far. */
1004int yynerrs;
1005
1006
1007
1008/*----------.
1009| yyparse. |
1010`----------*/
1011
1012#ifdef YYPARSE_PARAM
1013# if defined (__STDC__) || defined (__cplusplus)
1014int yyparse (void *YYPARSE_PARAM)
1015# else
1016int yyparse (YYPARSE_PARAM)
1017 void *YYPARSE_PARAM;
1018# endif
1019#else /* ! YYPARSE_PARAM */
1020#if defined (__STDC__) || defined (__cplusplus)
1021int
1022yyparse (void)
1023#else
1024int
1025yyparse ()
1026
1027#endif
1028#endif
1029{
1030
1031 register int yystate;
1032 register int yyn;
1033 int yyresult;
1034 /* Number of tokens to shift before error messages enabled. */
1035 int yyerrstatus;
1036 /* Lookahead token as an internal (translated) token number. */
1037 int yytoken = 0;
1038
1039 /* Three stacks and their tools:
1040 `yyss': related to states,
1041 `yyvs': related to semantic values,
1042 `yyls': related to locations.
1043
1044 Refer to the stacks thru separate pointers, to allow yyoverflow
1045 to reallocate them elsewhere. */
1046
1047 /* The state stack. */
1048 short yyssa[YYINITDEPTH];
1049 short *yyss = yyssa;
1050 register short *yyssp;
1051
1052 /* The semantic value stack. */
1053 YYSTYPE yyvsa[YYINITDEPTH];
1054 YYSTYPE *yyvs = yyvsa;
1055 register YYSTYPE *yyvsp;
1056
1057
1058
1059#define YYPOPSTACK (yyvsp--, yyssp--)
1060
1061 YYSIZE_T yystacksize = YYINITDEPTH;
1062
1063 /* The variables used to return semantic value and location from the
1064 action routines. */
1065 YYSTYPE yyval;
1066
1067
1068 /* When reducing, the number of symbols on the RHS of the reduced
1069 rule. */
1070 int yylen;
1071
1072 YYDPRINTF ((stderr, "Starting parse\n"));
1073
1074 yystate = 0;
1075 yyerrstatus = 0;
1076 yynerrs = 0;
1077 yychar = YYEMPTY; /* Cause a token to be read. */
1078
1079 /* Initialize stack pointers.
1080 Waste one element of value and location stack
1081 so that they stay on the same level as the state stack.
1082 The wasted elements are never initialized. */
1083
1084 yyssp = yyss;
1085 yyvsp = yyvs;
1086
1087 goto yysetstate;
1088
1089/*------------------------------------------------------------.
1090| yynewstate -- Push a new state, which is found in yystate. |
1091`------------------------------------------------------------*/
1092 yynewstate:
1093 /* In all cases, when you get here, the value and location stacks
1094 have just been pushed. so pushing a state here evens the stacks.
1095 */
1096 yyssp++;
1097
1098 yysetstate:
1099 *yyssp = yystate;
1100
1101 if (yyss + yystacksize - 1 <= yyssp)
1102 {
1103 /* Get the current used size of the three stacks, in elements. */
1104 YYSIZE_T yysize = yyssp - yyss + 1;
1105
1106#ifdef yyoverflow
1107 {
1108 /* Give user a chance to reallocate the stack. Use copies of
1109 these so that the &'s don't force the real ones into
1110 memory. */
1111 YYSTYPE *yyvs1 = yyvs;
1112 short *yyss1 = yyss;
1113
1114
1115 /* Each stack pointer address is followed by the size of the
1116 data in use in that stack, in bytes. This used to be a
1117 conditional around just the two extra args, but that might
1118 be undefined if yyoverflow is a macro. */
1119 yyoverflow ("parser stack overflow",
1120 &yyss1, yysize * sizeof (*yyssp),
1121 &yyvs1, yysize * sizeof (*yyvsp),
1122
1123 &yystacksize);
1124
1125 yyss = yyss1;
1126 yyvs = yyvs1;
1127 }
1128#else /* no yyoverflow */
1129# ifndef YYSTACK_RELOCATE
1130 goto yyoverflowlab;
1131# else
1132 /* Extend the stack our own way. */
1133 if (YYMAXDEPTH <= yystacksize)
1134 goto yyoverflowlab;
1135 yystacksize *= 2;
1136 if (YYMAXDEPTH < yystacksize)
1137 yystacksize = YYMAXDEPTH;
1138
1139 {
1140 short *yyss1 = yyss;
1141 union yyalloc *yyptr =
1142 (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
1143 if (! yyptr)
1144 goto yyoverflowlab;
1145 YYSTACK_RELOCATE (yyss);
1146 YYSTACK_RELOCATE (yyvs);
1147
1148# undef YYSTACK_RELOCATE
1149 if (yyss1 != yyssa)
1150 YYSTACK_FREE (yyss1);
1151 }
1152# endif
1153#endif /* no yyoverflow */
1154
1155 yyssp = yyss + yysize - 1;
1156 yyvsp = yyvs + yysize - 1;
1157
1158
1159 YYDPRINTF ((stderr, "Stack size increased to %lu\n",
1160 (unsigned long int) yystacksize));
1161
1162 if (yyss + yystacksize - 1 <= yyssp)
1163 YYABORT;
1164 }
1165
1166 YYDPRINTF ((stderr, "Entering state %d\n", yystate));
1167
1168 goto yybackup;
1169
1170/*-----------.
1171| yybackup. |
1172`-----------*/
1173yybackup:
1174
1175/* Do appropriate processing given the current state. */
1176/* Read a lookahead token if we need one and don't already have one. */
1177/* yyresume: */
1178
1179 /* First try to decide what to do without reference to lookahead token. */
1180
1181 yyn = yypact[yystate];
1182 if (yyn == YYPACT_NINF)
1183 goto yydefault;
1184
1185 /* Not known => get a lookahead token if don't already have one. */
1186
1187 /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
1188 if (yychar == YYEMPTY)
1189 {
1190 YYDPRINTF ((stderr, "Reading a token: "));
1191 yychar = YYLEX;
1192 }
1193
1194 if (yychar <= YYEOF)
1195 {
1196 yychar = yytoken = YYEOF;
1197 YYDPRINTF ((stderr, "Now at end of input.\n"));
1198 }
1199 else
1200 {
1201 yytoken = YYTRANSLATE (yychar);
1202 YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc);
1203 }
1204
1205 /* If the proper action on seeing token YYTOKEN is to reduce or to
1206 detect an error, take that action. */
1207 yyn += yytoken;
1208 if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
1209 goto yydefault;
1210 yyn = yytable[yyn];
1211 if (yyn <= 0)
1212 {
1213 if (yyn == 0 || yyn == YYTABLE_NINF)
1214 goto yyerrlab;
1215 yyn = -yyn;
1216 goto yyreduce;
1217 }
1218
1219 if (yyn == YYFINAL)
1220 YYACCEPT;
1221
1222 /* Shift the lookahead token. */
1223 YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken]));
1224
1225 /* Discard the token being shifted unless it is eof. */
1226 if (yychar != YYEOF)
1227 yychar = YYEMPTY;
1228
1229 *++yyvsp = yylval;
1230
1231
1232 /* Count tokens shifted since error; after three, turn off error
1233 status. */
1234 if (yyerrstatus)
1235 yyerrstatus--;
1236
1237 yystate = yyn;
1238 goto yynewstate;
1239
1240
1241/*-----------------------------------------------------------.
1242| yydefault -- do the default action for the current state. |
1243`-----------------------------------------------------------*/
1244yydefault:
1245 yyn = yydefact[yystate];
1246 if (yyn == 0)
1247 goto yyerrlab;
1248 goto yyreduce;
1249
1250
1251/*-----------------------------.
1252| yyreduce -- Do a reduction. |
1253`-----------------------------*/
1254yyreduce:
1255 /* yyn is the number of a rule to reduce with. */
1256 yylen = yyr2[yyn];
1257
1258 /* If YYLEN is nonzero, implement the default value of the action:
1259 `$$ = $1'.
1260
1261 Otherwise, the following line sets YYVAL to garbage.
1262 This behavior is undocumented and Bison
1263 users should not rely upon it. Assigning to YYVAL
1264 unconditionally makes the parser a bit smaller, and it avoids a
1265 GCC warning that YYVAL may be used uninitialized. */
1266 yyval = yyvsp[1-yylen];
1267
1268
1269 YY_REDUCE_PRINT (yyn);
1270 switch (yyn)
1271 {
1272 case 8:
1273
1274 { zconfprint("unexpected 'endmenu' statement"); ;}
1275 break;
1276
1277 case 9:
1278
1279 { zconfprint("unexpected 'endif' statement"); ;}
1280 break;
1281
1282 case 10:
1283
1284 { zconfprint("unexpected 'endchoice' statement"); ;}
1285 break;
1286
1287 case 11:
1288
1289 { zconfprint("syntax error"); yyerrok; ;}
1290 break;
1291
1292 case 18:
1293
1294 {
1295 struct symbol *sym = sym_lookup(yyvsp[-1].string, 0);
1296 sym->flags |= SYMBOL_OPTIONAL;
1297 menu_add_entry(sym);
1298 printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), yyvsp[-1].string);
1299;}
1300 break;
1301
1302 case 19:
1303
1304 {
1305 menu_end_entry();
1306 printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
1307;}
1308 break;
1309
1310 case 20:
1311
1312 {
1313 struct symbol *sym = sym_lookup(yyvsp[-1].string, 0);
1314 sym->flags |= SYMBOL_OPTIONAL;
1315 menu_add_entry(sym);
1316 printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), yyvsp[-1].string);
1317;}
1318 break;
1319
1320 case 21:
1321
1322 {
1323 if (current_entry->prompt)
1324 current_entry->prompt->type = P_MENU;
1325 else
1326 zconfprint("warning: menuconfig statement without prompt");
1327 menu_end_entry();
1328 printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
1329;}
1330 break;
1331
1332 case 27:
1333
1334 {
1335 menu_set_type(S_TRISTATE);
1336 printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno());
1337;}
1338 break;
1339
1340 case 28:
1341
1342 {
1343 menu_add_expr(P_DEFAULT, yyvsp[-2].expr, yyvsp[-1].expr);
1344 menu_set_type(S_TRISTATE);
1345 printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno());
1346;}
1347 break;
1348
1349 case 29:
1350
1351 {
1352 menu_set_type(S_BOOLEAN);
1353 printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno());
1354;}
1355 break;
1356
1357 case 30:
1358
1359 {
1360 menu_add_expr(P_DEFAULT, yyvsp[-2].expr, yyvsp[-1].expr);
1361 menu_set_type(S_BOOLEAN);
1362 printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno());
1363;}
1364 break;
1365
1366 case 31:
1367
1368 {
1369 menu_set_type(S_INT);
1370 printd(DEBUG_PARSE, "%s:%d:int\n", zconf_curname(), zconf_lineno());
1371;}
1372 break;
1373
1374 case 32:
1375
1376 {
1377 menu_set_type(S_HEX);
1378 printd(DEBUG_PARSE, "%s:%d:hex\n", zconf_curname(), zconf_lineno());
1379;}
1380 break;
1381
1382 case 33:
1383
1384 {
1385 menu_set_type(S_STRING);
1386 printd(DEBUG_PARSE, "%s:%d:string\n", zconf_curname(), zconf_lineno());
1387;}
1388 break;
1389
1390 case 34:
1391
1392 {
1393 menu_add_prompt(P_PROMPT, yyvsp[-2].string, yyvsp[-1].expr);
1394 printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
1395;}
1396 break;
1397
1398 case 35:
1399
1400 {
1401 menu_add_expr(P_DEFAULT, yyvsp[-2].expr, yyvsp[-1].expr);
1402 printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno());
1403;}
1404 break;
1405
1406 case 36:
1407
1408 {
1409 menu_add_symbol(P_SELECT, sym_lookup(yyvsp[-2].string, 0), yyvsp[-1].expr);
1410 printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
1411;}
1412 break;
1413
1414 case 37:
1415
1416 {
1417 menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,yyvsp[-3].symbol, yyvsp[-2].symbol), yyvsp[-1].expr);
1418 printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
1419;}
1420 break;
1421
1422 case 38:
1423
1424 {
1425 struct symbol *sym = sym_lookup(NULL, 0);
1426 sym->flags |= SYMBOL_CHOICE;
1427 menu_add_entry(sym);
1428 menu_add_expr(P_CHOICE, NULL, NULL);
1429 printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
1430;}
1431 break;
1432
1433 case 39:
1434
1435 {
1436 menu_end_entry();
1437 menu_add_menu();
1438;}
1439 break;
1440
1441 case 40:
1442
1443 {
1444 if (zconf_endtoken(yyvsp[0].token, T_CHOICE, T_ENDCHOICE)) {
1445 menu_end_menu();
1446 printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
1447 }
1448;}
1449 break;
1450
1451 case 42:
1452
1453 {
1454 printf("%s:%d: missing 'endchoice' for this 'choice' statement\n", current_menu->file->name, current_menu->lineno);
1455 zconfnerrs++;
1456;}
1457 break;
1458
1459 case 48:
1460
1461 {
1462 menu_add_prompt(P_PROMPT, yyvsp[-2].string, yyvsp[-1].expr);
1463 printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
1464;}
1465 break;
1466
1467 case 49:
1468
1469 {
1470 menu_set_type(S_TRISTATE);
1471 printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno());
1472;}
1473 break;
1474
1475 case 50:
1476
1477 {
1478 menu_set_type(S_BOOLEAN);
1479 printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno());
1480;}
1481 break;
1482
1483 case 51:
1484
1485 {
1486 current_entry->sym->flags |= SYMBOL_OPTIONAL;
1487 printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
1488;}
1489 break;
1490
1491 case 52:
1492
1493 {
1494 menu_add_symbol(P_DEFAULT, sym_lookup(yyvsp[-2].string, 0), yyvsp[-1].expr);
1495 printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno());
1496;}
1497 break;
1498
1499 case 55:
1500
1501 {
1502 printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
1503 menu_add_entry(NULL);
1504 menu_add_dep(yyvsp[-1].expr);
1505 menu_end_entry();
1506 menu_add_menu();
1507;}
1508 break;
1509
1510 case 56:
1511
1512 {
1513 if (zconf_endtoken(yyvsp[0].token, T_IF, T_ENDIF)) {
1514 menu_end_menu();
1515 printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
1516 }
1517;}
1518 break;
1519
1520 case 58:
1521
1522 {
1523 printf("%s:%d: missing 'endif' for this 'if' statement\n", current_menu->file->name, current_menu->lineno);
1524 zconfnerrs++;
1525;}
1526 break;
1527
1528 case 63:
1529
1530 {
1531 menu_add_entry(NULL);
1532 menu_add_prop(P_MENU, yyvsp[-1].string, NULL, NULL);
1533 printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
1534;}
1535 break;
1536
1537 case 64:
1538
1539 {
1540 menu_end_entry();
1541 menu_add_menu();
1542;}
1543 break;
1544
1545 case 65:
1546
1547 {
1548 if (zconf_endtoken(yyvsp[0].token, T_MENU, T_ENDMENU)) {
1549 menu_end_menu();
1550 printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
1551 }
1552;}
1553 break;
1554
1555 case 67:
1556
1557 {
1558 printf("%s:%d: missing 'endmenu' for this 'menu' statement\n", current_menu->file->name, current_menu->lineno);
1559 zconfnerrs++;
1560;}
1561 break;
1562
1563 case 72:
1564
1565 { zconfprint("invalid menu option"); yyerrok; ;}
1566 break;
1567
1568 case 73:
1569
1570 {
1571 yyval.string = yyvsp[-1].string;
1572 printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), yyvsp[-1].string);
1573;}
1574 break;
1575
1576 case 74:
1577
1578 {
1579 zconf_nextfile(yyvsp[0].string);
1580;}
1581 break;
1582
1583 case 75:
1584
1585 {
1586 menu_add_entry(NULL);
1587 menu_add_prop(P_COMMENT, yyvsp[-1].string, NULL, NULL);
1588 printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
1589;}
1590 break;
1591
1592 case 76:
1593
1594 {
1595 menu_end_entry();
1596;}
1597 break;
1598
1599 case 77:
1600
1601 {
1602 printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
1603 zconf_starthelp();
1604;}
1605 break;
1606
1607 case 78:
1608
1609 {
1610 current_entry->sym->help = yyvsp[0].string;
1611;}
1612 break;
1613
1614 case 82:
1615
1616 {
1617 menu_add_dep(yyvsp[-1].expr);
1618 printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
1619;}
1620 break;
1621
1622 case 83:
1623
1624 {
1625 menu_add_dep(yyvsp[-1].expr);
1626 printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno());
1627;}
1628 break;
1629
1630 case 84:
1631
1632 {
1633 menu_add_dep(yyvsp[-1].expr);
1634 printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno());
1635;}
1636 break;
1637
1638 case 86:
1639
1640 {
1641 menu_add_prop(P_PROMPT, yyvsp[-1].string, NULL, yyvsp[0].expr);
1642;}
1643 break;
1644
1645 case 89:
1646
1647 { yyval.token = T_ENDMENU; ;}
1648 break;
1649
1650 case 90:
1651
1652 { yyval.token = T_ENDCHOICE; ;}
1653 break;
1654
1655 case 91:
1656
1657 { yyval.token = T_ENDIF; ;}
1658 break;
1659
1660 case 94:
1661
1662 { yyval.expr = NULL; ;}
1663 break;
1664
1665 case 95:
1666
1667 { yyval.expr = yyvsp[0].expr; ;}
1668 break;
1669
1670 case 96:
1671
1672 { yyval.expr = expr_alloc_symbol(yyvsp[0].symbol); ;}
1673 break;
1674
1675 case 97:
1676
1677 { yyval.expr = expr_alloc_comp(E_EQUAL, yyvsp[-2].symbol, yyvsp[0].symbol); ;}
1678 break;
1679
1680 case 98:
1681
1682 { yyval.expr = expr_alloc_comp(E_UNEQUAL, yyvsp[-2].symbol, yyvsp[0].symbol); ;}
1683 break;
1684
1685 case 99:
1686
1687 { yyval.expr = yyvsp[-1].expr; ;}
1688 break;
1689
1690 case 100:
1691
1692 { yyval.expr = expr_alloc_one(E_NOT, yyvsp[0].expr); ;}
1693 break;
1694
1695 case 101:
1696
1697 { yyval.expr = expr_alloc_two(E_OR, yyvsp[-2].expr, yyvsp[0].expr); ;}
1698 break;
1699
1700 case 102:
1701
1702 { yyval.expr = expr_alloc_two(E_AND, yyvsp[-2].expr, yyvsp[0].expr); ;}
1703 break;
1704
1705 case 103:
1706
1707 { yyval.symbol = sym_lookup(yyvsp[0].string, 0); free(yyvsp[0].string); ;}
1708 break;
1709
1710 case 104:
1711
1712 { yyval.symbol = sym_lookup(yyvsp[0].string, 1); free(yyvsp[0].string); ;}
1713 break;
1714
1715
1716 }
1717
1718/* Line 999 of yacc.c. */
1719
1720
1721 yyvsp -= yylen;
1722 yyssp -= yylen;
1723
1724
1725 YY_STACK_PRINT (yyss, yyssp);
1726
1727 *++yyvsp = yyval;
1728
1729
1730 /* Now `shift' the result of the reduction. Determine what state
1731 that goes to, based on the state we popped back to and the rule
1732 number reduced by. */
1733
1734 yyn = yyr1[yyn];
1735
1736 yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
1737 if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
1738 yystate = yytable[yystate];
1739 else
1740 yystate = yydefgoto[yyn - YYNTOKENS];
1741
1742 goto yynewstate;
1743
1744
1745/*------------------------------------.
1746| yyerrlab -- here on detecting error |
1747`------------------------------------*/
1748yyerrlab:
1749 /* If not already recovering from an error, report this error. */
1750 if (!yyerrstatus)
1751 {
1752 ++yynerrs;
1753#if YYERROR_VERBOSE
1754 yyn = yypact[yystate];
1755
1756 if (YYPACT_NINF < yyn && yyn < YYLAST)
1757 {
1758 YYSIZE_T yysize = 0;
1759 int yytype = YYTRANSLATE (yychar);
1760 char *yymsg;
1761 int yyx, yycount;
1762
1763 yycount = 0;
1764 /* Start YYX at -YYN if negative to avoid negative indexes in
1765 YYCHECK. */
1766 for (yyx = yyn < 0 ? -yyn : 0;
1767 yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++)
1768 if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
1769 yysize += yystrlen (yytname[yyx]) + 15, yycount++;
1770 yysize += yystrlen ("syntax error, unexpected ") + 1;
1771 yysize += yystrlen (yytname[yytype]);
1772 yymsg = (char *) YYSTACK_ALLOC (yysize);
1773 if (yymsg != 0)
1774 {
1775 char *yyp = yystpcpy (yymsg, "syntax error, unexpected ");
1776 yyp = yystpcpy (yyp, yytname[yytype]);
1777
1778 if (yycount < 5)
1779 {
1780 yycount = 0;
1781 for (yyx = yyn < 0 ? -yyn : 0;
1782 yyx < (int) (sizeof (yytname) / sizeof (char *));
1783 yyx++)
1784 if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
1785 {
1786 const char *yyq = ! yycount ? ", expecting " : " or ";
1787 yyp = yystpcpy (yyp, yyq);
1788 yyp = yystpcpy (yyp, yytname[yyx]);
1789 yycount++;
1790 }
1791 }
1792 yyerror (yymsg);
1793 YYSTACK_FREE (yymsg);
1794 }
1795 else
1796 yyerror ("syntax error; also virtual memory exhausted");
1797 }
1798 else
1799#endif /* YYERROR_VERBOSE */
1800 yyerror ("syntax error");
1801 }
1802
1803
1804
1805 if (yyerrstatus == 3)
1806 {
1807 /* If just tried and failed to reuse lookahead token after an
1808 error, discard it. */
1809
1810 /* Return failure if at end of input. */
1811 if (yychar == YYEOF)
1812 {
1813 /* Pop the error token. */
1814 YYPOPSTACK;
1815 /* Pop the rest of the stack. */
1816 while (yyss < yyssp)
1817 {
1818 YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
1819 yydestruct (yystos[*yyssp], yyvsp);
1820 YYPOPSTACK;
1821 }
1822 YYABORT;
1823 }
1824
1825 YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc);
1826 yydestruct (yytoken, &yylval);
1827 yychar = YYEMPTY;
1828
1829 }
1830
1831 /* Else will try to reuse lookahead token after shifting the error
1832 token. */
1833 goto yyerrlab1;
1834
1835
1836/*----------------------------------------------------.
1837| yyerrlab1 -- error raised explicitly by an action. |
1838`----------------------------------------------------*/
1839yyerrlab1:
1840 yyerrstatus = 3; /* Each real token shifted decrements this. */
1841
1842 for (;;)
1843 {
1844 yyn = yypact[yystate];
1845 if (yyn != YYPACT_NINF)
1846 {
1847 yyn += YYTERROR;
1848 if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
1849 {
1850 yyn = yytable[yyn];
1851 if (0 < yyn)
1852 break;
1853 }
1854 }
1855
1856 /* Pop the current state because it cannot handle the error token. */
1857 if (yyssp == yyss)
1858 YYABORT;
1859
1860 YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
1861 yydestruct (yystos[yystate], yyvsp);
1862 yyvsp--;
1863 yystate = *--yyssp;
1864
1865 YY_STACK_PRINT (yyss, yyssp);
1866 }
1867
1868 if (yyn == YYFINAL)
1869 YYACCEPT;
1870
1871 YYDPRINTF ((stderr, "Shifting error token, "));
1872
1873 *++yyvsp = yylval;
1874
1875
1876 yystate = yyn;
1877 goto yynewstate;
1878
1879
1880/*-------------------------------------.
1881| yyacceptlab -- YYACCEPT comes here. |
1882`-------------------------------------*/
1883yyacceptlab:
1884 yyresult = 0;
1885 goto yyreturn;
1886
1887/*-----------------------------------.
1888| yyabortlab -- YYABORT comes here. |
1889`-----------------------------------*/
1890yyabortlab:
1891 yyresult = 1;
1892 goto yyreturn;
1893
1894#ifndef yyoverflow
1895/*----------------------------------------------.
1896| yyoverflowlab -- parser overflow comes here. |
1897`----------------------------------------------*/
1898yyoverflowlab:
1899 yyerror ("parser stack overflow");
1900 yyresult = 2;
1901 /* Fall through. */
1902#endif
1903
1904yyreturn:
1905#ifndef yyoverflow
1906 if (yyss != yyssa)
1907 YYSTACK_FREE (yyss);
1908#endif
1909 return yyresult;
1910}
1911
1912
1913
1914
1915
1916void conf_parse(const char *name)
1917{
1918 struct symbol *sym;
1919 int i;
1920
1921 zconf_initscan(name);
1922
1923 sym_init();
1924 menu_init();
1925 modules_sym = sym_lookup("MODULES", 0);
1926 rootmenu.prompt = menu_add_prop(P_MENU, "BusyBox Configuration", NULL, NULL);
1927
1928 //zconfdebug = 1;
1929 zconfparse();
1930 if (zconfnerrs)
1931 exit(1);
1932 menu_finalize(&rootmenu);
1933 for_all_symbols(i, sym) {
1934 if (!(sym->flags & SYMBOL_CHECKED) && sym_check_deps(sym))
1935 printf("\n");
1936 else
1937 sym->flags |= SYMBOL_CHECK_DONE;
1938 }
1939
1940 sym_change_count = 1;
1941}
1942
1943const char *zconf_tokenname(int token)
1944{
1945 switch (token) {
1946 case T_MENU: return "menu";
1947 case T_ENDMENU: return "endmenu";
1948 case T_CHOICE: return "choice";
1949 case T_ENDCHOICE: return "endchoice";
1950 case T_IF: return "if";
1951 case T_ENDIF: return "endif";
1952 }
1953 return "<token>";
1954}
1955
1956static bool zconf_endtoken(int token, int starttoken, int endtoken)
1957{
1958 if (token != endtoken) {
1959 zconfprint("unexpected '%s' within %s block", zconf_tokenname(token), zconf_tokenname(starttoken));
1960 zconfnerrs++;
1961 return false;
1962 }
1963 if (current_menu->file != current_file) {
1964 zconfprint("'%s' in different file than '%s'", zconf_tokenname(token), zconf_tokenname(starttoken));
1965 zconfprint("location of the '%s'", zconf_tokenname(starttoken));
1966 zconfnerrs++;
1967 return false;
1968 }
1969 return true;
1970}
1971
1972static void zconfprint(const char *err, ...)
1973{
1974 va_list ap;
1975
1976 fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno() + 1);
1977 va_start(ap, err);
1978 vfprintf(stderr, err, ap);
1979 va_end(ap);
1980 fprintf(stderr, "\n");
1981}
1982
1983static void zconferror(const char *err)
1984{
1985 fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err);
1986}
1987
1988void print_quoted_string(FILE *out, const char *str)
1989{
1990 const char *p;
1991 int len;
1992
1993 putc('"', out);
1994 while ((p = strchr(str, '"'))) {
1995 len = p - str;
1996 if (len)
1997 fprintf(out, "%.*s", len, str);
1998 fputs("\\\"", out);
1999 str = p + 1;
2000 }
2001 fputs(str, out);
2002 putc('"', out);
2003}
2004
2005void print_symbol(FILE *out, struct menu *menu)
2006{
2007 struct symbol *sym = menu->sym;
2008 struct property *prop;
2009
2010 if (sym_is_choice(sym))
2011 fprintf(out, "choice\n");
2012 else
2013 fprintf(out, "config %s\n", sym->name);
2014 switch (sym->type) {
2015 case S_BOOLEAN:
2016 fputs(" boolean\n", out);
2017 break;
2018 case S_TRISTATE:
2019 fputs(" tristate\n", out);
2020 break;
2021 case S_STRING:
2022 fputs(" string\n", out);
2023 break;
2024 case S_INT:
2025 fputs(" integer\n", out);
2026 break;
2027 case S_HEX:
2028 fputs(" hex\n", out);
2029 break;
2030 default:
2031 fputs(" ???\n", out);
2032 break;
2033 }
2034 for (prop = sym->prop; prop; prop = prop->next) {
2035 if (prop->menu != menu)
2036 continue;
2037 switch (prop->type) {
2038 case P_PROMPT:
2039 fputs(" prompt ", out);
2040 print_quoted_string(out, prop->text);
2041 if (!expr_is_yes(prop->visible.expr)) {
2042 fputs(" if ", out);
2043 expr_fprint(prop->visible.expr, out);
2044 }
2045 fputc('\n', out);
2046 break;
2047 case P_DEFAULT:
2048 fputs( " default ", out);
2049 expr_fprint(prop->expr, out);
2050 if (!expr_is_yes(prop->visible.expr)) {
2051 fputs(" if ", out);
2052 expr_fprint(prop->visible.expr, out);
2053 }
2054 fputc('\n', out);
2055 break;
2056 case P_CHOICE:
2057 fputs(" #choice value\n", out);
2058 break;
2059 default:
2060 fprintf(out, " unknown prop %d!\n", prop->type);
2061 break;
2062 }
2063 }
2064 if (sym->help) {
2065 int len = strlen(sym->help);
2066 while (sym->help[--len] == '\n')
2067 sym->help[len] = 0;
2068 fprintf(out, " help\n%s\n", sym->help);
2069 }
2070 fputc('\n', out);
2071}
2072
2073void zconfdump(FILE *out)
2074{
2075 struct property *prop;
2076 struct symbol *sym;
2077 struct menu *menu;
2078
2079 menu = rootmenu.list;
2080 while (menu) {
2081 if ((sym = menu->sym))
2082 print_symbol(out, menu);
2083 else if ((prop = menu->prompt)) {
2084 switch (prop->type) {
2085 case P_COMMENT:
2086 fputs("\ncomment ", out);
2087 print_quoted_string(out, prop->text);
2088 fputs("\n", out);
2089 break;
2090 case P_MENU:
2091 fputs("\nmenu ", out);
2092 print_quoted_string(out, prop->text);
2093 fputs("\n", out);
2094 break;
2095 default:
2096 ;
2097 }
2098 if (!expr_is_yes(prop->visible.expr)) {
2099 fputs(" depends ", out);
2100 expr_fprint(prop->visible.expr, out);
2101 fputc('\n', out);
2102 }
2103 fputs("\n", out);
2104 }
2105
2106 if (menu->list)
2107 menu = menu->list;
2108 else if (menu->next)
2109 menu = menu->next;
2110 else while ((menu = menu->parent)) {
2111 if (menu->prompt && menu->prompt->type == P_MENU)
2112 fputs("\nendmenu\n", out);
2113 if (menu->next) {
2114 menu = menu->next;
2115 break;
2116 }
2117 }
2118 }
2119}
2120
2121#include "lex.zconf.c"
2122#include "confdata.c"
2123#include "expr.c"
2124#include "symbol.c"
2125#include "menu.c"
2126
2127
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 @@
1/* A Bison parser, made from zconf.y, by GNU bison 1.75. */
2
3/* Skeleton parser for Yacc-like parsing with Bison,
4 Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software Foundation, Inc.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21/* As a special exception, when this file is copied by Bison into a
22 Bison output file, you may use that output file without restriction.
23 This special exception was added by the Free Software Foundation
24 in version 1.24 of Bison. */
25
26#ifndef BISON_ZCONF_TAB_H
27# define BISON_ZCONF_TAB_H
28
29/* Tokens. */
30#ifndef YYTOKENTYPE
31# define YYTOKENTYPE
32 /* Put the tokens into the symbol table, so that GDB and other debuggers
33 know about them. */
34 enum yytokentype {
35 T_MAINMENU = 258,
36 T_MENU = 259,
37 T_ENDMENU = 260,
38 T_SOURCE = 261,
39 T_CHOICE = 262,
40 T_ENDCHOICE = 263,
41 T_COMMENT = 264,
42 T_CONFIG = 265,
43 T_HELP = 266,
44 T_HELPTEXT = 267,
45 T_IF = 268,
46 T_ENDIF = 269,
47 T_DEPENDS = 270,
48 T_REQUIRES = 271,
49 T_OPTIONAL = 272,
50 T_PROMPT = 273,
51 T_DEFAULT = 274,
52 T_TRISTATE = 275,
53 T_BOOLEAN = 276,
54 T_INT = 277,
55 T_HEX = 278,
56 T_WORD = 279,
57 T_STRING = 280,
58 T_UNEQUAL = 281,
59 T_EOF = 282,
60 T_EOL = 283,
61 T_CLOSE_PAREN = 284,
62 T_OPEN_PAREN = 285,
63 T_ON = 286,
64 T_OR = 287,
65 T_AND = 288,
66 T_EQUAL = 289,
67 T_NOT = 290
68 };
69#endif
70#define T_MAINMENU 258
71#define T_MENU 259
72#define T_ENDMENU 260
73#define T_SOURCE 261
74#define T_CHOICE 262
75#define T_ENDCHOICE 263
76#define T_COMMENT 264
77#define T_CONFIG 265
78#define T_HELP 266
79#define T_HELPTEXT 267
80#define T_IF 268
81#define T_ENDIF 269
82#define T_DEPENDS 270
83#define T_REQUIRES 271
84#define T_OPTIONAL 272
85#define T_PROMPT 273
86#define T_DEFAULT 274
87#define T_TRISTATE 275
88#define T_BOOLEAN 276
89#define T_INT 277
90#define T_HEX 278
91#define T_WORD 279
92#define T_STRING 280
93#define T_UNEQUAL 281
94#define T_EOF 282
95#define T_EOL 283
96#define T_CLOSE_PAREN 284
97#define T_OPEN_PAREN 285
98#define T_ON 286
99#define T_OR 287
100#define T_AND 288
101#define T_EQUAL 289
102#define T_NOT 290
103
104
105
106
107#ifndef YYSTYPE
108#line 33 "zconf.y"
109typedef union {
110 int token;
111 char *string;
112 struct symbol *symbol;
113 struct expr *expr;
114 struct menu *menu;
115} yystype;
116/* Line 1281 of /usr/share/bison/yacc.c. */
117#line 118 "zconf.tab.h"
118# define YYSTYPE yystype
119#endif
120
121extern YYSTYPE zconflval;
122
123
124#endif /* not BISON_ZCONF_TAB_H */
125
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 @@
1%{
2/*
3 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
4 * Released under the terms of the GNU GPL v2.0.
5 */
6
7#include <ctype.h>
8#include <stdarg.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <stdbool.h>
13
14#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
15
16#define PRINTD 0x0001
17#define DEBUG_PARSE 0x0002
18
19int cdebug = PRINTD;
20
21extern int zconflex(void);
22static void zconfprint(const char *err, ...);
23static void zconferror(const char *err);
24static bool zconf_endtoken(int token, int starttoken, int endtoken);
25
26struct symbol *symbol_hash[257];
27
28#define YYERROR_VERBOSE
29%}
30%expect 40
31
32%union
33{
34 int token;
35 char *string;
36 struct symbol *symbol;
37 struct expr *expr;
38 struct menu *menu;
39}
40
41%token T_MAINMENU
42%token T_MENU
43%token T_ENDMENU
44%token T_SOURCE
45%token T_CHOICE
46%token T_ENDCHOICE
47%token T_COMMENT
48%token T_CONFIG
49%token T_MENUCONFIG
50%token T_HELP
51%token <string> T_HELPTEXT
52%token T_IF
53%token T_ENDIF
54%token T_DEPENDS
55%token T_REQUIRES
56%token T_OPTIONAL
57%token T_PROMPT
58%token T_DEFAULT
59%token T_TRISTATE
60%token T_DEF_TRISTATE
61%token T_BOOLEAN
62%token T_DEF_BOOLEAN
63%token T_STRING
64%token T_INT
65%token T_HEX
66%token <string> T_WORD
67%token <string> T_WORD_QUOTE
68%token T_UNEQUAL
69%token T_EOF
70%token T_EOL
71%token T_CLOSE_PAREN
72%token T_OPEN_PAREN
73%token T_ON
74%token T_SELECT
75%token T_RANGE
76
77%left T_OR
78%left T_AND
79%left T_EQUAL T_UNEQUAL
80%nonassoc T_NOT
81
82%type <string> prompt
83%type <string> source
84%type <symbol> symbol
85%type <expr> expr
86%type <expr> if_expr
87%type <token> end
88
89%{
90#define LKC_DIRECT_LINK
91#include "lkc.h"
92%}
93%%
94input: /* empty */
95 | input block
96;
97
98block: common_block
99 | choice_stmt
100 | menu_stmt
101 | T_MAINMENU prompt nl_or_eof
102 | T_ENDMENU { zconfprint("unexpected 'endmenu' statement"); }
103 | T_ENDIF { zconfprint("unexpected 'endif' statement"); }
104 | T_ENDCHOICE { zconfprint("unexpected 'endchoice' statement"); }
105 | error nl_or_eof { zconfprint("syntax error"); yyerrok; }
106;
107
108common_block:
109 if_stmt
110 | comment_stmt
111 | config_stmt
112 | menuconfig_stmt
113 | source_stmt
114 | nl_or_eof
115;
116
117
118/* config/menuconfig entry */
119
120config_entry_start: T_CONFIG T_WORD T_EOL
121{
122 struct symbol *sym = sym_lookup($2, 0);
123 sym->flags |= SYMBOL_OPTIONAL;
124 menu_add_entry(sym);
125 printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2);
126};
127
128config_stmt: config_entry_start config_option_list
129{
130 menu_end_entry();
131 printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
132};
133
134menuconfig_entry_start: T_MENUCONFIG T_WORD T_EOL
135{
136 struct symbol *sym = sym_lookup($2, 0);
137 sym->flags |= SYMBOL_OPTIONAL;
138 menu_add_entry(sym);
139 printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2);
140};
141
142menuconfig_stmt: menuconfig_entry_start config_option_list
143{
144 if (current_entry->prompt)
145 current_entry->prompt->type = P_MENU;
146 else
147 zconfprint("warning: menuconfig statement without prompt");
148 menu_end_entry();
149 printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
150};
151
152config_option_list:
153 /* empty */
154 | config_option_list config_option
155 | config_option_list depends
156 | config_option_list help
157 | config_option_list T_EOL
158;
159
160config_option: T_TRISTATE prompt_stmt_opt T_EOL
161{
162 menu_set_type(S_TRISTATE);
163 printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno());
164};
165
166config_option: T_DEF_TRISTATE expr if_expr T_EOL
167{
168 menu_add_expr(P_DEFAULT, $2, $3);
169 menu_set_type(S_TRISTATE);
170 printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno());
171};
172
173config_option: T_BOOLEAN prompt_stmt_opt T_EOL
174{
175 menu_set_type(S_BOOLEAN);
176 printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno());
177};
178
179config_option: T_DEF_BOOLEAN expr if_expr T_EOL
180{
181 menu_add_expr(P_DEFAULT, $2, $3);
182 menu_set_type(S_BOOLEAN);
183 printd(DEBUG_PARSE, "%s:%d:def_boolean\n", zconf_curname(), zconf_lineno());
184};
185
186config_option: T_INT prompt_stmt_opt T_EOL
187{
188 menu_set_type(S_INT);
189 printd(DEBUG_PARSE, "%s:%d:int\n", zconf_curname(), zconf_lineno());
190};
191
192config_option: T_HEX prompt_stmt_opt T_EOL
193{
194 menu_set_type(S_HEX);
195 printd(DEBUG_PARSE, "%s:%d:hex\n", zconf_curname(), zconf_lineno());
196};
197
198config_option: T_STRING prompt_stmt_opt T_EOL
199{
200 menu_set_type(S_STRING);
201 printd(DEBUG_PARSE, "%s:%d:string\n", zconf_curname(), zconf_lineno());
202};
203
204config_option: T_PROMPT prompt if_expr T_EOL
205{
206 menu_add_prompt(P_PROMPT, $2, $3);
207 printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
208};
209
210config_option: T_DEFAULT expr if_expr T_EOL
211{
212 menu_add_expr(P_DEFAULT, $2, $3);
213 printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno());
214};
215
216config_option: T_SELECT T_WORD if_expr T_EOL
217{
218 menu_add_symbol(P_SELECT, sym_lookup($2, 0), $3);
219 printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
220};
221
222config_option: T_RANGE symbol symbol if_expr T_EOL
223{
224 menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4);
225 printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
226};
227
228/* choice entry */
229
230choice: T_CHOICE T_EOL
231{
232 struct symbol *sym = sym_lookup(NULL, 0);
233 sym->flags |= SYMBOL_CHOICE;
234 menu_add_entry(sym);
235 menu_add_expr(P_CHOICE, NULL, NULL);
236 printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
237};
238
239choice_entry: choice choice_option_list
240{
241 menu_end_entry();
242 menu_add_menu();
243};
244
245choice_end: end
246{
247 if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) {
248 menu_end_menu();
249 printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
250 }
251};
252
253choice_stmt:
254 choice_entry choice_block choice_end
255 | choice_entry choice_block
256{
257 printf("%s:%d: missing 'endchoice' for this 'choice' statement\n", current_menu->file->name, current_menu->lineno);
258 zconfnerrs++;
259};
260
261choice_option_list:
262 /* empty */
263 | choice_option_list choice_option
264 | choice_option_list depends
265 | choice_option_list help
266 | choice_option_list T_EOL
267;
268
269choice_option: T_PROMPT prompt if_expr T_EOL
270{
271 menu_add_prompt(P_PROMPT, $2, $3);
272 printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
273};
274
275choice_option: T_TRISTATE prompt_stmt_opt T_EOL
276{
277 menu_set_type(S_TRISTATE);
278 printd(DEBUG_PARSE, "%s:%d:tristate\n", zconf_curname(), zconf_lineno());
279};
280
281choice_option: T_BOOLEAN prompt_stmt_opt T_EOL
282{
283 menu_set_type(S_BOOLEAN);
284 printd(DEBUG_PARSE, "%s:%d:boolean\n", zconf_curname(), zconf_lineno());
285};
286
287choice_option: T_OPTIONAL T_EOL
288{
289 current_entry->sym->flags |= SYMBOL_OPTIONAL;
290 printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
291};
292
293choice_option: T_DEFAULT T_WORD if_expr T_EOL
294{
295 menu_add_symbol(P_DEFAULT, sym_lookup($2, 0), $3);
296 printd(DEBUG_PARSE, "%s:%d:default\n", zconf_curname(), zconf_lineno());
297};
298
299choice_block:
300 /* empty */
301 | choice_block common_block
302;
303
304/* if entry */
305
306if: T_IF expr T_EOL
307{
308 printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
309 menu_add_entry(NULL);
310 menu_add_dep($2);
311 menu_end_entry();
312 menu_add_menu();
313};
314
315if_end: end
316{
317 if (zconf_endtoken($1, T_IF, T_ENDIF)) {
318 menu_end_menu();
319 printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
320 }
321};
322
323if_stmt:
324 if if_block if_end
325 | if if_block
326{
327 printf("%s:%d: missing 'endif' for this 'if' statement\n", current_menu->file->name, current_menu->lineno);
328 zconfnerrs++;
329};
330
331if_block:
332 /* empty */
333 | if_block common_block
334 | if_block menu_stmt
335 | if_block choice_stmt
336;
337
338/* menu entry */
339
340menu: T_MENU prompt T_EOL
341{
342 menu_add_entry(NULL);
343 menu_add_prop(P_MENU, $2, NULL, NULL);
344 printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
345};
346
347menu_entry: menu depends_list
348{
349 menu_end_entry();
350 menu_add_menu();
351};
352
353menu_end: end
354{
355 if (zconf_endtoken($1, T_MENU, T_ENDMENU)) {
356 menu_end_menu();
357 printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
358 }
359};
360
361menu_stmt:
362 menu_entry menu_block menu_end
363 | menu_entry menu_block
364{
365 printf("%s:%d: missing 'endmenu' for this 'menu' statement\n", current_menu->file->name, current_menu->lineno);
366 zconfnerrs++;
367};
368
369menu_block:
370 /* empty */
371 | menu_block common_block
372 | menu_block menu_stmt
373 | menu_block choice_stmt
374 | menu_block error T_EOL { zconfprint("invalid menu option"); yyerrok; }
375;
376
377source: T_SOURCE prompt T_EOL
378{
379 $$ = $2;
380 printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2);
381};
382
383source_stmt: source
384{
385 zconf_nextfile($1);
386};
387
388/* comment entry */
389
390comment: T_COMMENT prompt T_EOL
391{
392 menu_add_entry(NULL);
393 menu_add_prop(P_COMMENT, $2, NULL, NULL);
394 printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
395};
396
397comment_stmt: comment depends_list
398{
399 menu_end_entry();
400};
401
402/* help option */
403
404help_start: T_HELP T_EOL
405{
406 printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
407 zconf_starthelp();
408};
409
410help: help_start T_HELPTEXT
411{
412 current_entry->sym->help = $2;
413};
414
415/* depends option */
416
417depends_list: /* empty */
418 | depends_list depends
419 | depends_list T_EOL
420;
421
422depends: T_DEPENDS T_ON expr T_EOL
423{
424 menu_add_dep($3);
425 printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
426}
427 | T_DEPENDS expr T_EOL
428{
429 menu_add_dep($2);
430 printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno());
431}
432 | T_REQUIRES expr T_EOL
433{
434 menu_add_dep($2);
435 printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno());
436};
437
438/* prompt statement */
439
440prompt_stmt_opt:
441 /* empty */
442 | prompt if_expr
443{
444 menu_add_prop(P_PROMPT, $1, NULL, $2);
445};
446
447prompt: T_WORD
448 | T_WORD_QUOTE
449;
450
451end: T_ENDMENU nl_or_eof { $$ = T_ENDMENU; }
452 | T_ENDCHOICE nl_or_eof { $$ = T_ENDCHOICE; }
453 | T_ENDIF nl_or_eof { $$ = T_ENDIF; }
454;
455
456nl_or_eof:
457 T_EOL | T_EOF;
458
459if_expr: /* empty */ { $$ = NULL; }
460 | T_IF expr { $$ = $2; }
461;
462
463expr: symbol { $$ = expr_alloc_symbol($1); }
464 | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); }
465 | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); }
466 | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; }
467 | T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); }
468 | expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); }
469 | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); }
470;
471
472symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); }
473 | T_WORD_QUOTE { $$ = sym_lookup($1, 1); free($1); }
474;
475
476%%
477
478void conf_parse(const char *name)
479{
480 struct symbol *sym;
481 int i;
482
483 zconf_initscan(name);
484
485 sym_init();
486 menu_init();
487 modules_sym = sym_lookup("MODULES", 0);
488 rootmenu.prompt = menu_add_prop(P_MENU, "BusyBox Configuration", NULL, NULL);
489
490 //zconfdebug = 1;
491 zconfparse();
492 if (zconfnerrs)
493 exit(1);
494 menu_finalize(&rootmenu);
495 for_all_symbols(i, sym) {
496 if (!(sym->flags & SYMBOL_CHECKED) && sym_check_deps(sym))
497 printf("\n");
498 else
499 sym->flags |= SYMBOL_CHECK_DONE;
500 }
501
502 sym_change_count = 1;
503}
504
505const char *zconf_tokenname(int token)
506{
507 switch (token) {
508 case T_MENU: return "menu";
509 case T_ENDMENU: return "endmenu";
510 case T_CHOICE: return "choice";
511 case T_ENDCHOICE: return "endchoice";
512 case T_IF: return "if";
513 case T_ENDIF: return "endif";
514 }
515 return "<token>";
516}
517
518static bool zconf_endtoken(int token, int starttoken, int endtoken)
519{
520 if (token != endtoken) {
521 zconfprint("unexpected '%s' within %s block", zconf_tokenname(token), zconf_tokenname(starttoken));
522 zconfnerrs++;
523 return false;
524 }
525 if (current_menu->file != current_file) {
526 zconfprint("'%s' in different file than '%s'", zconf_tokenname(token), zconf_tokenname(starttoken));
527 zconfprint("location of the '%s'", zconf_tokenname(starttoken));
528 zconfnerrs++;
529 return false;
530 }
531 return true;
532}
533
534static void zconfprint(const char *err, ...)
535{
536 va_list ap;
537
538 fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno() + 1);
539 va_start(ap, err);
540 vfprintf(stderr, err, ap);
541 va_end(ap);
542 fprintf(stderr, "\n");
543}
544
545static void zconferror(const char *err)
546{
547 fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err);
548}
549
550void print_quoted_string(FILE *out, const char *str)
551{
552 const char *p;
553 int len;
554
555 putc('"', out);
556 while ((p = strchr(str, '"'))) {
557 len = p - str;
558 if (len)
559 fprintf(out, "%.*s", len, str);
560 fputs("\\\"", out);
561 str = p + 1;
562 }
563 fputs(str, out);
564 putc('"', out);
565}
566
567void print_symbol(FILE *out, struct menu *menu)
568{
569 struct symbol *sym = menu->sym;
570 struct property *prop;
571
572 if (sym_is_choice(sym))
573 fprintf(out, "choice\n");
574 else
575 fprintf(out, "config %s\n", sym->name);
576 switch (sym->type) {
577 case S_BOOLEAN:
578 fputs(" boolean\n", out);
579 break;
580 case S_TRISTATE:
581 fputs(" tristate\n", out);
582 break;
583 case S_STRING:
584 fputs(" string\n", out);
585 break;
586 case S_INT:
587 fputs(" integer\n", out);
588 break;
589 case S_HEX:
590 fputs(" hex\n", out);
591 break;
592 default:
593 fputs(" ???\n", out);
594 break;
595 }
596 for (prop = sym->prop; prop; prop = prop->next) {
597 if (prop->menu != menu)
598 continue;
599 switch (prop->type) {
600 case P_PROMPT:
601 fputs(" prompt ", out);
602 print_quoted_string(out, prop->text);
603 if (!expr_is_yes(prop->visible.expr)) {
604 fputs(" if ", out);
605 expr_fprint(prop->visible.expr, out);
606 }
607 fputc('\n', out);
608 break;
609 case P_DEFAULT:
610 fputs( " default ", out);
611 expr_fprint(prop->expr, out);
612 if (!expr_is_yes(prop->visible.expr)) {
613 fputs(" if ", out);
614 expr_fprint(prop->visible.expr, out);
615 }
616 fputc('\n', out);
617 break;
618 case P_CHOICE:
619 fputs(" #choice value\n", out);
620 break;
621 default:
622 fprintf(out, " unknown prop %d!\n", prop->type);
623 break;
624 }
625 }
626 if (sym->help) {
627 int len = strlen(sym->help);
628 while (sym->help[--len] == '\n')
629 sym->help[len] = 0;
630 fprintf(out, " help\n%s\n", sym->help);
631 }
632 fputc('\n', out);
633}
634
635void zconfdump(FILE *out)
636{
637 struct property *prop;
638 struct symbol *sym;
639 struct menu *menu;
640
641 menu = rootmenu.list;
642 while (menu) {
643 if ((sym = menu->sym))
644 print_symbol(out, menu);
645 else if ((prop = menu->prompt)) {
646 switch (prop->type) {
647 case P_COMMENT:
648 fputs("\ncomment ", out);
649 print_quoted_string(out, prop->text);
650 fputs("\n", out);
651 break;
652 case P_MENU:
653 fputs("\nmenu ", out);
654 print_quoted_string(out, prop->text);
655 fputs("\n", out);
656 break;
657 default:
658 ;
659 }
660 if (!expr_is_yes(prop->visible.expr)) {
661 fputs(" depends ", out);
662 expr_fprint(prop->visible.expr, out);
663 fputc('\n', out);
664 }
665 fputs("\n", out);
666 }
667
668 if (menu->list)
669 menu = menu->list;
670 else if (menu->next)
671 menu = menu->next;
672 else while ((menu = menu->parent)) {
673 if (menu->prompt && menu->prompt->type == P_MENU)
674 fputs("\nendmenu\n", out);
675 if (menu->next) {
676 menu = menu->next;
677 break;
678 }
679 }
680 }
681}
682
683#include "lex.zconf.c"
684#include "confdata.c"
685#include "expr.c"
686#include "symbol.c"
687#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 @@
1/*
2 * Originally by Linus Torvalds.
3 * Smart CONFIG_* processing by Werner Almesberger, Michael Chastain.
4 *
5 * Usage: mkdep cflags -- file ...
6 *
7 * Read source files and output makefile dependency lines for them.
8 * I make simple dependency lines for #include <*.h> and #include "*.h".
9 * I also find instances of CONFIG_FOO and generate dependencies
10 * like include/config/foo.h.
11 *
12 * 1 August 1999, Michael Elizabeth Chastain, <mec@shout.net>
13 * - Keith Owens reported a bug in smart config processing. There used
14 * to be an optimization for "#define CONFIG_FOO ... #ifdef CONFIG_FOO",
15 * so that the file would not depend on CONFIG_FOO because the file defines
16 * this symbol itself. But this optimization is bogus! Consider this code:
17 * "#if 0 \n #define CONFIG_FOO \n #endif ... #ifdef CONFIG_FOO". Here
18 * the definition is inactivated, but I still used it. It turns out this
19 * actually happens a few times in the kernel source. The simple way to
20 * fix this problem is to remove this particular optimization.
21 *
22 * 2.3.99-pre1, Andrew Morton <andrewm@uow.edu.au>
23 * - Changed so that 'filename.o' depends upon 'filename.[cS]'. This is so that
24 * missing source files are noticed, rather than silently ignored.
25 *
26 * 2.4.2-pre3, Keith Owens <kaos@ocs.com.au>
27 * - Accept cflags followed by '--' followed by filenames. mkdep extracts -I
28 * options from cflags and looks in the specified directories as well as the
29 * defaults. Only -I is supported, no attempt is made to handle -idirafter,
30 * -isystem, -I- etc.
31 */
32
33#include <ctype.h>
34#include <fcntl.h>
35#include <limits.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
41#include <sys/fcntl.h>
42#include <sys/mman.h>
43#include <sys/stat.h>
44#include <sys/types.h>
45
46
47
48char depname[512];
49int hasdep;
50
51struct path_struct {
52 int len;
53 char *buffer;
54};
55struct path_struct *path_array;
56int paths;
57
58
59/* Current input file */
60static const char *g_filename;
61
62/*
63 * This records all the configuration options seen.
64 * In perl this would be a hash, but here it's a long string
65 * of values separated by newlines. This is simple and
66 * extremely fast.
67 */
68char * str_config = NULL;
69int size_config = 0;
70int len_config = 0;
71
72static void
73do_depname(void)
74{
75 if (!hasdep) {
76 hasdep = 1;
77 if (g_filename) {
78 /* Source file (*.[cS]) */
79 printf("%s:", depname);
80 printf(" %s", g_filename);
81 } else {
82 /* header file (*.h) */
83 printf("dep_%s +=", depname);
84 }
85 }
86}
87
88/*
89 * Grow the configuration string to a desired length.
90 * Usually the first growth is plenty.
91 */
92void grow_config(int len)
93{
94 while (len_config + len > size_config) {
95 if (size_config == 0)
96 size_config = 2048;
97 str_config = realloc(str_config, size_config *= 2);
98 if (str_config == NULL)
99 { perror("malloc config"); exit(1); }
100 }
101}
102
103
104
105/*
106 * Lookup a value in the configuration string.
107 */
108int is_defined_config(const char * name, int len)
109{
110 const char * pconfig;
111 const char * plast = str_config + len_config - len;
112 for ( pconfig = str_config + 1; pconfig < plast; pconfig++ ) {
113 if (pconfig[ -1] == '\n'
114 && pconfig[len] == '\n'
115 && !memcmp(pconfig, name, len))
116 return 1;
117 }
118 return 0;
119}
120
121
122
123/*
124 * Add a new value to the configuration string.
125 */
126void define_config(const char * name, int len)
127{
128 grow_config(len + 1);
129
130 memcpy(str_config+len_config, name, len);
131 len_config += len;
132 str_config[len_config++] = '\n';
133}
134
135
136
137/*
138 * Clear the set of configuration strings.
139 */
140void clear_config(void)
141{
142 len_config = 0;
143 define_config("", 0);
144}
145
146
147
148/*
149 * This records all the precious .h filenames. No need for a hash,
150 * it's a long string of values enclosed in tab and newline.
151 */
152char * str_precious = NULL;
153int size_precious = 0;
154int len_precious = 0;
155
156
157
158/*
159 * Grow the precious string to a desired length.
160 * Usually the first growth is plenty.
161 */
162void grow_precious(int len)
163{
164 while (len_precious + len > size_precious) {
165 if (size_precious == 0)
166 size_precious = 2048;
167 str_precious = realloc(str_precious, size_precious *= 2);
168 if (str_precious == NULL)
169 { perror("malloc"); exit(1); }
170 }
171}
172
173
174
175/*
176 * Add a new value to the precious string.
177 */
178void define_precious(const char * filename)
179{
180 int len = strlen(filename);
181 grow_precious(len + 4);
182 *(str_precious+len_precious++) = '\t';
183 memcpy(str_precious+len_precious, filename, len);
184 len_precious += len;
185 memcpy(str_precious+len_precious, " \\\n", 3);
186 len_precious += 3;
187}
188
189
190
191/*
192 * Handle an #include line.
193 */
194void handle_include(int start, const char * name, int len)
195{
196 struct path_struct *path;
197 int i;
198
199 if (len == 14 && !memcmp(name, "include/config.h", len))
200 return;
201
202 if (len >= 7 && !memcmp(name, "config/", 7))
203 define_config(name+7, len-7-2);
204
205 for (i = start, path = path_array+start; i < paths; ++i, ++path) {
206 memcpy(path->buffer+path->len, name, len);
207 path->buffer[path->len+len] = '\0';
208 if (access(path->buffer, F_OK) == 0) {
209 do_depname();
210 printf(" \\\n %s $(dep_%s)", path->buffer, path->buffer);
211 return;
212 }
213 }
214
215}
216
217
218
219/*
220 * Add a path to the list of include paths.
221 */
222void add_path(const char * name)
223{
224 struct path_struct *path;
225 char resolved_path[PATH_MAX+1];
226 const char *name2;
227
228 if (strcmp(name, ".")) {
229 name2 = realpath(name, resolved_path);
230 if (!name2) {
231 fprintf(stderr, "realpath(%s) failed, %m\n", name);
232 exit(1);
233 }
234 }
235 else {
236 name2 = "";
237 }
238
239 path_array = realloc(path_array, (++paths)*sizeof(*path_array));
240 if (!path_array) {
241 fprintf(stderr, "cannot expand path_arry\n");
242 exit(1);
243 }
244
245 path = path_array+paths-1;
246 path->len = strlen(name2);
247 path->buffer = malloc(path->len+1+256+1);
248 if (!path->buffer) {
249 fprintf(stderr, "cannot allocate path buffer\n");
250 exit(1);
251 }
252 strcpy(path->buffer, name2);
253 if (path->len && *(path->buffer+path->len-1) != '/') {
254 *(path->buffer+path->len) = '/';
255 *(path->buffer+(++(path->len))) = '\0';
256 }
257}
258
259
260
261/*
262 * Record the use of a CONFIG_* word.
263 */
264void use_config(const char * name, int len)
265{
266 char *pc;
267 int i;
268
269 pc = path_array[paths-1].buffer + path_array[paths-1].len;
270 memcpy(pc, "config/", 7);
271 pc += 7;
272
273 for (i = 0; i < len; i++) {
274 char c = name[i];
275 if (isupper((int)c)) c = tolower((int)c);
276 if (c == '_') c = '/';
277 pc[i] = c;
278 }
279 pc[len] = '\0';
280
281 if (is_defined_config(pc, len))
282 return;
283
284 define_config(pc, len);
285
286 do_depname();
287 printf(" \\\n $(wildcard %s.h)", path_array[paths-1].buffer);
288}
289
290
291
292/*
293 * Macros for stunningly fast map-based character access.
294 * __buf is a register which holds the current word of the input.
295 * Thus, there is one memory access per sizeof(unsigned long) characters.
296 */
297
298#if defined(__alpha__) || defined(__i386__) || defined(__ia64__) || defined(__x86_64__) || defined(__MIPSEL__) \
299 || defined(__arm__)
300#define LE_MACHINE
301#endif
302
303#ifdef LE_MACHINE
304#define next_byte(x) (x >>= 8)
305#define current ((unsigned char) __buf)
306#else
307#define next_byte(x) (x <<= 8)
308#define current (__buf >> 8*(sizeof(unsigned long)-1))
309#endif
310
311#define GETNEXT { \
312 next_byte(__buf); \
313 if ((unsigned long) next % sizeof(unsigned long) == 0) { \
314 if (next >= end) \
315 break; \
316 __buf = * (unsigned long *) next; \
317 } \
318 next++; \
319}
320
321/*
322 * State machine macros.
323 */
324#define CASE(c,label) if (current == c) goto label
325#define NOTCASE(c,label) if (current != c) goto label
326
327/*
328 * Yet another state machine speedup.
329 */
330#define MAX2(a,b) ((a)>(b)?(a):(b))
331#define MIN2(a,b) ((a)<(b)?(a):(b))
332#define MAX5(a,b,c,d,e) (MAX2(a,MAX2(b,MAX2(c,MAX2(d,e)))))
333#define MIN5(a,b,c,d,e) (MIN2(a,MIN2(b,MIN2(c,MIN2(d,e)))))
334
335
336
337/*
338 * The state machine looks for (approximately) these Perl regular expressions:
339 *
340 * m|\/\*.*?\*\/|
341 * m|\/\/.*|
342 * m|'.*?'|
343 * m|".*?"|
344 * m|#\s*include\s*"(.*?)"|
345 * m|#\s*include\s*<(.*?>"|
346 * m|#\s*(?define|undef)\s*CONFIG_(\w*)|
347 * m|(?!\w)CONFIG_|
348 *
349 * About 98% of the CPU time is spent here, and most of that is in
350 * the 'start' paragraph. Because the current characters are
351 * in a register, the start loop usually eats 4 or 8 characters
352 * per memory read. The MAX5 and MIN5 tests dispose of most
353 * input characters with 1 or 2 comparisons.
354 */
355void state_machine(const char * map, const char * end)
356{
357 const char * next = map;
358 const char * map_dot;
359 unsigned long __buf = 0;
360
361 for (;;) {
362start:
363 GETNEXT
364__start:
365 if (current > MAX5('/','\'','"','#','C')) goto start;
366 if (current < MIN5('/','\'','"','#','C')) goto start;
367 CASE('/', slash);
368 CASE('\'', squote);
369 CASE('"', dquote);
370 CASE('#', pound);
371 CASE('C', cee);
372 goto start;
373
374/* // */
375slash_slash:
376 GETNEXT
377 CASE('\n', start);
378 NOTCASE('\\', slash_slash);
379 GETNEXT
380 goto slash_slash;
381
382/* / */
383slash:
384 GETNEXT
385 CASE('/', slash_slash);
386 NOTCASE('*', __start);
387slash_star_dot_star:
388 GETNEXT
389__slash_star_dot_star:
390 NOTCASE('*', slash_star_dot_star);
391 GETNEXT
392 NOTCASE('/', __slash_star_dot_star);
393 goto start;
394
395/* '.*?' */
396squote:
397 GETNEXT
398 CASE('\'', start);
399 NOTCASE('\\', squote);
400 GETNEXT
401 goto squote;
402
403/* ".*?" */
404dquote:
405 GETNEXT
406 CASE('"', start);
407 NOTCASE('\\', dquote);
408 GETNEXT
409 goto dquote;
410
411/* #\s* */
412pound:
413 GETNEXT
414 CASE(' ', pound);
415 CASE('\t', pound);
416 CASE('i', pound_i);
417 CASE('d', pound_d);
418 CASE('u', pound_u);
419 goto __start;
420
421/* #\s*i */
422pound_i:
423 GETNEXT NOTCASE('n', __start);
424 GETNEXT NOTCASE('c', __start);
425 GETNEXT NOTCASE('l', __start);
426 GETNEXT NOTCASE('u', __start);
427 GETNEXT NOTCASE('d', __start);
428 GETNEXT NOTCASE('e', __start);
429 goto pound_include;
430
431/* #\s*include\s* */
432pound_include:
433 GETNEXT
434 CASE(' ', pound_include);
435 CASE('\t', pound_include);
436 map_dot = next;
437 CASE('"', pound_include_dquote);
438 CASE('<', pound_include_langle);
439 goto __start;
440
441/* #\s*include\s*"(.*)" */
442pound_include_dquote:
443 GETNEXT
444 CASE('\n', start);
445 NOTCASE('"', pound_include_dquote);
446 handle_include(0, map_dot, next - map_dot - 1);
447 goto start;
448
449/* #\s*include\s*<(.*)> */
450pound_include_langle:
451 GETNEXT
452 CASE('\n', start);
453 NOTCASE('>', pound_include_langle);
454 handle_include(1, map_dot, next - map_dot - 1);
455 goto start;
456
457/* #\s*d */
458pound_d:
459 GETNEXT NOTCASE('e', __start);
460 GETNEXT NOTCASE('f', __start);
461 GETNEXT NOTCASE('i', __start);
462 GETNEXT NOTCASE('n', __start);
463 GETNEXT NOTCASE('e', __start);
464 goto pound_define_undef;
465
466/* #\s*u */
467pound_u:
468 GETNEXT NOTCASE('n', __start);
469 GETNEXT NOTCASE('d', __start);
470 GETNEXT NOTCASE('e', __start);
471 GETNEXT NOTCASE('f', __start);
472 goto pound_define_undef;
473
474/*
475 * #\s*(define|undef)\s*CONFIG_(\w*)
476 *
477 * this does not define the word, because it could be inside another
478 * conditional (#if 0). But I do parse the word so that this instance
479 * does not count as a use. -- mec
480 */
481pound_define_undef:
482 GETNEXT
483 CASE(' ', pound_define_undef);
484 CASE('\t', pound_define_undef);
485
486 NOTCASE('C', __start);
487 GETNEXT NOTCASE('O', __start);
488 GETNEXT NOTCASE('N', __start);
489 GETNEXT NOTCASE('F', __start);
490 GETNEXT NOTCASE('I', __start);
491 GETNEXT NOTCASE('G', __start);
492 GETNEXT NOTCASE('_', __start);
493
494 map_dot = next;
495pound_define_undef_CONFIG_word:
496 GETNEXT
497 if (isalnum(current) || current == '_')
498 goto pound_define_undef_CONFIG_word;
499 goto __start;
500
501/* \<CONFIG_(\w*) */
502cee:
503 if (next >= map+2 && (isalnum((int)next[-2]) || next[-2] == '_'))
504 goto start;
505 GETNEXT NOTCASE('O', __start);
506 GETNEXT NOTCASE('N', __start);
507 GETNEXT NOTCASE('F', __start);
508 GETNEXT NOTCASE('I', __start);
509 GETNEXT NOTCASE('G', __start);
510 GETNEXT NOTCASE('_', __start);
511
512 map_dot = next;
513cee_CONFIG_word:
514 GETNEXT
515 if (isalnum(current) || current == '_')
516 goto cee_CONFIG_word;
517 use_config(map_dot, next - map_dot - 1);
518 goto __start;
519 }
520}
521
522
523
524/*
525 * Generate dependencies for one file.
526 */
527void do_depend(const char * filename)
528{
529 int mapsize;
530 int pagesizem1 = getpagesize()-1;
531 int fd;
532 struct stat st;
533 char * map;
534
535 fd = open(filename, O_RDONLY);
536 if (fd < 0) {
537 perror(filename);
538 return;
539 }
540
541 fstat(fd, &st);
542 if (st.st_size == 0) {
543 fprintf(stderr,"%s is empty\n",filename);
544 close(fd);
545 return;
546 }
547
548 mapsize = st.st_size;
549 mapsize = (mapsize+pagesizem1) & ~pagesizem1;
550 map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0);
551 if ((long) map == -1) {
552 perror("mkdep: mmap");
553 close(fd);
554 return;
555 }
556 if ((unsigned long) map % sizeof(unsigned long) != 0)
557 {
558 fprintf(stderr, "do_depend: map not aligned\n");
559 exit(1);
560 }
561
562 hasdep = 0;
563 clear_config();
564 state_machine(map, map+st.st_size);
565 if (hasdep) {
566 puts("");
567 }
568
569 munmap(map, mapsize);
570 close(fd);
571}
572
573
574
575/*
576 * Generate dependencies for all files.
577 */
578int main(int argc, char **argv)
579{
580 int len;
581 const char *hpath;
582
583 hpath = getenv("TOPDIR");
584 if (!hpath) {
585 fputs("mkdep: TOPDIR not set in environment. "
586 "Don't bypass the top level Makefile.\n", stderr);
587 return 1;
588 }
589
590 add_path("."); /* for #include "..." */
591
592 while (++argv, --argc > 0) {
593 if (strncmp(*argv, "-I", 2) == 0) {
594 if (*((*argv)+2)) {
595 add_path((*argv)+2);
596 }
597 else {
598 ++argv;
599 --argc;
600 add_path(*argv);
601 }
602 }
603 else if (strcmp(*argv, "--") == 0) {
604 break;
605 }
606 }
607
608 add_path(hpath); /* must be last entry, for config files */
609
610 while (--argc > 0) {
611 const char * filename = *++argv;
612 g_filename = 0;
613 len = strlen(filename);
614 memcpy(depname, filename, len+1);
615 if (len > 2 && filename[len-2] == '.') {
616 if (filename[len-1] == 'c' || filename[len-1] == 'S') {
617 depname[len-1] = 'o';
618 g_filename = filename;
619 }
620 }
621 do_depend(filename);
622 }
623 if (len_precious) {
624 *(str_precious+len_precious) = '\0';
625 printf(".PRECIOUS:%s\n", str_precious);
626 }
627 return 0;
628}
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 @@
1/*
2 * split-include.c
3 *
4 * Copyright abandoned, Michael Chastain, <mailto:mec@shout.net>.
5 * This is a C version of syncdep.pl by Werner Almesberger.
6 *
7 * This program takes autoconf.h as input and outputs a directory full
8 * of one-line include files, merging onto the old values.
9 *
10 * Think of the configuration options as key-value pairs. Then there
11 * are five cases:
12 *
13 * key old value new value action
14 *
15 * KEY-1 VALUE-1 VALUE-1 leave file alone
16 * KEY-2 VALUE-2A VALUE-2B write VALUE-2B into file
17 * KEY-3 - VALUE-3 write VALUE-3 into file
18 * KEY-4 VALUE-4 - write an empty file
19 * KEY-5 (empty) - leave old empty file alone
20 */
21
22#include <sys/stat.h>
23#include <sys/types.h>
24
25#include <ctype.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <unistd.h>
32
33#define ERROR_EXIT(strExit) \
34 { \
35 const int errnoSave = errno; \
36 fprintf(stderr, "%s: ", str_my_name); \
37 errno = errnoSave; \
38 perror((strExit)); \
39 exit(1); \
40 }
41
42
43
44int main(int argc, const char * argv [])
45{
46 const char * str_my_name;
47 const char * str_file_autoconf;
48 const char * str_dir_config;
49
50 FILE * fp_config;
51 FILE * fp_target;
52 FILE * fp_find;
53
54 int buffer_size;
55
56 char * line;
57 char * old_line;
58 char * list_target;
59 char * ptarget;
60
61 struct stat stat_buf;
62
63 /* Check arg count. */
64 if (argc != 3)
65 {
66 fprintf(stderr, "%s: wrong number of arguments.\n", argv[0]);
67 exit(1);
68 }
69
70 str_my_name = argv[0];
71 str_file_autoconf = argv[1];
72 str_dir_config = argv[2];
73
74 /* Find a buffer size. */
75 if (stat(str_file_autoconf, &stat_buf) != 0)
76 ERROR_EXIT(str_file_autoconf);
77 buffer_size = 2 * stat_buf.st_size + 4096;
78
79 /* Allocate buffers. */
80 if ( (line = malloc(buffer_size)) == NULL
81 || (old_line = malloc(buffer_size)) == NULL
82 || (list_target = malloc(buffer_size)) == NULL )
83 ERROR_EXIT(str_file_autoconf);
84
85 /* Open autoconfig file. */
86 if ((fp_config = fopen(str_file_autoconf, "r")) == NULL)
87 ERROR_EXIT(str_file_autoconf);
88
89 /* Make output directory if needed. */
90 if (stat(str_dir_config, &stat_buf) != 0)
91 {
92 if (mkdir(str_dir_config, 0755) != 0)
93 ERROR_EXIT(str_dir_config);
94 }
95
96 /* Change to output directory. */
97 if (chdir(str_dir_config) != 0)
98 ERROR_EXIT(str_dir_config);
99
100 /* Put initial separator into target list. */
101 ptarget = list_target;
102 *ptarget++ = '\n';
103
104 /* Read config lines. */
105 while (fgets(line, buffer_size, fp_config))
106 {
107 const char * str_config;
108 int is_same;
109 int itarget;
110
111 if (line[0] != '#')
112 continue;
113 if ((str_config = strstr(line, "CONFIG_")) == NULL)
114 continue;
115
116 /* Make the output file name. */
117 str_config += sizeof("CONFIG_") - 1;
118 for (itarget = 0; !isspace(str_config[itarget]); itarget++)
119 {
120 char c = str_config[itarget];
121 if (isupper(c)) c = tolower(c);
122 if (c == '_') c = '/';
123 ptarget[itarget] = c;
124 }
125 ptarget[itarget++] = '.';
126 ptarget[itarget++] = 'h';
127 ptarget[itarget++] = '\0';
128
129 /* Check for existing file. */
130 is_same = 0;
131 if ((fp_target = fopen(ptarget, "r")) != NULL)
132 {
133 fgets(old_line, buffer_size, fp_target);
134 if (fclose(fp_target) != 0)
135 ERROR_EXIT(ptarget);
136 if (!strcmp(line, old_line))
137 is_same = 1;
138 }
139
140 if (!is_same)
141 {
142 /* Auto-create directories. */
143 int islash;
144 for (islash = 0; islash < itarget; islash++)
145 {
146 if (ptarget[islash] == '/')
147 {
148 ptarget[islash] = '\0';
149 if (stat(ptarget, &stat_buf) != 0
150 && mkdir(ptarget, 0755) != 0)
151 ERROR_EXIT( ptarget );
152 ptarget[islash] = '/';
153 }
154 }
155
156 /* Write the file. */
157 if ((fp_target = fopen(ptarget, "w" )) == NULL)
158 ERROR_EXIT(ptarget);
159 fputs(line, fp_target);
160 if (ferror(fp_target) || fclose(fp_target) != 0)
161 ERROR_EXIT(ptarget);
162 }
163
164 /* Update target list */
165 ptarget += itarget;
166 *(ptarget-1) = '\n';
167 }
168
169 /*
170 * Close autoconfig file.
171 * Terminate the target list.
172 */
173 if (fclose(fp_config) != 0)
174 ERROR_EXIT(str_file_autoconf);
175 *ptarget = '\0';
176
177 /*
178 * Fix up existing files which have no new value.
179 * This is Case 4 and Case 5.
180 *
181 * I re-read the tree and filter it against list_target.
182 * This is crude. But it avoids data copies. Also, list_target
183 * is compact and contiguous, so it easily fits into cache.
184 *
185 * Notice that list_target contains strings separated by \n,
186 * with a \n before the first string and after the last.
187 * fgets gives the incoming names a terminating \n.
188 * So by having an initial \n, strstr will find exact matches.
189 */
190
191 fp_find = popen("find * -type f -name \"*.h\" -print", "r");
192 if (fp_find == 0)
193 ERROR_EXIT( "find" );
194
195 line[0] = '\n';
196 while (fgets(line+1, buffer_size, fp_find))
197 {
198 if (strstr(list_target, line) == NULL)
199 {
200 /*
201 * This is an old file with no CONFIG_* flag in autoconf.h.
202 */
203
204 /* First strip the \n. */
205 line[strlen(line)-1] = '\0';
206
207 /* Grab size. */
208 if (stat(line+1, &stat_buf) != 0)
209 ERROR_EXIT(line);
210
211 /* If file is not empty, make it empty and give it a fresh date. */
212 if (stat_buf.st_size != 0)
213 {
214 if ((fp_target = fopen(line+1, "w")) == NULL)
215 ERROR_EXIT(line);
216 if (fclose(fp_target) != 0)
217 ERROR_EXIT(line);
218 }
219 }
220 }
221
222 if (pclose(fp_find) != 0)
223 ERROR_EXIT("find");
224
225 return 0;
226}
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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Another Bourne-like Shell"
7
8choice
9 prompt "Choose your default shell"
10 default CONFIG_FEATURE_SH_IS_NONE
11 help
12 Choose a shell. The ash shell is the most bash compatible
13 and full featured one.
14
15config CONFIG_FEATURE_SH_IS_ASH
16 select CONFIG_ASH
17 bool "ash"
18
19config CONFIG_FEATURE_SH_IS_HUSH
20 select CONFIG_HUSH
21 bool "hush"
22
23config CONFIG_FEATURE_SH_IS_LASH
24 select CONFIG_LASH
25 bool "lash"
26
27config CONFIG_FEATURE_SH_IS_MSH
28 select CONFIG_MSH
29 bool "msh"
30
31config CONFIG_FEATURE_SH_IS_NONE
32 bool "none"
33
34endchoice
35
36config CONFIG_ASH
37 bool "ash"
38 default y
39 help
40 Tha 'ash' shell adds about 60k in the default configuration and is
41 the most complete and most pedantically correct shell included with
42 busybox. This shell is actually a derivative of the Debian 'dash'
43 shell (by Herbert Xu), which was created by porting the 'ash' shell
44 (written by Kenneth Almquist) from NetBSD.
45
46comment "Ash Shell Options"
47 depends on CONFIG_ASH
48
49config CONFIG_ASH_JOB_CONTROL
50 bool " Enable Job control"
51 default y
52 depends on CONFIG_ASH
53 help
54 Enable job control in the ash shell.
55
56config CONFIG_ASH_ALIAS
57 bool " Enable alias support"
58 default y
59 depends on CONFIG_ASH
60 help
61 Enable alias support in the ash shell.
62
63config CONFIG_ASH_MATH_SUPPORT
64 bool " Enable Posix math support"
65 default y
66 depends on CONFIG_ASH
67 help
68 Enable math support in the ash shell.
69
70config CONFIG_ASH_MATH_SUPPORT_64
71 bool " Extend Posix math support to 64 bit"
72 default n
73 depends on CONFIG_ASH_MATH_SUPPORT
74 help
75 Enable 64-bit math support in the ash shell. This will make
76 the shell slightly larger, but will allow computation with very
77 large numbers.
78
79config CONFIG_ASH_GETOPTS
80 bool " Enable getopts builtin to parse positional parameters"
81 default n
82 depends on CONFIG_ASH
83 help
84 Enable getopts builtin in the ash shell.
85
86config CONFIG_ASH_CMDCMD
87 bool " Enable cmdcmd to override shell builtins"
88 default n
89 depends on CONFIG_ASH
90 help
91 Enable support for the ash 'command' builtin, which allows
92 you to run the specified command with the specified arguments,
93 even when there is an ash builtin command with the same name.
94
95config CONFIG_ASH_MAIL
96 bool " Check for new mail on interactive shells"
97 default y
98 depends on CONFIG_ASH
99 help
100 Enable "check for new mail" in the ash shell.
101
102config CONFIG_ASH_OPTIMIZE_FOR_SIZE
103 bool " Optimize for size instead of speed"
104 default y
105 depends on CONFIG_ASH
106 help
107 Compile ash for reduced size at price of speed.
108
109config CONFIG_ASH_RANDOM_SUPPORT
110 bool " Enable pseudorandom generator and variable $RANDOM"
111 default n
112 depends on CONFIG_ASH
113 help
114 Enable pseudorandom generator and dynamic variable "$RANDOM".
115 Each read of "$RANDOM" will generate a new pseudorandom value.
116 You can reset the generator by using a specified start value.
117 After "unset RANDOM" then generator will switch off and this
118 variable will no longer have special treatment.
119
120config CONFIG_HUSH
121 bool "hush"
122 default n
123 help
124 hush is a very small shell (just 18k) and it has fairly complete
125 Bourne shell grammar. It even handles all the normal flow control
126 options such as if/then/elif/else/fi, for/in/do/done, while loops,
127 etc.
128
129 It does not handle case/esac, select, function, here documents ( <<
130 word ), arithmetic expansion, aliases, brace expansion, tilde
131 expansion, &> and >& redirection of stdout+stderr, etc.
132
133
134config CONFIG_LASH
135 bool "lash"
136 default n
137 help
138 lash is the very smallest shell (adds just 10k) and it is quite
139 usable as a command prompt, but it is not suitable for any but the
140 most trivial scripting (such as an initrd that calls insmod a few
141 times) since it does not understand any Bourne shell grammar. It
142 does handle pipes, redirects, and job control though. Adding in
143 command editing makes it a very nice lightweight command prompt.
144
145
146config CONFIG_MSH
147 bool "msh"
148 default n
149 help
150 The minix shell (adds just 30k) is quite complete and handles things
151 like for/do/done, case/esac and all the things you expect a Bourne
152 shell to do. It is not always pedantically correct about Bourne
153 shell grammar (try running the shell testscript "tests/sh.testcases"
154 on it and compare vs bash) but for most things it works quite well.
155 It also uses only vfork, so it can be used on uClinux systems.
156
157comment "Bourne Shell Options"
158 depends on CONFIG_MSH || CONFIG_LASH || CONFIG_HUSH || CONFIG_ASH
159
160config CONFIG_FEATURE_SH_EXTRA_QUIET
161 bool "Hide message on interactive shell startup"
162 default n
163 depends on CONFIG_MSH || CONFIG_LASH || CONFIG_HUSH || CONFIG_ASH
164 help
165 Remove the busybox introduction when starting a shell.
166
167config CONFIG_FEATURE_SH_STANDALONE_SHELL
168 bool "Standalone shell"
169 default n
170 depends on CONFIG_MSH || CONFIG_LASH || CONFIG_HUSH || CONFIG_ASH
171 help
172 This option causes the selected busybox shell to use busybox applets
173 in preference to executables in the PATH whenever possible. For
174 example, entering the command 'ifconfig' into the shell would cause
175 busybox to use the ifconfig busybox applet. Specifying the fully
176 qualified executable name, such as '/sbin/ifconfig' will still
177 execute the /sbin/ifconfig executable on the filesystem. This option
178 is generally used when creating a staticly linked version of busybox
179 for use as a rescue shell, in the event that you screw up your system.
180
181 Note that when using this option, the shell will attempt to directly
182 run '/bin/busybox'. If you do not have the busybox binary sitting in
183 that exact location with that exact name, this option will not work at
184 all.
185
186config CONFIG_FEATURE_COMMAND_EDITING
187 bool "command line editing"
188 default n
189 depends on CONFIG_MSH || CONFIG_LASH || CONFIG_HUSH || CONFIG_ASH
190 help
191 Enable command editing in shell.
192
193config CONFIG_FEATURE_COMMAND_HISTORY
194 int "history size"
195 default 15
196 depends on CONFIG_FEATURE_COMMAND_EDITING
197 help
198 Specify command history size in shell.
199
200config CONFIG_FEATURE_COMMAND_SAVEHISTORY
201 bool "history saving"
202 default n
203 depends on CONFIG_ASH && CONFIG_FEATURE_COMMAND_EDITING
204 help
205 Enable history saving in ash shell.
206
207config CONFIG_FEATURE_COMMAND_TAB_COMPLETION
208 bool "tab completion"
209 default n
210 depends on CONFIG_FEATURE_COMMAND_EDITING
211 help
212 Enable tab completion in shell.
213
214config CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
215 bool "username completion"
216 default n
217 depends on CONFIG_FEATURE_COMMAND_TAB_COMPLETION
218 help
219 Enable username completion in shell.
220
221config CONFIG_FEATURE_SH_FANCY_PROMPT
222 bool "Fancy shell prompts"
223 default n
224 depends on CONFIG_FEATURE_COMMAND_EDITING
225 help
226 Setting this option allows for prompts to use things like \w and
227 \$ and also using escape codes.
228
229endmenu
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/shell
23SHELL_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19
20SHELL_AR:=shell.a
21ifndef $(SHELL_DIR)
22SHELL_DIR:=$(top_builddir)/shell/
23endif
24srcdir=$(top_srcdir)/shell
25
26SHELLT-y:=
27SHELLT-$(CONFIG_ASH) += ash.o
28SHELLT-$(CONFIG_HUSH) += hush.o
29SHELLT-$(CONFIG_LASH) += lash.o
30SHELLT-$(CONFIG_MSH) += msh.o
31SHELLT-$(CONFIG_FEATURE_COMMAND_EDITING) += cmdedit.o
32
33libraries-y+=$(SHELL_DIR)$(SHELL_AR)
34
35$(SHELL_DIR)$(SHELL_AR): $(patsubst %,$(SHELL_DIR)%, $(SHELLT-y))
36 $(AR) -ro $@ $(patsubst %,$(SHELL_DIR)%, $(SHELLT-y))
37
38$(SHELL_DIR)%.o: $(srcdir)/%.c
39 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
40
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
5 * Copyright (c) 1989, 1991, 1993, 1994
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Copyright (c) 1997-2003 Herbert Xu <herbert@debian.org>
9 * was re-ported from NetBSD and debianized.
10 *
11 *
12 * This code is derived from software contributed to Berkeley by
13 * Kenneth Almquist.
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 *
29 * Original BSD copyright notice is retained at the end of this file.
30 */
31
32/*
33 * rewrite arith.y to micro stack based cryptic algorithm by
34 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
35 *
36 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
37 * dynamic variables.
38 *
39 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2004 to be
40 * used in busybox and size optimizations,
41 * rewrote arith (see notes to this), added locale support,
42 * rewrote dynamic variables.
43 *
44 */
45
46
47/*
48 * The follow should be set to reflect the type of system you have:
49 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
50 * define SYSV if you are running under System V.
51 * define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
52 * define DEBUG=2 to compile in and turn on debugging.
53 *
54 * When debugging is on, debugging info will be written to ./trace and
55 * a quit signal will generate a core dump.
56 */
57
58
59
60#define IFS_BROKEN
61
62#define PROFILE 0
63
64#ifdef DEBUG
65#define _GNU_SOURCE
66#endif
67
68#include <sys/types.h>
69#include <sys/cdefs.h>
70#include <sys/ioctl.h>
71#include <sys/param.h>
72#include <sys/resource.h>
73#include <sys/stat.h>
74#include <sys/time.h>
75#include <sys/wait.h>
76
77#include <stdio.h>
78#include <stdlib.h>
79#include <string.h>
80#include <unistd.h>
81
82#include <stdarg.h>
83#include <stddef.h>
84#include <assert.h>
85#include <ctype.h>
86#include <dirent.h>
87#include <errno.h>
88#include <fcntl.h>
89#include <limits.h>
90#include <paths.h>
91#include <setjmp.h>
92#include <signal.h>
93#include <stdint.h>
94#include <sysexits.h>
95#include <time.h>
96#include <fnmatch.h>
97
98
99#include "busybox.h"
100#include "pwd_.h"
101
102#ifdef CONFIG_ASH_JOB_CONTROL
103#define JOBS 1
104#else
105#undef JOBS
106#endif
107
108#if JOBS
109#include <termios.h>
110#endif
111
112#include "cmdedit.h"
113
114#ifdef __GLIBC__
115/* glibc sucks */
116static int *dash_errno;
117#undef errno
118#define errno (*dash_errno)
119#endif
120
121#if defined(__uClinux__)
122#error "Do not even bother, ash will not run on uClinux"
123#endif
124
125#ifdef DEBUG
126#define _DIAGASSERT(assert_expr) assert(assert_expr)
127#else
128#define _DIAGASSERT(assert_expr)
129#endif
130
131
132#ifdef CONFIG_ASH_ALIAS
133/* $NetBSD: alias.h,v 1.5 2002/11/24 22:35:38 christos Exp $ */
134
135#define ALIASINUSE 1
136#define ALIASDEAD 2
137
138struct alias {
139 struct alias *next;
140 char *name;
141 char *val;
142 int flag;
143};
144
145static struct alias *lookupalias(const char *, int);
146static int aliascmd(int, char **);
147static int unaliascmd(int, char **);
148static void rmaliases(void);
149static int unalias(const char *);
150static void printalias(const struct alias *);
151#endif
152
153/* $NetBSD: cd.h,v 1.3 2002/11/24 22:35:39 christos Exp $ */
154
155
156static void setpwd(const char *, int);
157
158/* $NetBSD: error.h,v 1.15 2002/11/24 22:35:39 christos Exp $ */
159
160
161/*
162 * Types of operations (passed to the errmsg routine).
163 */
164
165
166static const char not_found_msg[] = "%s: not found";
167
168
169#define E_OPEN "No such file" /* opening a file */
170#define E_CREAT "Directory nonexistent" /* creating a file */
171#define E_EXEC not_found_msg+4 /* executing a program */
172
173/*
174 * We enclose jmp_buf in a structure so that we can declare pointers to
175 * jump locations. The global variable handler contains the location to
176 * jump to when an exception occurs, and the global variable exception
177 * contains a code identifying the exception. To implement nested
178 * exception handlers, the user should save the value of handler on entry
179 * to an inner scope, set handler to point to a jmploc structure for the
180 * inner scope, and restore handler on exit from the scope.
181 */
182
183struct jmploc {
184 jmp_buf loc;
185};
186
187static struct jmploc *handler;
188static int exception;
189static volatile int suppressint;
190static volatile sig_atomic_t intpending;
191
192static int exerrno; /* Last exec error, error for EXEXEC */
193
194/* exceptions */
195#define EXINT 0 /* SIGINT received */
196#define EXERROR 1 /* a generic error */
197#define EXSHELLPROC 2 /* execute a shell procedure */
198#define EXEXEC 3 /* command execution failed */
199#define EXEXIT 4 /* exit the shell */
200#define EXSIG 5 /* trapped signal in wait(1) */
201
202
203/* do we generate EXSIG events */
204static int exsig;
205/* last pending signal */
206static volatile sig_atomic_t pendingsigs;
207
208/*
209 * These macros allow the user to suspend the handling of interrupt signals
210 * over a period of time. This is similar to SIGHOLD to or sigblock, but
211 * much more efficient and portable. (But hacking the kernel is so much
212 * more fun than worrying about efficiency and portability. :-))
213 */
214
215#define xbarrier() ({ __asm__ __volatile__ ("": : :"memory"); })
216#define INTOFF \
217 ({ \
218 suppressint++; \
219 xbarrier(); \
220 0; \
221 })
222#define SAVEINT(v) ((v) = suppressint)
223#define RESTOREINT(v) \
224 ({ \
225 xbarrier(); \
226 if ((suppressint = (v)) == 0 && intpending) onint(); \
227 0; \
228 })
229#define EXSIGON() \
230 ({ \
231 exsig++; \
232 xbarrier(); \
233 if (pendingsigs) \
234 exraise(EXSIG); \
235 0; \
236 })
237/* EXSIG is turned off by evalbltin(). */
238
239
240static void exraise(int) __attribute__((__noreturn__));
241static void onint(void) __attribute__((__noreturn__));
242
243static void error(const char *, ...) __attribute__((__noreturn__));
244static void exerror(int, const char *, ...) __attribute__((__noreturn__));
245
246static void sh_warnx(const char *, ...);
247
248#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
249static void
250inton(void) {
251 if (--suppressint == 0 && intpending) {
252 onint();
253 }
254}
255#define INTON inton()
256static void forceinton(void)
257{
258 suppressint = 0;
259 if (intpending)
260 onint();
261}
262#define FORCEINTON forceinton()
263#else
264#define INTON \
265 ({ \
266 xbarrier(); \
267 if (--suppressint == 0 && intpending) onint(); \
268 0; \
269 })
270#define FORCEINTON \
271 ({ \
272 xbarrier(); \
273 suppressint = 0; \
274 if (intpending) onint(); \
275 0; \
276 })
277#endif /* CONFIG_ASH_OPTIMIZE_FOR_SIZE */
278
279/*
280 * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
281 * so we use _setjmp instead.
282 */
283
284#if defined(BSD) && !defined(__SVR4) && !defined(__GLIBC__)
285#define setjmp(jmploc) _setjmp(jmploc)
286#define longjmp(jmploc, val) _longjmp(jmploc, val)
287#endif
288
289/* $NetBSD: expand.h,v 1.13 2002/11/24 22:35:40 christos Exp $ */
290
291struct strlist {
292 struct strlist *next;
293 char *text;
294};
295
296
297struct arglist {
298 struct strlist *list;
299 struct strlist **lastp;
300};
301
302/*
303 * expandarg() flags
304 */
305#define EXP_FULL 0x1 /* perform word splitting & file globbing */
306#define EXP_TILDE 0x2 /* do normal tilde expansion */
307#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
308#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
309#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
310#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
311#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
312#define EXP_WORD 0x80 /* expand word in parameter expansion */
313#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
314
315
316union node;
317static void expandarg(union node *, struct arglist *, int);
318#define rmescapes(p) _rmescapes((p), 0)
319static char *_rmescapes(char *, int);
320static int casematch(union node *, char *);
321
322#ifdef CONFIG_ASH_MATH_SUPPORT
323static void expari(int);
324#endif
325
326/* $NetBSD: eval.h,v 1.13 2002/11/24 22:35:39 christos Exp $ */
327
328static char *commandname; /* currently executing command */
329static struct strlist *cmdenviron; /* environment for builtin command */
330static int exitstatus; /* exit status of last command */
331static int back_exitstatus; /* exit status of backquoted command */
332
333
334struct backcmd { /* result of evalbackcmd */
335 int fd; /* file descriptor to read from */
336 char *buf; /* buffer */
337 int nleft; /* number of chars in buffer */
338 struct job *jp; /* job structure for command */
339};
340
341/*
342 * This file was generated by the mknodes program.
343 */
344
345#define NCMD 0
346#define NPIPE 1
347#define NREDIR 2
348#define NBACKGND 3
349#define NSUBSHELL 4
350#define NAND 5
351#define NOR 6
352#define NSEMI 7
353#define NIF 8
354#define NWHILE 9
355#define NUNTIL 10
356#define NFOR 11
357#define NCASE 12
358#define NCLIST 13
359#define NDEFUN 14
360#define NARG 15
361#define NTO 16
362#define NCLOBBER 17
363#define NFROM 18
364#define NFROMTO 19
365#define NAPPEND 20
366#define NTOFD 21
367#define NFROMFD 22
368#define NHERE 23
369#define NXHERE 24
370#define NNOT 25
371
372
373
374struct ncmd {
375 int type;
376 union node *assign;
377 union node *args;
378 union node *redirect;
379};
380
381
382struct npipe {
383 int type;
384 int backgnd;
385 struct nodelist *cmdlist;
386};
387
388
389struct nredir {
390 int type;
391 union node *n;
392 union node *redirect;
393};
394
395
396struct nbinary {
397 int type;
398 union node *ch1;
399 union node *ch2;
400};
401
402
403struct nif {
404 int type;
405 union node *test;
406 union node *ifpart;
407 union node *elsepart;
408};
409
410
411struct nfor {
412 int type;
413 union node *args;
414 union node *body;
415 char *var;
416};
417
418
419struct ncase {
420 int type;
421 union node *expr;
422 union node *cases;
423};
424
425
426struct nclist {
427 int type;
428 union node *next;
429 union node *pattern;
430 union node *body;
431};
432
433
434struct narg {
435 int type;
436 union node *next;
437 char *text;
438 struct nodelist *backquote;
439};
440
441
442struct nfile {
443 int type;
444 union node *next;
445 int fd;
446 union node *fname;
447 char *expfname;
448};
449
450
451struct ndup {
452 int type;
453 union node *next;
454 int fd;
455 int dupfd;
456 union node *vname;
457};
458
459
460struct nhere {
461 int type;
462 union node *next;
463 int fd;
464 union node *doc;
465};
466
467
468struct nnot {
469 int type;
470 union node *com;
471};
472
473
474union node {
475 int type;
476 struct ncmd ncmd;
477 struct npipe npipe;
478 struct nredir nredir;
479 struct nbinary nbinary;
480 struct nif nif;
481 struct nfor nfor;
482 struct ncase ncase;
483 struct nclist nclist;
484 struct narg narg;
485 struct nfile nfile;
486 struct ndup ndup;
487 struct nhere nhere;
488 struct nnot nnot;
489};
490
491
492struct nodelist {
493 struct nodelist *next;
494 union node *n;
495};
496
497
498struct funcnode {
499 int count;
500 union node n;
501};
502
503
504static void freefunc(struct funcnode *);
505/* $NetBSD: parser.h,v 1.15 2002/11/24 22:35:42 christos Exp $ */
506
507/* control characters in argument strings */
508#define CTL_FIRST '\201' /* first 'special' character */
509#define CTLESC '\201' /* escape next character */
510#define CTLVAR '\202' /* variable defn */
511#define CTLENDVAR '\203'
512#define CTLBACKQ '\204'
513#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
514/* CTLBACKQ | CTLQUOTE == '\205' */
515#define CTLARI '\206' /* arithmetic expression */
516#define CTLENDARI '\207'
517#define CTLQUOTEMARK '\210'
518#define CTL_LAST '\210' /* last 'special' character */
519
520/* variable substitution byte (follows CTLVAR) */
521#define VSTYPE 0x0f /* type of variable substitution */
522#define VSNUL 0x10 /* colon--treat the empty string as unset */
523#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
524
525/* values of VSTYPE field */
526#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
527#define VSMINUS 0x2 /* ${var-text} */
528#define VSPLUS 0x3 /* ${var+text} */
529#define VSQUESTION 0x4 /* ${var?message} */
530#define VSASSIGN 0x5 /* ${var=text} */
531#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
532#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
533#define VSTRIMLEFT 0x8 /* ${var#pattern} */
534#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
535#define VSLENGTH 0xa /* ${#var} */
536
537/* values of checkkwd variable */
538#define CHKALIAS 0x1
539#define CHKKWD 0x2
540#define CHKNL 0x4
541
542#define IBUFSIZ (BUFSIZ + 1)
543
544/*
545 * NEOF is returned by parsecmd when it encounters an end of file. It
546 * must be distinct from NULL, so we use the address of a variable that
547 * happens to be handy.
548 */
549static int plinno = 1; /* input line number */
550
551/* number of characters left in input buffer */
552static int parsenleft; /* copy of parsefile->nleft */
553static int parselleft; /* copy of parsefile->lleft */
554
555/* next character in input buffer */
556static char *parsenextc; /* copy of parsefile->nextc */
557
558struct strpush {
559 struct strpush *prev; /* preceding string on stack */
560 char *prevstring;
561 int prevnleft;
562#ifdef CONFIG_ASH_ALIAS
563 struct alias *ap; /* if push was associated with an alias */
564#endif
565 char *string; /* remember the string since it may change */
566};
567
568struct parsefile {
569 struct parsefile *prev; /* preceding file on stack */
570 int linno; /* current line */
571 int fd; /* file descriptor (or -1 if string) */
572 int nleft; /* number of chars left in this line */
573 int lleft; /* number of chars left in this buffer */
574 char *nextc; /* next char in buffer */
575 char *buf; /* input buffer */
576 struct strpush *strpush; /* for pushing strings at this level */
577 struct strpush basestrpush; /* so pushing one is fast */
578};
579
580static struct parsefile basepf; /* top level input file */
581static char basebuf[IBUFSIZ]; /* buffer for top level input file */
582static struct parsefile *parsefile = &basepf; /* current input file */
583
584
585static int tokpushback; /* last token pushed back */
586#define NEOF ((union node *)&tokpushback)
587static int parsebackquote; /* nonzero if we are inside backquotes */
588static int doprompt; /* if set, prompt the user */
589static int needprompt; /* true if interactive and at start of line */
590static int lasttoken; /* last token read */
591static char *wordtext; /* text of last word returned by readtoken */
592static int checkkwd;
593static struct nodelist *backquotelist;
594static union node *redirnode;
595static struct heredoc *heredoc;
596static int quoteflag; /* set if (part of) last token was quoted */
597static int startlinno; /* line # where last token started */
598
599static union node *parsecmd(int);
600static void fixredir(union node *, const char *, int);
601static const char *const *findkwd(const char *);
602static char *endofname(const char *);
603
604/* $NetBSD: shell.h,v 1.16 2003/01/22 20:36:04 dsl Exp $ */
605
606typedef void *pointer;
607
608static char nullstr[1]; /* zero length string */
609static const char spcstr[] = " ";
610static const char snlfmt[] = "%s\n";
611static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
612static const char illnum[] = "Illegal number: %s";
613static const char homestr[] = "HOME";
614
615#ifdef DEBUG
616#define TRACE(param) trace param
617#define TRACEV(param) tracev param
618#else
619#define TRACE(param)
620#define TRACEV(param)
621#endif
622
623#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
624#define __builtin_expect(x, expected_value) (x)
625#endif
626
627#define xlikely(x) __builtin_expect((x),1)
628
629
630#define TEOF 0
631#define TNL 1
632#define TREDIR 2
633#define TWORD 3
634#define TSEMI 4
635#define TBACKGND 5
636#define TAND 6
637#define TOR 7
638#define TPIPE 8
639#define TLP 9
640#define TRP 10
641#define TENDCASE 11
642#define TENDBQUOTE 12
643#define TNOT 13
644#define TCASE 14
645#define TDO 15
646#define TDONE 16
647#define TELIF 17
648#define TELSE 18
649#define TESAC 19
650#define TFI 20
651#define TFOR 21
652#define TIF 22
653#define TIN 23
654#define TTHEN 24
655#define TUNTIL 25
656#define TWHILE 26
657#define TBEGIN 27
658#define TEND 28
659
660/* first char is indicating which tokens mark the end of a list */
661static const char *const tokname_array[] = {
662 "\1end of file",
663 "\0newline",
664 "\0redirection",
665 "\0word",
666 "\0;",
667 "\0&",
668 "\0&&",
669 "\0||",
670 "\0|",
671 "\0(",
672 "\1)",
673 "\1;;",
674 "\1`",
675#define KWDOFFSET 13
676 /* the following are keywords */
677 "\0!",
678 "\0case",
679 "\1do",
680 "\1done",
681 "\1elif",
682 "\1else",
683 "\1esac",
684 "\1fi",
685 "\0for",
686 "\0if",
687 "\0in",
688 "\1then",
689 "\0until",
690 "\0while",
691 "\0{",
692 "\1}",
693};
694
695static const char *tokname(int tok)
696{
697 static char buf[16];
698
699 if (tok >= TSEMI)
700 buf[0] = '"';
701 sprintf(buf + (tok >= TSEMI), "%s%c",
702 tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
703 return buf;
704}
705
706/* $NetBSD: machdep.h,v 1.10 2002/10/07 14:26:08 christos Exp $ */
707
708/*
709 * Most machines require the value returned from malloc to be aligned
710 * in some way. The following macro will get this right on many machines.
711 */
712
713#define SHELL_SIZE (sizeof(union {int i; char *cp; double d; }) - 1)
714/*
715 * It appears that grabstackstr() will barf with such alignments
716 * because stalloc() will return a string allocated in a new stackblock.
717 */
718#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
719
720/*
721 * This file was generated by the mksyntax program.
722 */
723
724
725/* Syntax classes */
726#define CWORD 0 /* character is nothing special */
727#define CNL 1 /* newline character */
728#define CBACK 2 /* a backslash character */
729#define CSQUOTE 3 /* single quote */
730#define CDQUOTE 4 /* double quote */
731#define CENDQUOTE 5 /* a terminating quote */
732#define CBQUOTE 6 /* backwards single quote */
733#define CVAR 7 /* a dollar sign */
734#define CENDVAR 8 /* a '}' character */
735#define CLP 9 /* a left paren in arithmetic */
736#define CRP 10 /* a right paren in arithmetic */
737#define CENDFILE 11 /* end of file */
738#define CCTL 12 /* like CWORD, except it must be escaped */
739#define CSPCL 13 /* these terminate a word */
740#define CIGN 14 /* character should be ignored */
741
742#ifdef CONFIG_ASH_ALIAS
743#define SYNBASE 130
744#define PEOF -130
745#define PEOA -129
746#define PEOA_OR_PEOF PEOA
747#else
748#define SYNBASE 129
749#define PEOF -129
750#define PEOA_OR_PEOF PEOF
751#endif
752
753#define is_digit(c) ((unsigned)((c) - '0') <= 9)
754#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
755#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
756
757/*
758 * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
759 * (assuming ascii char codes, as the original implementation did)
760 */
761#define is_special(c) \
762 ( (((unsigned int)c) - 33 < 32) \
763 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
764
765#define digit_val(c) ((c) - '0')
766
767/*
768 * This file was generated by the mksyntax program.
769 */
770
771#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
772#define USE_SIT_FUNCTION
773#endif
774
775/* number syntax index */
776#define BASESYNTAX 0 /* not in quotes */
777#define DQSYNTAX 1 /* in double quotes */
778#define SQSYNTAX 2 /* in single quotes */
779#define ARISYNTAX 3 /* in arithmetic */
780
781#ifdef CONFIG_ASH_MATH_SUPPORT
782static const char S_I_T[][4] = {
783#ifdef CONFIG_ASH_ALIAS
784 {CSPCL, CIGN, CIGN, CIGN}, /* 0, PEOA */
785#endif
786 {CSPCL, CWORD, CWORD, CWORD}, /* 1, ' ' */
787 {CNL, CNL, CNL, CNL}, /* 2, \n */
788 {CWORD, CCTL, CCTL, CWORD}, /* 3, !*-/:=?[]~ */
789 {CDQUOTE, CENDQUOTE, CWORD, CWORD}, /* 4, '"' */
790 {CVAR, CVAR, CWORD, CVAR}, /* 5, $ */
791 {CSQUOTE, CWORD, CENDQUOTE, CWORD}, /* 6, "'" */
792 {CSPCL, CWORD, CWORD, CLP}, /* 7, ( */
793 {CSPCL, CWORD, CWORD, CRP}, /* 8, ) */
794 {CBACK, CBACK, CCTL, CBACK}, /* 9, \ */
795 {CBQUOTE, CBQUOTE, CWORD, CBQUOTE}, /* 10, ` */
796 {CENDVAR, CENDVAR, CWORD, CENDVAR}, /* 11, } */
797#ifndef USE_SIT_FUNCTION
798 {CENDFILE, CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
799 {CWORD, CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
800 {CCTL, CCTL, CCTL, CCTL} /* 14, CTLESC ... */
801#endif
802};
803#else
804static const char S_I_T[][3] = {
805#ifdef CONFIG_ASH_ALIAS
806 {CSPCL, CIGN, CIGN}, /* 0, PEOA */
807#endif
808 {CSPCL, CWORD, CWORD}, /* 1, ' ' */
809 {CNL, CNL, CNL}, /* 2, \n */
810 {CWORD, CCTL, CCTL}, /* 3, !*-/:=?[]~ */
811 {CDQUOTE, CENDQUOTE, CWORD}, /* 4, '"' */
812 {CVAR, CVAR, CWORD}, /* 5, $ */
813 {CSQUOTE, CWORD, CENDQUOTE}, /* 6, "'" */
814 {CSPCL, CWORD, CWORD}, /* 7, ( */
815 {CSPCL, CWORD, CWORD}, /* 8, ) */
816 {CBACK, CBACK, CCTL}, /* 9, \ */
817 {CBQUOTE, CBQUOTE, CWORD}, /* 10, ` */
818 {CENDVAR, CENDVAR, CWORD}, /* 11, } */
819#ifndef USE_SIT_FUNCTION
820 {CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
821 {CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
822 {CCTL, CCTL, CCTL} /* 14, CTLESC ... */
823#endif
824};
825#endif /* CONFIG_ASH_MATH_SUPPORT */
826
827#ifdef USE_SIT_FUNCTION
828
829#define U_C(c) ((unsigned char)(c))
830
831static int SIT(int c, int syntax)
832{
833 static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
834#ifdef CONFIG_ASH_ALIAS
835 static const char syntax_index_table[] = {
836 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
837 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
838 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
839 11, 3 /* "}~" */
840 };
841#else
842 static const char syntax_index_table[] = {
843 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
844 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
845 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
846 10, 2 /* "}~" */
847 };
848#endif
849 const char *s;
850 int indx;
851
852 if (c == PEOF) /* 2^8+2 */
853 return CENDFILE;
854#ifdef CONFIG_ASH_ALIAS
855 if (c == PEOA) /* 2^8+1 */
856 indx = 0;
857 else
858#endif
859 if (U_C(c) >= U_C(CTLESC) && U_C(c) <= U_C(CTLQUOTEMARK))
860 return CCTL;
861 else {
862 s = strchr(spec_symbls, c);
863 if (s == 0 || *s == 0)
864 return CWORD;
865 indx = syntax_index_table[(s - spec_symbls)];
866 }
867 return S_I_T[indx][syntax];
868}
869
870#else /* USE_SIT_FUNCTION */
871
872#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]
873
874#ifdef CONFIG_ASH_ALIAS
875#define CSPCL_CIGN_CIGN_CIGN 0
876#define CSPCL_CWORD_CWORD_CWORD 1
877#define CNL_CNL_CNL_CNL 2
878#define CWORD_CCTL_CCTL_CWORD 3
879#define CDQUOTE_CENDQUOTE_CWORD_CWORD 4
880#define CVAR_CVAR_CWORD_CVAR 5
881#define CSQUOTE_CWORD_CENDQUOTE_CWORD 6
882#define CSPCL_CWORD_CWORD_CLP 7
883#define CSPCL_CWORD_CWORD_CRP 8
884#define CBACK_CBACK_CCTL_CBACK 9
885#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10
886#define CENDVAR_CENDVAR_CWORD_CENDVAR 11
887#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12
888#define CWORD_CWORD_CWORD_CWORD 13
889#define CCTL_CCTL_CCTL_CCTL 14
890#else
891#define CSPCL_CWORD_CWORD_CWORD 0
892#define CNL_CNL_CNL_CNL 1
893#define CWORD_CCTL_CCTL_CWORD 2
894#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
895#define CVAR_CVAR_CWORD_CVAR 4
896#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
897#define CSPCL_CWORD_CWORD_CLP 6
898#define CSPCL_CWORD_CWORD_CRP 7
899#define CBACK_CBACK_CCTL_CBACK 8
900#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
901#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
902#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
903#define CWORD_CWORD_CWORD_CWORD 12
904#define CCTL_CCTL_CCTL_CCTL 13
905#endif
906
907static const char syntax_index_table[258] = {
908 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
909 /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
910#ifdef CONFIG_ASH_ALIAS
911 /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
912#endif
913 /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
914 /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
915 /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
916 /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
917 /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
918 /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
919 /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
920 /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
921 /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
922 /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
923 /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
924 /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
925 /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
926 /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
927 /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
928 /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
929 /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
930 /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
931 /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
932 /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
933 /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
934 /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
935 /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
936 /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
937 /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
938 /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
939 /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
940 /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
941 /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
942 /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
943 /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
944 /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
945 /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
946 /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
947 /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
948 /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
949 /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
950 /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
951 /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
952 /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
953 /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
954 /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
955 /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
956 /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
957 /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
958 /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
959 /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
960 /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
961 /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
962 /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
963 /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
964 /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
965 /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
966 /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
967 /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
968 /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
969 /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
970 /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
971 /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
972 /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
973 /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
974 /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
975 /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
976 /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
977 /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
978 /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
979 /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
980 /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
981 /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
982 /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
983 /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
984 /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
985 /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
986 /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
987 /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
988 /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
989 /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
990 /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
991 /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
992 /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
993 /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
994 /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
995 /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
996 /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
997 /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
998 /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
999 /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
1000 /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
1001 /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
1002 /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
1003 /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
1004 /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
1005 /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
1006 /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
1007 /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
1008 /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
1009 /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
1010 /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
1011 /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
1012 /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
1013 /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
1014 /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
1015 /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
1016 /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
1017 /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
1018 /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
1019 /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
1020 /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
1021 /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
1022 /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
1023 /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
1024 /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
1025 /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
1026 /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
1027 /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
1028 /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
1029 /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
1030 /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
1031 /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
1032 /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
1033 /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
1034 /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
1035 /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
1036 /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
1037 /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
1038 /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
1039 /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
1040 /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
1041 /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
1042 /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
1043 /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
1044 /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
1045 /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
1046 /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
1047 /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
1048 /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
1049 /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
1050 /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
1051 /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
1052 /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
1053 /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
1054 /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
1055 /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
1056 /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
1057 /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
1058 /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
1059 /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
1060 /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
1061 /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
1062 /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
1063 /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
1064 /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
1065 /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
1066 /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
1067 /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
1068 /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
1069 /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
1070 /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
1071 /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
1072 /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
1073 /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
1074 /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
1075 /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
1076 /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
1077 /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
1078 /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
1079 /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
1080 /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
1081 /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
1082 /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
1083 /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
1084 /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
1085 /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
1086 /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
1087 /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
1088 /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
1089 /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
1090 /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
1091 /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
1092 /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
1093 /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
1094 /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
1095 /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
1096 /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
1097 /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
1098 /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
1099 /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
1100 /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
1101 /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
1102 /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
1103 /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
1104 /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
1105 /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
1106 /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
1107 /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
1108 /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
1109 /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
1110 /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
1111 /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
1112 /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
1113 /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
1114 /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
1115 /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
1116 /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
1117 /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
1118 /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
1119 /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
1120 /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
1121 /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
1122 /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
1123 /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
1124 /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
1125 /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
1126 /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
1127 /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
1128 /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
1129 /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
1130 /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
1131 /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
1132 /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
1133 /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
1134 /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
1135 /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
1136 /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
1137 /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
1138 /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
1139 /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
1140 /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
1141 /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
1142 /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
1143 /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
1144 /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
1145 /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
1146 /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
1147 /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
1148 /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
1149 /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
1150 /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
1151 /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
1152 /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
1153 /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
1154 /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
1155 /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
1156 /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
1157 /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
1158 /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
1159 /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
1160 /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
1161 /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
1162 /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
1163 /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
1164 /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
1165 /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
1166 /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
1167 /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
1168 /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
1169};
1170
1171#endif /* USE_SIT_FUNCTION */
1172
1173/* $NetBSD: alias.c,v 1.11 2002/11/24 22:35:38 christos Exp $ */
1174
1175
1176#define ATABSIZE 39
1177
1178static int funcblocksize; /* size of structures in function */
1179static int funcstringsize; /* size of strings in node */
1180static pointer funcblock; /* block to allocate function from */
1181static char *funcstring; /* block to allocate strings from */
1182
1183static const short nodesize[26] = {
1184 SHELL_ALIGN(sizeof (struct ncmd)),
1185 SHELL_ALIGN(sizeof (struct npipe)),
1186 SHELL_ALIGN(sizeof (struct nredir)),
1187 SHELL_ALIGN(sizeof (struct nredir)),
1188 SHELL_ALIGN(sizeof (struct nredir)),
1189 SHELL_ALIGN(sizeof (struct nbinary)),
1190 SHELL_ALIGN(sizeof (struct nbinary)),
1191 SHELL_ALIGN(sizeof (struct nbinary)),
1192 SHELL_ALIGN(sizeof (struct nif)),
1193 SHELL_ALIGN(sizeof (struct nbinary)),
1194 SHELL_ALIGN(sizeof (struct nbinary)),
1195 SHELL_ALIGN(sizeof (struct nfor)),
1196 SHELL_ALIGN(sizeof (struct ncase)),
1197 SHELL_ALIGN(sizeof (struct nclist)),
1198 SHELL_ALIGN(sizeof (struct narg)),
1199 SHELL_ALIGN(sizeof (struct narg)),
1200 SHELL_ALIGN(sizeof (struct nfile)),
1201 SHELL_ALIGN(sizeof (struct nfile)),
1202 SHELL_ALIGN(sizeof (struct nfile)),
1203 SHELL_ALIGN(sizeof (struct nfile)),
1204 SHELL_ALIGN(sizeof (struct nfile)),
1205 SHELL_ALIGN(sizeof (struct ndup)),
1206 SHELL_ALIGN(sizeof (struct ndup)),
1207 SHELL_ALIGN(sizeof (struct nhere)),
1208 SHELL_ALIGN(sizeof (struct nhere)),
1209 SHELL_ALIGN(sizeof (struct nnot)),
1210};
1211
1212
1213static void calcsize(union node *);
1214static void sizenodelist(struct nodelist *);
1215static union node *copynode(union node *);
1216static struct nodelist *copynodelist(struct nodelist *);
1217static char *nodesavestr(char *);
1218
1219
1220
1221static void evalstring(char *);
1222union node; /* BLETCH for ansi C */
1223static void evaltree(union node *, int);
1224static void evalbackcmd(union node *, struct backcmd *);
1225
1226/* in_function returns nonzero if we are currently evaluating a function */
1227#define in_function() funcnest
1228static int evalskip; /* set if we are skipping commands */
1229static int skipcount; /* number of levels to skip */
1230static int funcnest; /* depth of function calls */
1231
1232/* reasons for skipping commands (see comment on breakcmd routine) */
1233#define SKIPBREAK 1
1234#define SKIPCONT 2
1235#define SKIPFUNC 3
1236#define SKIPFILE 4
1237
1238/*
1239 * This file was generated by the mkbuiltins program.
1240 */
1241
1242#ifdef JOBS
1243static int bgcmd(int, char **);
1244#endif
1245static int breakcmd(int, char **);
1246static int cdcmd(int, char **);
1247#ifdef CONFIG_ASH_CMDCMD
1248static int commandcmd(int, char **);
1249#endif
1250static int dotcmd(int, char **);
1251static int evalcmd(int, char **);
1252static int execcmd(int, char **);
1253static int exitcmd(int, char **);
1254static int exportcmd(int, char **);
1255static int falsecmd(int, char **);
1256#ifdef JOBS
1257static int fgcmd(int, char **);
1258#endif
1259#ifdef CONFIG_ASH_GETOPTS
1260static int getoptscmd(int, char **);
1261#endif
1262static int hashcmd(int, char **);
1263#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
1264static int helpcmd(int argc, char **argv);
1265#endif
1266#ifdef JOBS
1267static int jobscmd(int, char **);
1268#endif
1269#ifdef CONFIG_ASH_MATH_SUPPORT
1270static int letcmd(int, char **);
1271#endif
1272static int localcmd(int, char **);
1273static int pwdcmd(int, char **);
1274static int readcmd(int, char **);
1275static int returncmd(int, char **);
1276static int setcmd(int, char **);
1277static int shiftcmd(int, char **);
1278static int timescmd(int, char **);
1279static int trapcmd(int, char **);
1280static int truecmd(int, char **);
1281static int typecmd(int, char **);
1282static int umaskcmd(int, char **);
1283static int unsetcmd(int, char **);
1284static int waitcmd(int, char **);
1285static int ulimitcmd(int, char **);
1286#ifdef JOBS
1287static int killcmd(int, char **);
1288#endif
1289
1290/* $NetBSD: mail.h,v 1.9 2002/11/24 22:35:40 christos Exp $ */
1291
1292#ifdef CONFIG_ASH_MAIL
1293static void chkmail(void);
1294static void changemail(const char *);
1295#endif
1296
1297/* $NetBSD: exec.h,v 1.20 2003/01/22 20:36:04 dsl Exp $ */
1298
1299/* values of cmdtype */
1300#define CMDUNKNOWN -1 /* no entry in table for command */
1301#define CMDNORMAL 0 /* command is an executable program */
1302#define CMDFUNCTION 1 /* command is a shell function */
1303#define CMDBUILTIN 2 /* command is a shell builtin */
1304
1305struct builtincmd {
1306 const char *name;
1307 int (*builtin)(int, char **);
1308 /* unsigned flags; */
1309};
1310
1311#ifdef CONFIG_ASH_CMDCMD
1312# ifdef JOBS
1313# ifdef CONFIG_ASH_ALIAS
1314# define COMMANDCMD (builtincmd + 7)
1315# define EXECCMD (builtincmd + 10)
1316# else
1317# define COMMANDCMD (builtincmd + 6)
1318# define EXECCMD (builtincmd + 9)
1319# endif
1320# else /* ! JOBS */
1321# ifdef CONFIG_ASH_ALIAS
1322# define COMMANDCMD (builtincmd + 6)
1323# define EXECCMD (builtincmd + 9)
1324# else
1325# define COMMANDCMD (builtincmd + 5)
1326# define EXECCMD (builtincmd + 8)
1327# endif
1328# endif /* JOBS */
1329#else /* ! CONFIG_ASH_CMDCMD */
1330# ifdef JOBS
1331# ifdef CONFIG_ASH_ALIAS
1332# define EXECCMD (builtincmd + 9)
1333# else
1334# define EXECCMD (builtincmd + 8)
1335# endif
1336# else /* ! JOBS */
1337# ifdef CONFIG_ASH_ALIAS
1338# define EXECCMD (builtincmd + 8)
1339# else
1340# define EXECCMD (builtincmd + 7)
1341# endif
1342# endif /* JOBS */
1343#endif /* CONFIG_ASH_CMDCMD */
1344
1345#define BUILTIN_NOSPEC "0"
1346#define BUILTIN_SPECIAL "1"
1347#define BUILTIN_REGULAR "2"
1348#define BUILTIN_SPEC_REG "3"
1349#define BUILTIN_ASSIGN "4"
1350#define BUILTIN_SPEC_ASSG "5"
1351#define BUILTIN_REG_ASSG "6"
1352#define BUILTIN_SPEC_REG_ASSG "7"
1353
1354#define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1)
1355#define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2)
1356
1357static const struct builtincmd builtincmd[] = {
1358 { BUILTIN_SPEC_REG ".", dotcmd },
1359 { BUILTIN_SPEC_REG ":", truecmd },
1360#ifdef CONFIG_ASH_ALIAS
1361 { BUILTIN_REG_ASSG "alias", aliascmd },
1362#endif
1363#ifdef JOBS
1364 { BUILTIN_REGULAR "bg", bgcmd },
1365#endif
1366 { BUILTIN_SPEC_REG "break", breakcmd },
1367 { BUILTIN_REGULAR "cd", cdcmd },
1368 { BUILTIN_NOSPEC "chdir", cdcmd },
1369#ifdef CONFIG_ASH_CMDCMD
1370 { BUILTIN_REGULAR "command", commandcmd },
1371#endif
1372 { BUILTIN_SPEC_REG "continue", breakcmd },
1373 { BUILTIN_SPEC_REG "eval", evalcmd },
1374 { BUILTIN_SPEC_REG "exec", execcmd },
1375 { BUILTIN_SPEC_REG "exit", exitcmd },
1376 { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
1377 { BUILTIN_REGULAR "false", falsecmd },
1378#ifdef JOBS
1379 { BUILTIN_REGULAR "fg", fgcmd },
1380#endif
1381#ifdef CONFIG_ASH_GETOPTS
1382 { BUILTIN_REGULAR "getopts", getoptscmd },
1383#endif
1384 { BUILTIN_NOSPEC "hash", hashcmd },
1385#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
1386 { BUILTIN_NOSPEC "help", helpcmd },
1387#endif
1388#ifdef JOBS
1389 { BUILTIN_REGULAR "jobs", jobscmd },
1390 { BUILTIN_REGULAR "kill", killcmd },
1391#endif
1392#ifdef CONFIG_ASH_MATH_SUPPORT
1393 { BUILTIN_NOSPEC "let", letcmd },
1394#endif
1395 { BUILTIN_ASSIGN "local", localcmd },
1396 { BUILTIN_NOSPEC "pwd", pwdcmd },
1397 { BUILTIN_REGULAR "read", readcmd },
1398 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
1399 { BUILTIN_SPEC_REG "return", returncmd },
1400 { BUILTIN_SPEC_REG "set", setcmd },
1401 { BUILTIN_SPEC_REG "shift", shiftcmd },
1402 { BUILTIN_SPEC_REG "times", timescmd },
1403 { BUILTIN_SPEC_REG "trap", trapcmd },
1404 { BUILTIN_REGULAR "true", truecmd },
1405 { BUILTIN_NOSPEC "type", typecmd },
1406 { BUILTIN_NOSPEC "ulimit", ulimitcmd },
1407 { BUILTIN_REGULAR "umask", umaskcmd },
1408#ifdef CONFIG_ASH_ALIAS
1409 { BUILTIN_REGULAR "unalias", unaliascmd },
1410#endif
1411 { BUILTIN_SPEC_REG "unset", unsetcmd },
1412 { BUILTIN_REGULAR "wait", waitcmd },
1413};
1414
1415#define NUMBUILTINS (sizeof (builtincmd) / sizeof (struct builtincmd) )
1416
1417
1418
1419struct cmdentry {
1420 int cmdtype;
1421 union param {
1422 int index;
1423 const struct builtincmd *cmd;
1424 struct funcnode *func;
1425 } u;
1426};
1427
1428
1429/* action to find_command() */
1430#define DO_ERR 0x01 /* prints errors */
1431#define DO_ABS 0x02 /* checks absolute paths */
1432#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
1433#define DO_ALTPATH 0x08 /* using alternate path */
1434#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
1435
1436static const char *pathopt; /* set by padvance */
1437
1438static void shellexec(char **, const char *, int)
1439 __attribute__((__noreturn__));
1440static char *padvance(const char **, const char *);
1441static void find_command(char *, struct cmdentry *, int, const char *);
1442static struct builtincmd *find_builtin(const char *);
1443static void hashcd(void);
1444static void changepath(const char *);
1445static void defun(char *, union node *);
1446static void unsetfunc(const char *);
1447
1448#ifdef CONFIG_ASH_MATH_SUPPORT_64
1449typedef int64_t arith_t;
1450#else
1451typedef long arith_t;
1452#endif
1453
1454#ifdef CONFIG_ASH_MATH_SUPPORT
1455static arith_t dash_arith(const char *);
1456static arith_t arith(const char *expr, int *perrcode);
1457#endif
1458
1459#ifdef CONFIG_ASH_RANDOM_SUPPORT
1460static unsigned long rseed;
1461static void change_random(const char *);
1462# ifndef DYNAMIC_VAR
1463# define DYNAMIC_VAR
1464# endif
1465#endif
1466
1467/* $NetBSD: init.h,v 1.9 2002/11/24 22:35:40 christos Exp $ */
1468
1469static void reset(void);
1470
1471/* $NetBSD: var.h,v 1.21 2003/01/22 20:36:04 dsl Exp $ */
1472
1473/*
1474 * Shell variables.
1475 */
1476
1477/* flags */
1478#define VEXPORT 0x01 /* variable is exported */
1479#define VREADONLY 0x02 /* variable cannot be modified */
1480#define VSTRFIXED 0x04 /* variable struct is statically allocated */
1481#define VTEXTFIXED 0x08 /* text is statically allocated */
1482#define VSTACK 0x10 /* text is allocated on the stack */
1483#define VUNSET 0x20 /* the variable is not set */
1484#define VNOFUNC 0x40 /* don't call the callback function */
1485#define VNOSET 0x80 /* do not set variable - just readonly test */
1486#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
1487#ifdef DYNAMIC_VAR
1488# define VDYNAMIC 0x200 /* dynamic variable */
1489# else
1490# define VDYNAMIC 0
1491#endif
1492
1493struct var {
1494 struct var *next; /* next entry in hash list */
1495 int flags; /* flags are defined above */
1496 const char *text; /* name=value */
1497 void (*func)(const char *); /* function to be called when */
1498 /* the variable gets set/unset */
1499};
1500
1501struct localvar {
1502 struct localvar *next; /* next local variable in list */
1503 struct var *vp; /* the variable that was made local */
1504 int flags; /* saved flags */
1505 const char *text; /* saved text */
1506};
1507
1508
1509static struct localvar *localvars;
1510
1511/*
1512 * Shell variables.
1513 */
1514
1515#ifdef CONFIG_ASH_GETOPTS
1516static void getoptsreset(const char *);
1517#endif
1518
1519#ifdef CONFIG_LOCALE_SUPPORT
1520#include <locale.h>
1521static void change_lc_all(const char *value);
1522static void change_lc_ctype(const char *value);
1523#endif
1524
1525
1526#define VTABSIZE 39
1527
1528static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
1529#ifdef IFS_BROKEN
1530static const char defifsvar[] = "IFS= \t\n";
1531#define defifs (defifsvar + 4)
1532#else
1533static const char defifs[] = " \t\n";
1534#endif
1535
1536
1537static struct var varinit[] = {
1538#ifdef IFS_BROKEN
1539 { 0, VSTRFIXED|VTEXTFIXED, defifsvar, 0 },
1540#else
1541 { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", 0 },
1542#endif
1543
1544#ifdef CONFIG_ASH_MAIL
1545 { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
1546 { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
1547#endif
1548
1549 { 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
1550 { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 },
1551 { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 },
1552 { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 },
1553#ifdef CONFIG_ASH_GETOPTS
1554 { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
1555#endif
1556#ifdef CONFIG_ASH_RANDOM_SUPPORT
1557 {0, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
1558#endif
1559#ifdef CONFIG_LOCALE_SUPPORT
1560 {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
1561 {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
1562#endif
1563#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
1564 {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
1565#endif
1566};
1567
1568#define vifs varinit[0]
1569#ifdef CONFIG_ASH_MAIL
1570#define vmail (&vifs)[1]
1571#define vmpath (&vmail)[1]
1572#else
1573#define vmpath vifs
1574#endif
1575#define vpath (&vmpath)[1]
1576#define vps1 (&vpath)[1]
1577#define vps2 (&vps1)[1]
1578#define vps4 (&vps2)[1]
1579#define voptind (&vps4)[1]
1580#ifdef CONFIG_ASH_GETOPTS
1581#define vrandom (&voptind)[1]
1582#else
1583#define vrandom (&vps4)[1]
1584#endif
1585#define defpath (defpathvar + 5)
1586
1587/*
1588 * The following macros access the values of the above variables.
1589 * They have to skip over the name. They return the null string
1590 * for unset variables.
1591 */
1592
1593#define ifsval() (vifs.text + 4)
1594#define ifsset() ((vifs.flags & VUNSET) == 0)
1595#define mailval() (vmail.text + 5)
1596#define mpathval() (vmpath.text + 9)
1597#define pathval() (vpath.text + 5)
1598#define ps1val() (vps1.text + 4)
1599#define ps2val() (vps2.text + 4)
1600#define ps4val() (vps4.text + 4)
1601#define optindval() (voptind.text + 7)
1602
1603#define mpathset() ((vmpath.flags & VUNSET) == 0)
1604
1605static void setvar(const char *, const char *, int);
1606static void setvareq(char *, int);
1607static void listsetvar(struct strlist *, int);
1608static char *lookupvar(const char *);
1609static char *bltinlookup(const char *);
1610static char **listvars(int, int, char ***);
1611#define environment() listvars(VEXPORT, VUNSET, 0)
1612static int showvars(const char *, int, int);
1613static void poplocalvars(void);
1614static int unsetvar(const char *);
1615#ifdef CONFIG_ASH_GETOPTS
1616static int setvarsafe(const char *, const char *, int);
1617#endif
1618static int varcmp(const char *, const char *);
1619static struct var **hashvar(const char *);
1620
1621
1622static inline int varequal(const char *a, const char *b) {
1623 return !varcmp(a, b);
1624}
1625
1626
1627static int loopnest; /* current loop nesting level */
1628
1629/*
1630 * The parsefile structure pointed to by the global variable parsefile
1631 * contains information about the current file being read.
1632 */
1633
1634
1635struct redirtab {
1636 struct redirtab *next;
1637 int renamed[10];
1638 int nullredirs;
1639};
1640
1641static struct redirtab *redirlist;
1642static int nullredirs;
1643
1644extern char **environ;
1645
1646/* $NetBSD: output.h,v 1.16 2002/11/24 22:35:42 christos Exp $ */
1647
1648
1649static void outstr(const char *, FILE *);
1650static void outcslow(int, FILE *);
1651static void flushall(void);
1652static void flusherr(void);
1653static int out1fmt(const char *, ...)
1654 __attribute__((__format__(__printf__,1,2)));
1655static int fmtstr(char *, size_t, const char *, ...)
1656 __attribute__((__format__(__printf__,3,4)));
1657
1658static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
1659
1660
1661static void out1str(const char *p)
1662{
1663 outstr(p, stdout);
1664}
1665
1666static void out2str(const char *p)
1667{
1668 outstr(p, stderr);
1669 flusherr();
1670}
1671
1672/*
1673 * Initialization code.
1674 */
1675
1676/*
1677 * This routine initializes the builtin variables.
1678 */
1679
1680static inline void
1681initvar(void)
1682{
1683 struct var *vp;
1684 struct var *end;
1685 struct var **vpp;
1686
1687 /*
1688 * PS1 depends on uid
1689 */
1690#if defined(CONFIG_FEATURE_COMMAND_EDITING) && defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1691 vps1.text = "PS1=\\w \\$ ";
1692#else
1693 if (!geteuid())
1694 vps1.text = "PS1=# ";
1695#endif
1696 vp = varinit;
1697 end = vp + sizeof(varinit) / sizeof(varinit[0]);
1698 do {
1699 vpp = hashvar(vp->text);
1700 vp->next = *vpp;
1701 *vpp = vp;
1702 } while (++vp < end);
1703}
1704
1705static inline void
1706init(void)
1707{
1708
1709 /* from input.c: */
1710 {
1711 basepf.nextc = basepf.buf = basebuf;
1712 }
1713
1714 /* from trap.c: */
1715 {
1716 signal(SIGCHLD, SIG_DFL);
1717 }
1718
1719 /* from var.c: */
1720 {
1721 char **envp;
1722 char ppid[32];
1723
1724 initvar();
1725 for (envp = environ ; *envp ; envp++) {
1726 if (strchr(*envp, '=')) {
1727 setvareq(*envp, VEXPORT|VTEXTFIXED);
1728 }
1729 }
1730
1731 snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
1732 setvar("PPID", ppid, 0);
1733 setpwd(0, 0);
1734 }
1735}
1736
1737/* PEOF (the end of file marker) */
1738
1739/*
1740 * The input line number. Input.c just defines this variable, and saves
1741 * and restores it when files are pushed and popped. The user of this
1742 * package must set its value.
1743 */
1744
1745static int pgetc(void);
1746static int pgetc2(void);
1747static int preadbuffer(void);
1748static void pungetc(void);
1749static void pushstring(char *, void *);
1750static void popstring(void);
1751static void setinputfile(const char *, int);
1752static void setinputfd(int, int);
1753static void setinputstring(char *);
1754static void popfile(void);
1755static void popallfiles(void);
1756static void closescript(void);
1757
1758
1759/* $NetBSD: jobs.h,v 1.17 2003/01/22 20:36:04 dsl Exp $ */
1760
1761
1762/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
1763#define FORK_FG 0
1764#define FORK_BG 1
1765#define FORK_NOJOB 2
1766
1767/* mode flags for showjob(s) */
1768#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
1769#define SHOW_PID 0x04 /* include process pid */
1770#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
1771
1772
1773/*
1774 * A job structure contains information about a job. A job is either a
1775 * single process or a set of processes contained in a pipeline. In the
1776 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
1777 * array of pids.
1778 */
1779
1780struct procstat {
1781 pid_t pid; /* process id */
1782 int status; /* last process status from wait() */
1783 char *cmd; /* text of command being run */
1784};
1785
1786struct job {
1787 struct procstat ps0; /* status of process */
1788 struct procstat *ps; /* status or processes when more than one */
1789#if JOBS
1790 int stopstatus; /* status of a stopped job */
1791#endif
1792 uint32_t
1793 nprocs: 16, /* number of processes */
1794 state: 8,
1795#define JOBRUNNING 0 /* at least one proc running */
1796#define JOBSTOPPED 1 /* all procs are stopped */
1797#define JOBDONE 2 /* all procs are completed */
1798#if JOBS
1799 sigint: 1, /* job was killed by SIGINT */
1800 jobctl: 1, /* job running under job control */
1801#endif
1802 waited: 1, /* true if this entry has been waited for */
1803 used: 1, /* true if this entry is in used */
1804 changed: 1; /* true if status has changed */
1805 struct job *prev_job; /* previous job */
1806};
1807
1808static pid_t backgndpid; /* pid of last background process */
1809static int job_warning; /* user was warned about stopped jobs */
1810#if JOBS
1811static int jobctl; /* true if doing job control */
1812#endif
1813
1814static struct job *makejob(union node *, int);
1815static int forkshell(struct job *, union node *, int);
1816static int waitforjob(struct job *);
1817static int stoppedjobs(void);
1818
1819#if ! JOBS
1820#define setjobctl(on) /* do nothing */
1821#else
1822static void setjobctl(int);
1823static void showjobs(FILE *, int);
1824#endif
1825
1826/* $NetBSD: main.h,v 1.9 2002/11/24 22:35:41 christos Exp $ */
1827
1828
1829/* pid of main shell */
1830static int rootpid;
1831/* true if we aren't a child of the main shell */
1832static int rootshell;
1833
1834static void readcmdfile(char *);
1835static void cmdloop(int);
1836
1837/* $NetBSD: memalloc.h,v 1.13 2003/01/22 20:36:04 dsl Exp $ */
1838
1839
1840struct stackmark {
1841 struct stack_block *stackp;
1842 char *stacknxt;
1843 size_t stacknleft;
1844 struct stackmark *marknext;
1845};
1846
1847/* minimum size of a block */
1848#define MINSIZE SHELL_ALIGN(504)
1849
1850struct stack_block {
1851 struct stack_block *prev;
1852 char space[MINSIZE];
1853};
1854
1855static struct stack_block stackbase;
1856static struct stack_block *stackp = &stackbase;
1857static struct stackmark *markp;
1858static char *stacknxt = stackbase.space;
1859static size_t stacknleft = MINSIZE;
1860static char *sstrend = stackbase.space + MINSIZE;
1861static int herefd = -1;
1862
1863
1864static pointer ckmalloc(size_t);
1865static pointer ckrealloc(pointer, size_t);
1866static char *savestr(const char *);
1867static pointer stalloc(size_t);
1868static void stunalloc(pointer);
1869static void setstackmark(struct stackmark *);
1870static void popstackmark(struct stackmark *);
1871static void growstackblock(void);
1872static void *growstackstr(void);
1873static char *makestrspace(size_t, char *);
1874static char *stnputs(const char *, size_t, char *);
1875static char *stputs(const char *, char *);
1876
1877
1878static inline char *_STPUTC(char c, char *p) {
1879 if (p == sstrend)
1880 p = growstackstr();
1881 *p++ = c;
1882 return p;
1883}
1884
1885#define stackblock() ((void *)stacknxt)
1886#define stackblocksize() stacknleft
1887#define STARTSTACKSTR(p) ((p) = stackblock())
1888#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
1889#define CHECKSTRSPACE(n, p) \
1890 ({ \
1891 char *q = (p); \
1892 size_t l = (n); \
1893 size_t m = sstrend - q; \
1894 if (l > m) \
1895 (p) = makestrspace(l, q); \
1896 0; \
1897 })
1898#define USTPUTC(c, p) (*p++ = (c))
1899#define STACKSTRNUL(p) ((p) == sstrend? (p = growstackstr(), *p = '\0') : (*p = '\0'))
1900#define STUNPUTC(p) (--p)
1901#define STTOPC(p) p[-1]
1902#define STADJUST(amount, p) (p += (amount))
1903
1904#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
1905#define ungrabstackstr(s, p) stunalloc((s))
1906#define stackstrend() ((void *)sstrend)
1907
1908#define ckfree(p) free((pointer)(p))
1909
1910/* $NetBSD: mystring.h,v 1.10 2002/11/24 22:35:42 christos Exp $ */
1911
1912
1913#define DOLATSTRLEN 4
1914
1915static char *prefix(const char *, const char *);
1916static int number(const char *);
1917static int is_number(const char *);
1918static char *single_quote(const char *);
1919static char *sstrdup(const char *);
1920
1921#define equal(s1, s2) (strcmp(s1, s2) == 0)
1922#define scopy(s1, s2) ((void)strcpy(s2, s1))
1923
1924/* $NetBSD: options.h,v 1.16 2003/01/22 20:36:04 dsl Exp $ */
1925
1926struct shparam {
1927 int nparam; /* # of positional parameters (without $0) */
1928 unsigned char malloc; /* if parameter list dynamically allocated */
1929 char **p; /* parameter list */
1930#ifdef CONFIG_ASH_GETOPTS
1931 int optind; /* next parameter to be processed by getopts */
1932 int optoff; /* used by getopts */
1933#endif
1934};
1935
1936
1937#define eflag optlist[0]
1938#define fflag optlist[1]
1939#define Iflag optlist[2]
1940#define iflag optlist[3]
1941#define mflag optlist[4]
1942#define nflag optlist[5]
1943#define sflag optlist[6]
1944#define xflag optlist[7]
1945#define vflag optlist[8]
1946#define Cflag optlist[9]
1947#define aflag optlist[10]
1948#define bflag optlist[11]
1949#define uflag optlist[12]
1950#define qflag optlist[13]
1951
1952#ifdef DEBUG
1953#define nolog optlist[14]
1954#define debug optlist[15]
1955#define NOPTS 16
1956#else
1957#define NOPTS 14
1958#endif
1959
1960/* $NetBSD: options.c,v 1.33 2003/01/22 20:36:04 dsl Exp $ */
1961
1962
1963static const char *const optletters_optnames[NOPTS] = {
1964 "e" "errexit",
1965 "f" "noglob",
1966 "I" "ignoreeof",
1967 "i" "interactive",
1968 "m" "monitor",
1969 "n" "noexec",
1970 "s" "stdin",
1971 "x" "xtrace",
1972 "v" "verbose",
1973 "C" "noclobber",
1974 "a" "allexport",
1975 "b" "notify",
1976 "u" "nounset",
1977 "q" "quietprofile",
1978#ifdef DEBUG
1979 "\0" "nolog",
1980 "\0" "debug",
1981#endif
1982};
1983
1984#define optletters(n) optletters_optnames[(n)][0]
1985#define optnames(n) (&optletters_optnames[(n)][1])
1986
1987
1988static char optlist[NOPTS];
1989
1990
1991static char *arg0; /* value of $0 */
1992static struct shparam shellparam; /* $@ current positional parameters */
1993static char **argptr; /* argument list for builtin commands */
1994static char *optionarg; /* set by nextopt (like getopt) */
1995static char *optptr; /* used by nextopt */
1996
1997static char *minusc; /* argument to -c option */
1998
1999
2000static void procargs(int, char **);
2001static void optschanged(void);
2002static void setparam(char **);
2003static void freeparam(volatile struct shparam *);
2004static int shiftcmd(int, char **);
2005static int setcmd(int, char **);
2006static int nextopt(const char *);
2007
2008/* $NetBSD: redir.h,v 1.14 2002/11/24 22:35:43 christos Exp $ */
2009
2010/* flags passed to redirect */
2011#define REDIR_PUSH 01 /* save previous values of file descriptors */
2012#define REDIR_SAVEFD2 03 /* set preverrout */
2013
2014union node;
2015static void redirect(union node *, int);
2016static void popredir(int);
2017static void clearredir(int);
2018static int copyfd(int, int);
2019static int redirectsafe(union node *, int);
2020
2021/* $NetBSD: show.h,v 1.6 2003/01/22 20:36:04 dsl Exp $ */
2022
2023
2024#ifdef DEBUG
2025static void showtree(union node *);
2026static void trace(const char *, ...);
2027static void tracev(const char *, va_list);
2028static void trargs(char **);
2029static void trputc(int);
2030static void trputs(const char *);
2031static void opentrace(void);
2032#endif
2033
2034/* $NetBSD: trap.h,v 1.16 2002/11/24 22:35:43 christos Exp $ */
2035
2036
2037/* trap handler commands */
2038static char *trap[NSIG];
2039/* current value of signal */
2040static char sigmode[NSIG - 1];
2041/* indicates specified signal received */
2042static char gotsig[NSIG - 1];
2043
2044static void clear_traps(void);
2045static void setsignal(int);
2046static void ignoresig(int);
2047static void onsig(int);
2048static void dotrap(void);
2049static void setinteractive(int);
2050static void exitshell(void) __attribute__((__noreturn__));
2051static int decode_signal(const char *, int);
2052
2053/*
2054 * This routine is called when an error or an interrupt occurs in an
2055 * interactive shell and control is returned to the main command loop.
2056 */
2057
2058static void
2059reset(void)
2060{
2061 /* from eval.c: */
2062 {
2063 evalskip = 0;
2064 loopnest = 0;
2065 funcnest = 0;
2066 }
2067
2068 /* from input.c: */
2069 {
2070 parselleft = parsenleft = 0; /* clear input buffer */
2071 popallfiles();
2072 }
2073
2074 /* from parser.c: */
2075 {
2076 tokpushback = 0;
2077 checkkwd = 0;
2078 }
2079
2080 /* from redir.c: */
2081 {
2082 clearredir(0);
2083 }
2084
2085}
2086
2087#ifdef CONFIG_ASH_ALIAS
2088static struct alias *atab[ATABSIZE];
2089
2090static void setalias(const char *, const char *);
2091static struct alias *freealias(struct alias *);
2092static struct alias **__lookupalias(const char *);
2093
2094static void
2095setalias(const char *name, const char *val)
2096{
2097 struct alias *ap, **app;
2098
2099 app = __lookupalias(name);
2100 ap = *app;
2101 INTOFF;
2102 if (ap) {
2103 if (!(ap->flag & ALIASINUSE)) {
2104 ckfree(ap->val);
2105 }
2106 ap->val = savestr(val);
2107 ap->flag &= ~ALIASDEAD;
2108 } else {
2109 /* not found */
2110 ap = ckmalloc(sizeof (struct alias));
2111 ap->name = savestr(name);
2112 ap->val = savestr(val);
2113 ap->flag = 0;
2114 ap->next = 0;
2115 *app = ap;
2116 }
2117 INTON;
2118}
2119
2120static int
2121unalias(const char *name)
2122{
2123 struct alias **app;
2124
2125 app = __lookupalias(name);
2126
2127 if (*app) {
2128 INTOFF;
2129 *app = freealias(*app);
2130 INTON;
2131 return (0);
2132 }
2133
2134 return (1);
2135}
2136
2137static void
2138rmaliases(void)
2139{
2140 struct alias *ap, **app;
2141 int i;
2142
2143 INTOFF;
2144 for (i = 0; i < ATABSIZE; i++) {
2145 app = &atab[i];
2146 for (ap = *app; ap; ap = *app) {
2147 *app = freealias(*app);
2148 if (ap == *app) {
2149 app = &ap->next;
2150 }
2151 }
2152 }
2153 INTON;
2154}
2155
2156static struct alias *
2157lookupalias(const char *name, int check)
2158{
2159 struct alias *ap = *__lookupalias(name);
2160
2161 if (check && ap && (ap->flag & ALIASINUSE))
2162 return (NULL);
2163 return (ap);
2164}
2165
2166/*
2167 * TODO - sort output
2168 */
2169static int
2170aliascmd(int argc, char **argv)
2171{
2172 char *n, *v;
2173 int ret = 0;
2174 struct alias *ap;
2175
2176 if (argc == 1) {
2177 int i;
2178
2179 for (i = 0; i < ATABSIZE; i++)
2180 for (ap = atab[i]; ap; ap = ap->next) {
2181 printalias(ap);
2182 }
2183 return (0);
2184 }
2185 while ((n = *++argv) != NULL) {
2186 if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
2187 if ((ap = *__lookupalias(n)) == NULL) {
2188 fprintf(stderr, "%s: %s not found\n", "alias", n);
2189 ret = 1;
2190 } else
2191 printalias(ap);
2192 } else {
2193 *v++ = '\0';
2194 setalias(n, v);
2195 }
2196 }
2197
2198 return (ret);
2199}
2200
2201static int
2202unaliascmd(int argc, char **argv)
2203{
2204 int i;
2205
2206 while ((i = nextopt("a")) != '\0') {
2207 if (i == 'a') {
2208 rmaliases();
2209 return (0);
2210 }
2211 }
2212 for (i = 0; *argptr; argptr++) {
2213 if (unalias(*argptr)) {
2214 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
2215 i = 1;
2216 }
2217 }
2218
2219 return (i);
2220}
2221
2222static struct alias *
2223freealias(struct alias *ap) {
2224 struct alias *next;
2225
2226 if (ap->flag & ALIASINUSE) {
2227 ap->flag |= ALIASDEAD;
2228 return ap;
2229 }
2230
2231 next = ap->next;
2232 ckfree(ap->name);
2233 ckfree(ap->val);
2234 ckfree(ap);
2235 return next;
2236}
2237
2238static void
2239printalias(const struct alias *ap) {
2240 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
2241}
2242
2243static struct alias **
2244__lookupalias(const char *name) {
2245 unsigned int hashval;
2246 struct alias **app;
2247 const char *p;
2248 unsigned int ch;
2249
2250 p = name;
2251
2252 ch = (unsigned char)*p;
2253 hashval = ch << 4;
2254 while (ch) {
2255 hashval += ch;
2256 ch = (unsigned char)*++p;
2257 }
2258 app = &atab[hashval % ATABSIZE];
2259
2260 for (; *app; app = &(*app)->next) {
2261 if (equal(name, (*app)->name)) {
2262 break;
2263 }
2264 }
2265
2266 return app;
2267}
2268#endif /* CONFIG_ASH_ALIAS */
2269
2270
2271/* $NetBSD: cd.c,v 1.30 2003/01/22 20:36:03 dsl Exp $ */
2272
2273/*
2274 * The cd and pwd commands.
2275 */
2276
2277#define CD_PHYSICAL 1
2278#define CD_PRINT 2
2279
2280static int docd(const char *, int);
2281static int cdopt(void);
2282
2283static char *curdir = nullstr; /* current working directory */
2284static char *physdir = nullstr; /* physical working directory */
2285
2286static int
2287cdopt(void)
2288{
2289 int flags = 0;
2290 int i, j;
2291
2292 j = 'L';
2293 while ((i = nextopt("LP"))) {
2294 if (i != j) {
2295 flags ^= CD_PHYSICAL;
2296 j = i;
2297 }
2298 }
2299
2300 return flags;
2301}
2302
2303static int
2304cdcmd(int argc, char **argv)
2305{
2306 const char *dest;
2307 const char *path;
2308 const char *p;
2309 char c;
2310 struct stat statb;
2311 int flags;
2312
2313 flags = cdopt();
2314 dest = *argptr;
2315 if (!dest)
2316 dest = bltinlookup(homestr);
2317 else if (dest[0] == '-' && dest[1] == '\0') {
2318 dest = bltinlookup("OLDPWD");
2319 if ( !dest ) goto out;
2320 flags |= CD_PRINT;
2321 goto step7;
2322 }
2323 if (!dest)
2324 dest = nullstr;
2325 if (*dest == '/')
2326 goto step7;
2327 if (*dest == '.') {
2328 c = dest[1];
2329dotdot:
2330 switch (c) {
2331 case '\0':
2332 case '/':
2333 goto step6;
2334 case '.':
2335 c = dest[2];
2336 if (c != '.')
2337 goto dotdot;
2338 }
2339 }
2340 if (!*dest)
2341 dest = ".";
2342 if (!(path = bltinlookup("CDPATH"))) {
2343step6:
2344step7:
2345 p = dest;
2346 goto docd;
2347 }
2348 do {
2349 c = *path;
2350 p = padvance(&path, dest);
2351 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2352 if (c && c != ':')
2353 flags |= CD_PRINT;
2354docd:
2355 if (!docd(p, flags))
2356 goto out;
2357 break;
2358 }
2359 } while (path);
2360 error("can't cd to %s", dest);
2361 /* NOTREACHED */
2362out:
2363 if (flags & CD_PRINT)
2364 out1fmt(snlfmt, curdir);
2365 return 0;
2366}
2367
2368
2369/*
2370 * Update curdir (the name of the current directory) in response to a
2371 * cd command.
2372 */
2373
2374static inline const char *
2375updatepwd(const char *dir)
2376{
2377 char *new;
2378 char *p;
2379 char *cdcomppath;
2380 const char *lim;
2381
2382 cdcomppath = sstrdup(dir);
2383 STARTSTACKSTR(new);
2384 if (*dir != '/') {
2385 if (curdir == nullstr)
2386 return 0;
2387 new = stputs(curdir, new);
2388 }
2389 new = makestrspace(strlen(dir) + 2, new);
2390 lim = stackblock() + 1;
2391 if (*dir != '/') {
2392 if (new[-1] != '/')
2393 USTPUTC('/', new);
2394 if (new > lim && *lim == '/')
2395 lim++;
2396 } else {
2397 USTPUTC('/', new);
2398 cdcomppath++;
2399 if (dir[1] == '/' && dir[2] != '/') {
2400 USTPUTC('/', new);
2401 cdcomppath++;
2402 lim++;
2403 }
2404 }
2405 p = strtok(cdcomppath, "/");
2406 while (p) {
2407 switch(*p) {
2408 case '.':
2409 if (p[1] == '.' && p[2] == '\0') {
2410 while (new > lim) {
2411 STUNPUTC(new);
2412 if (new[-1] == '/')
2413 break;
2414 }
2415 break;
2416 } else if (p[1] == '\0')
2417 break;
2418 /* fall through */
2419 default:
2420 new = stputs(p, new);
2421 USTPUTC('/', new);
2422 }
2423 p = strtok(0, "/");
2424 }
2425 if (new > lim)
2426 STUNPUTC(new);
2427 *new = 0;
2428 return stackblock();
2429}
2430
2431/*
2432 * Actually do the chdir. We also call hashcd to let the routines in exec.c
2433 * know that the current directory has changed.
2434 */
2435
2436static int
2437docd(const char *dest, int flags)
2438{
2439 const char *dir = 0;
2440 int err;
2441
2442 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2443
2444 INTOFF;
2445 if (!(flags & CD_PHYSICAL)) {
2446 dir = updatepwd(dest);
2447 if (dir)
2448 dest = dir;
2449 }
2450 err = chdir(dest);
2451 if (err)
2452 goto out;
2453 setpwd(dir, 1);
2454 hashcd();
2455out:
2456 INTON;
2457 return err;
2458}
2459
2460/*
2461 * Find out what the current directory is. If we already know the current
2462 * directory, this routine returns immediately.
2463 */
2464static inline char *
2465getpwd(void)
2466{
2467 char *dir = getcwd(0, 0);
2468 return dir ? dir : nullstr;
2469}
2470
2471static int
2472pwdcmd(int argc, char **argv)
2473{
2474 int flags;
2475 const char *dir = curdir;
2476
2477 flags = cdopt();
2478 if (flags) {
2479 if (physdir == nullstr)
2480 setpwd(dir, 0);
2481 dir = physdir;
2482 }
2483 out1fmt(snlfmt, dir);
2484 return 0;
2485}
2486
2487static void
2488setpwd(const char *val, int setold)
2489{
2490 char *oldcur, *dir;
2491
2492 oldcur = dir = curdir;
2493
2494 if (setold) {
2495 setvar("OLDPWD", oldcur, VEXPORT);
2496 }
2497 INTOFF;
2498 if (physdir != nullstr) {
2499 if (physdir != oldcur)
2500 free(physdir);
2501 physdir = nullstr;
2502 }
2503 if (oldcur == val || !val) {
2504 char *s = getpwd();
2505 physdir = s;
2506 if (!val)
2507 dir = s;
2508 } else
2509 dir = savestr(val);
2510 if (oldcur != dir && oldcur != nullstr) {
2511 free(oldcur);
2512 }
2513 curdir = dir;
2514 INTON;
2515 setvar("PWD", dir, VEXPORT);
2516}
2517
2518/* $NetBSD: error.c,v 1.30 2003/01/22 20:36:03 dsl Exp $ */
2519
2520/*
2521 * Errors and exceptions.
2522 */
2523
2524/*
2525 * Code to handle exceptions in C.
2526 */
2527
2528
2529
2530static void exverror(int, const char *, va_list)
2531 __attribute__((__noreturn__));
2532
2533/*
2534 * Called to raise an exception. Since C doesn't include exceptions, we
2535 * just do a longjmp to the exception handler. The type of exception is
2536 * stored in the global variable "exception".
2537 */
2538
2539static void
2540exraise(int e)
2541{
2542#ifdef DEBUG
2543 if (handler == NULL)
2544 abort();
2545#endif
2546 INTOFF;
2547
2548 exception = e;
2549 longjmp(handler->loc, 1);
2550}
2551
2552
2553/*
2554 * Called from trap.c when a SIGINT is received. (If the user specifies
2555 * that SIGINT is to be trapped or ignored using the trap builtin, then
2556 * this routine is not called.) Suppressint is nonzero when interrupts
2557 * are held using the INTOFF macro. (The test for iflag is just
2558 * defensive programming.)
2559 */
2560
2561static void
2562onint(void) {
2563 int i;
2564
2565 intpending = 0;
2566 sigsetmask(0);
2567 i = EXSIG;
2568 if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
2569 if (!(rootshell && iflag)) {
2570 signal(SIGINT, SIG_DFL);
2571 raise(SIGINT);
2572 }
2573 i = EXINT;
2574 }
2575 exraise(i);
2576 /* NOTREACHED */
2577}
2578
2579static void
2580exvwarning(const char *msg, va_list ap)
2581{
2582 FILE *errs;
2583 const char *name;
2584 const char *fmt;
2585
2586 errs = stderr;
2587 name = arg0;
2588 fmt = "%s: ";
2589 if (commandname) {
2590 name = commandname;
2591 fmt = "%s: %d: ";
2592 }
2593 fprintf(errs, fmt, name, startlinno);
2594 vfprintf(errs, msg, ap);
2595 outcslow('\n', errs);
2596}
2597
2598/*
2599 * Exverror is called to raise the error exception. If the second argument
2600 * is not NULL then error prints an error message using printf style
2601 * formatting. It then raises the error exception.
2602 */
2603static void
2604exverror(int cond, const char *msg, va_list ap)
2605{
2606#ifdef DEBUG
2607 if (msg) {
2608 TRACE(("exverror(%d, \"", cond));
2609 TRACEV((msg, ap));
2610 TRACE(("\") pid=%d\n", getpid()));
2611 } else
2612 TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
2613 if (msg)
2614#endif
2615 exvwarning(msg, ap);
2616
2617 flushall();
2618 exraise(cond);
2619 /* NOTREACHED */
2620}
2621
2622
2623static void
2624error(const char *msg, ...)
2625{
2626 va_list ap;
2627
2628 va_start(ap, msg);
2629 exverror(EXERROR, msg, ap);
2630 /* NOTREACHED */
2631 va_end(ap);
2632}
2633
2634
2635static void
2636exerror(int cond, const char *msg, ...)
2637{
2638 va_list ap;
2639
2640 va_start(ap, msg);
2641 exverror(cond, msg, ap);
2642 /* NOTREACHED */
2643 va_end(ap);
2644}
2645
2646/*
2647 * error/warning routines for external builtins
2648 */
2649
2650static void
2651sh_warnx(const char *fmt, ...)
2652{
2653 va_list ap;
2654
2655 va_start(ap, fmt);
2656 exvwarning(fmt, ap);
2657 va_end(ap);
2658}
2659
2660
2661/*
2662 * Return a string describing an error. The returned string may be a
2663 * pointer to a static buffer that will be overwritten on the next call.
2664 * Action describes the operation that got the error.
2665 */
2666
2667static const char *
2668errmsg(int e, const char *em)
2669{
2670 if(e == ENOENT || e == ENOTDIR) {
2671
2672 return em;
2673 }
2674 return strerror(e);
2675}
2676
2677
2678/* $NetBSD: eval.c,v 1.71 2003/01/23 03:33:16 rafal Exp $ */
2679
2680/*
2681 * Evaluate a command.
2682 */
2683
2684/* flags in argument to evaltree */
2685#define EV_EXIT 01 /* exit after evaluating tree */
2686#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
2687#define EV_BACKCMD 04 /* command executing within back quotes */
2688
2689
2690static void evalloop(union node *, int);
2691static void evalfor(union node *, int);
2692static void evalcase(union node *, int);
2693static void evalsubshell(union node *, int);
2694static void expredir(union node *);
2695static void evalpipe(union node *, int);
2696static void evalcommand(union node *, int);
2697static int evalbltin(const struct builtincmd *, int, char **);
2698static int evalfun(struct funcnode *, int, char **, int);
2699static void prehash(union node *);
2700static int bltincmd(int, char **);
2701
2702
2703static const struct builtincmd bltin = {
2704 "\0\0", bltincmd
2705};
2706
2707
2708/*
2709 * Called to reset things after an exception.
2710 */
2711
2712/*
2713 * The eval command.
2714 */
2715
2716static int
2717evalcmd(int argc, char **argv)
2718{
2719 char *p;
2720 char *concat;
2721 char **ap;
2722
2723 if (argc > 1) {
2724 p = argv[1];
2725 if (argc > 2) {
2726 STARTSTACKSTR(concat);
2727 ap = argv + 2;
2728 for (;;) {
2729 concat = stputs(p, concat);
2730 if ((p = *ap++) == NULL)
2731 break;
2732 STPUTC(' ', concat);
2733 }
2734 STPUTC('\0', concat);
2735 p = grabstackstr(concat);
2736 }
2737 evalstring(p);
2738 }
2739 return exitstatus;
2740}
2741
2742
2743/*
2744 * Execute a command or commands contained in a string.
2745 */
2746
2747static void
2748evalstring(char *s)
2749{
2750 union node *n;
2751 struct stackmark smark;
2752
2753 setstackmark(&smark);
2754 setinputstring(s);
2755
2756 while ((n = parsecmd(0)) != NEOF) {
2757 evaltree(n, 0);
2758 popstackmark(&smark);
2759 if (evalskip)
2760 break;
2761 }
2762 popfile();
2763 popstackmark(&smark);
2764}
2765
2766
2767
2768/*
2769 * Evaluate a parse tree. The value is left in the global variable
2770 * exitstatus.
2771 */
2772
2773static void
2774evaltree(union node *n, int flags)
2775{
2776 int checkexit = 0;
2777 void (*evalfn)(union node *, int);
2778 unsigned isor;
2779 int status;
2780 if (n == NULL) {
2781 TRACE(("evaltree(NULL) called\n"));
2782 goto out;
2783 }
2784 TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
2785 getpid(), n, n->type, flags));
2786 switch (n->type) {
2787 default:
2788#ifdef DEBUG
2789 out1fmt("Node type = %d\n", n->type);
2790 fflush(stdout);
2791 break;
2792#endif
2793 case NNOT:
2794 evaltree(n->nnot.com, EV_TESTED);
2795 status = !exitstatus;
2796 goto setstatus;
2797 case NREDIR:
2798 expredir(n->nredir.redirect);
2799 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
2800 if (!status) {
2801 evaltree(n->nredir.n, flags & EV_TESTED);
2802 status = exitstatus;
2803 }
2804 popredir(0);
2805 goto setstatus;
2806 case NCMD:
2807 evalfn = evalcommand;
2808checkexit:
2809 if (eflag && !(flags & EV_TESTED))
2810 checkexit = ~0;
2811 goto calleval;
2812 case NFOR:
2813 evalfn = evalfor;
2814 goto calleval;
2815 case NWHILE:
2816 case NUNTIL:
2817 evalfn = evalloop;
2818 goto calleval;
2819 case NSUBSHELL:
2820 case NBACKGND:
2821 evalfn = evalsubshell;
2822 goto calleval;
2823 case NPIPE:
2824 evalfn = evalpipe;
2825 goto checkexit;
2826 case NCASE:
2827 evalfn = evalcase;
2828 goto calleval;
2829 case NAND:
2830 case NOR:
2831 case NSEMI:
2832#if NAND + 1 != NOR
2833#error NAND + 1 != NOR
2834#endif
2835#if NOR + 1 != NSEMI
2836#error NOR + 1 != NSEMI
2837#endif
2838 isor = n->type - NAND;
2839 evaltree(
2840 n->nbinary.ch1,
2841 (flags | ((isor >> 1) - 1)) & EV_TESTED
2842 );
2843 if (!exitstatus == isor)
2844 break;
2845 if (!evalskip) {
2846 n = n->nbinary.ch2;
2847evaln:
2848 evalfn = evaltree;
2849calleval:
2850 evalfn(n, flags);
2851 break;
2852 }
2853 break;
2854 case NIF:
2855 evaltree(n->nif.test, EV_TESTED);
2856 if (evalskip)
2857 break;
2858 if (exitstatus == 0) {
2859 n = n->nif.ifpart;
2860 goto evaln;
2861 } else if (n->nif.elsepart) {
2862 n = n->nif.elsepart;
2863 goto evaln;
2864 }
2865 goto success;
2866 case NDEFUN:
2867 defun(n->narg.text, n->narg.next);
2868success:
2869 status = 0;
2870setstatus:
2871 exitstatus = status;
2872 break;
2873 }
2874out:
2875 if (pendingsigs)
2876 dotrap();
2877 if (flags & EV_EXIT || checkexit & exitstatus)
2878 exraise(EXEXIT);
2879}
2880
2881
2882#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
2883static
2884#endif
2885void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
2886
2887
2888static void
2889evalloop(union node *n, int flags)
2890{
2891 int status;
2892
2893 loopnest++;
2894 status = 0;
2895 flags &= EV_TESTED;
2896 for (;;) {
2897 int i;
2898
2899 evaltree(n->nbinary.ch1, EV_TESTED);
2900 if (evalskip) {
2901skipping: if (evalskip == SKIPCONT && --skipcount <= 0) {
2902 evalskip = 0;
2903 continue;
2904 }
2905 if (evalskip == SKIPBREAK && --skipcount <= 0)
2906 evalskip = 0;
2907 break;
2908 }
2909 i = exitstatus;
2910 if (n->type != NWHILE)
2911 i = !i;
2912 if (i != 0)
2913 break;
2914 evaltree(n->nbinary.ch2, flags);
2915 status = exitstatus;
2916 if (evalskip)
2917 goto skipping;
2918 }
2919 loopnest--;
2920 exitstatus = status;
2921}
2922
2923
2924
2925static void
2926evalfor(union node *n, int flags)
2927{
2928 struct arglist arglist;
2929 union node *argp;
2930 struct strlist *sp;
2931 struct stackmark smark;
2932
2933 setstackmark(&smark);
2934 arglist.lastp = &arglist.list;
2935 for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
2936 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
2937 /* XXX */
2938 if (evalskip)
2939 goto out;
2940 }
2941 *arglist.lastp = NULL;
2942
2943 exitstatus = 0;
2944 loopnest++;
2945 flags &= EV_TESTED;
2946 for (sp = arglist.list ; sp ; sp = sp->next) {
2947 setvar(n->nfor.var, sp->text, 0);
2948 evaltree(n->nfor.body, flags);
2949 if (evalskip) {
2950 if (evalskip == SKIPCONT && --skipcount <= 0) {
2951 evalskip = 0;
2952 continue;
2953 }
2954 if (evalskip == SKIPBREAK && --skipcount <= 0)
2955 evalskip = 0;
2956 break;
2957 }
2958 }
2959 loopnest--;
2960out:
2961 popstackmark(&smark);
2962}
2963
2964
2965
2966static void
2967evalcase(union node *n, int flags)
2968{
2969 union node *cp;
2970 union node *patp;
2971 struct arglist arglist;
2972 struct stackmark smark;
2973
2974 setstackmark(&smark);
2975 arglist.lastp = &arglist.list;
2976 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
2977 exitstatus = 0;
2978 for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
2979 for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
2980 if (casematch(patp, arglist.list->text)) {
2981 if (evalskip == 0) {
2982 evaltree(cp->nclist.body, flags);
2983 }
2984 goto out;
2985 }
2986 }
2987 }
2988out:
2989 popstackmark(&smark);
2990}
2991
2992
2993
2994/*
2995 * Kick off a subshell to evaluate a tree.
2996 */
2997
2998static void
2999evalsubshell(union node *n, int flags)
3000{
3001 struct job *jp;
3002 int backgnd = (n->type == NBACKGND);
3003 int status;
3004
3005 expredir(n->nredir.redirect);
3006 if (!backgnd && flags & EV_EXIT && !trap[0])
3007 goto nofork;
3008 INTOFF;
3009 jp = makejob(n, 1);
3010 if (forkshell(jp, n, backgnd) == 0) {
3011 INTON;
3012 flags |= EV_EXIT;
3013 if (backgnd)
3014 flags &=~ EV_TESTED;
3015nofork:
3016 redirect(n->nredir.redirect, 0);
3017 evaltreenr(n->nredir.n, flags);
3018 /* never returns */
3019 }
3020 status = 0;
3021 if (! backgnd)
3022 status = waitforjob(jp);
3023 exitstatus = status;
3024 INTON;
3025}
3026
3027
3028
3029/*
3030 * Compute the names of the files in a redirection list.
3031 */
3032
3033static void
3034expredir(union node *n)
3035{
3036 union node *redir;
3037
3038 for (redir = n ; redir ; redir = redir->nfile.next) {
3039 struct arglist fn;
3040 fn.lastp = &fn.list;
3041 switch (redir->type) {
3042 case NFROMTO:
3043 case NFROM:
3044 case NTO:
3045 case NCLOBBER:
3046 case NAPPEND:
3047 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
3048 redir->nfile.expfname = fn.list->text;
3049 break;
3050 case NFROMFD:
3051 case NTOFD:
3052 if (redir->ndup.vname) {
3053 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
3054 fixredir(redir, fn.list->text, 1);
3055 }
3056 break;
3057 }
3058 }
3059}
3060
3061
3062
3063/*
3064 * Evaluate a pipeline. All the processes in the pipeline are children
3065 * of the process creating the pipeline. (This differs from some versions
3066 * of the shell, which make the last process in a pipeline the parent
3067 * of all the rest.)
3068 */
3069
3070static void
3071evalpipe(union node *n, int flags)
3072{
3073 struct job *jp;
3074 struct nodelist *lp;
3075 int pipelen;
3076 int prevfd;
3077 int pip[2];
3078
3079 TRACE(("evalpipe(0x%lx) called\n", (long)n));
3080 pipelen = 0;
3081 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
3082 pipelen++;
3083 flags |= EV_EXIT;
3084 INTOFF;
3085 jp = makejob(n, pipelen);
3086 prevfd = -1;
3087 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
3088 prehash(lp->n);
3089 pip[1] = -1;
3090 if (lp->next) {
3091 if (pipe(pip) < 0) {
3092 close(prevfd);
3093 error("Pipe call failed");
3094 }
3095 }
3096 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
3097 INTON;
3098 if (pip[1] >= 0) {
3099 close(pip[0]);
3100 }
3101 if (prevfd > 0) {
3102 dup2(prevfd, 0);
3103 close(prevfd);
3104 }
3105 if (pip[1] > 1) {
3106 dup2(pip[1], 1);
3107 close(pip[1]);
3108 }
3109 evaltreenr(lp->n, flags);
3110 /* never returns */
3111 }
3112 if (prevfd >= 0)
3113 close(prevfd);
3114 prevfd = pip[0];
3115 close(pip[1]);
3116 }
3117 if (n->npipe.backgnd == 0) {
3118 exitstatus = waitforjob(jp);
3119 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
3120 }
3121 INTON;
3122}
3123
3124
3125
3126/*
3127 * Execute a command inside back quotes. If it's a builtin command, we
3128 * want to save its output in a block obtained from malloc. Otherwise
3129 * we fork off a subprocess and get the output of the command via a pipe.
3130 * Should be called with interrupts off.
3131 */
3132
3133static void
3134evalbackcmd(union node *n, struct backcmd *result)
3135{
3136 int saveherefd;
3137
3138 result->fd = -1;
3139 result->buf = NULL;
3140 result->nleft = 0;
3141 result->jp = NULL;
3142 if (n == NULL) {
3143 goto out;
3144 }
3145
3146 saveherefd = herefd;
3147 herefd = -1;
3148
3149 {
3150 int pip[2];
3151 struct job *jp;
3152
3153 if (pipe(pip) < 0)
3154 error("Pipe call failed");
3155 jp = makejob(n, 1);
3156 if (forkshell(jp, n, FORK_NOJOB) == 0) {
3157 FORCEINTON;
3158 close(pip[0]);
3159 if (pip[1] != 1) {
3160 close(1);
3161 copyfd(pip[1], 1);
3162 close(pip[1]);
3163 }
3164 eflag = 0;
3165 evaltreenr(n, EV_EXIT);
3166 /* NOTREACHED */
3167 }
3168 close(pip[1]);
3169 result->fd = pip[0];
3170 result->jp = jp;
3171 }
3172 herefd = saveherefd;
3173out:
3174 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
3175 result->fd, result->buf, result->nleft, result->jp));
3176}
3177
3178#ifdef CONFIG_ASH_CMDCMD
3179static inline char **
3180parse_command_args(char **argv, const char **path)
3181{
3182 char *cp, c;
3183
3184 for (;;) {
3185 cp = *++argv;
3186 if (!cp)
3187 return 0;
3188 if (*cp++ != '-')
3189 break;
3190 if (!(c = *cp++))
3191 break;
3192 if (c == '-' && !*cp) {
3193 argv++;
3194 break;
3195 }
3196 do {
3197 switch (c) {
3198 case 'p':
3199 *path = defpath;
3200 break;
3201 default:
3202 /* run 'typecmd' for other options */
3203 return 0;
3204 }
3205 } while ((c = *cp++));
3206 }
3207 return argv;
3208}
3209#endif
3210
3211
3212
3213/*
3214 * Execute a simple command.
3215 */
3216
3217static void
3218evalcommand(union node *cmd, int flags)
3219{
3220 struct stackmark smark;
3221 union node *argp;
3222 struct arglist arglist;
3223 struct arglist varlist;
3224 char **argv;
3225 int argc;
3226 const struct strlist *sp;
3227 struct cmdentry cmdentry;
3228 struct job *jp;
3229 char *lastarg;
3230 const char *path;
3231 int spclbltin;
3232 int cmd_is_exec;
3233 int status;
3234 char **nargv;
3235
3236 /* First expand the arguments. */
3237 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
3238 setstackmark(&smark);
3239 back_exitstatus = 0;
3240
3241 cmdentry.cmdtype = CMDBUILTIN;
3242 cmdentry.u.cmd = &bltin;
3243 varlist.lastp = &varlist.list;
3244 *varlist.lastp = NULL;
3245 arglist.lastp = &arglist.list;
3246 *arglist.lastp = NULL;
3247
3248 argc = 0;
3249 for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
3250 struct strlist **spp;
3251
3252 spp = arglist.lastp;
3253 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
3254 for (sp = *spp; sp; sp = sp->next)
3255 argc++;
3256 }
3257
3258 argv = nargv = stalloc(sizeof (char *) * (argc + 1));
3259 for (sp = arglist.list ; sp ; sp = sp->next) {
3260 TRACE(("evalcommand arg: %s\n", sp->text));
3261 *nargv++ = sp->text;
3262 }
3263 *nargv = NULL;
3264
3265 lastarg = NULL;
3266 if (iflag && funcnest == 0 && argc > 0)
3267 lastarg = nargv[-1];
3268
3269 preverrout_fd = 2;
3270 expredir(cmd->ncmd.redirect);
3271 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2);
3272
3273 path = vpath.text;
3274 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
3275 struct strlist **spp;
3276 char *p;
3277
3278 spp = varlist.lastp;
3279 expandarg(argp, &varlist, EXP_VARTILDE);
3280
3281 /*
3282 * Modify the command lookup path, if a PATH= assignment
3283 * is present
3284 */
3285 p = (*spp)->text;
3286 if (varequal(p, path))
3287 path = p;
3288 }
3289
3290 /* Print the command if xflag is set. */
3291 if (xflag) {
3292 int n;
3293 const char *p = " %s";
3294
3295 p++;
3296 dprintf(preverrout_fd, p, ps4val());
3297
3298 sp = varlist.list;
3299 for(n = 0; n < 2; n++) {
3300 while (sp) {
3301 dprintf(preverrout_fd, p, sp->text);
3302 sp = sp->next;
3303 if(*p == '%') {
3304 p--;
3305 }
3306 }
3307 sp = arglist.list;
3308 }
3309 bb_full_write(preverrout_fd, "\n", 1);
3310 }
3311
3312 cmd_is_exec = 0;
3313 spclbltin = -1;
3314
3315 /* Now locate the command. */
3316 if (argc) {
3317 const char *oldpath;
3318 int cmd_flag = DO_ERR;
3319
3320 path += 5;
3321 oldpath = path;
3322 for (;;) {
3323 find_command(argv[0], &cmdentry, cmd_flag, path);
3324 if (cmdentry.cmdtype == CMDUNKNOWN) {
3325 status = 127;
3326 flusherr();
3327 goto bail;
3328 }
3329
3330 /* implement bltin and command here */
3331 if (cmdentry.cmdtype != CMDBUILTIN)
3332 break;
3333 if (spclbltin < 0)
3334 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
3335 if (cmdentry.u.cmd == EXECCMD)
3336 cmd_is_exec++;
3337#ifdef CONFIG_ASH_CMDCMD
3338 if (cmdentry.u.cmd == COMMANDCMD) {
3339
3340 path = oldpath;
3341 nargv = parse_command_args(argv, &path);
3342 if (!nargv)
3343 break;
3344 argc -= nargv - argv;
3345 argv = nargv;
3346 cmd_flag |= DO_NOFUNC;
3347 } else
3348#endif
3349 break;
3350 }
3351 }
3352
3353 if (status) {
3354 /* We have a redirection error. */
3355 if (spclbltin > 0)
3356 exraise(EXERROR);
3357bail:
3358 exitstatus = status;
3359 goto out;
3360 }
3361
3362 /* Execute the command. */
3363 switch (cmdentry.cmdtype) {
3364 default:
3365 /* Fork off a child process if necessary. */
3366 if (!(flags & EV_EXIT) || trap[0]) {
3367 INTOFF;
3368 jp = makejob(cmd, 1);
3369 if (forkshell(jp, cmd, FORK_FG) != 0) {
3370 exitstatus = waitforjob(jp);
3371 INTON;
3372 break;
3373 }
3374 FORCEINTON;
3375 }
3376 listsetvar(varlist.list, VEXPORT|VSTACK);
3377 shellexec(argv, path, cmdentry.u.index);
3378 /* NOTREACHED */
3379
3380 case CMDBUILTIN:
3381 cmdenviron = varlist.list;
3382 if (cmdenviron) {
3383 struct strlist *list = cmdenviron;
3384 int i = VNOSET;
3385 if (spclbltin > 0 || argc == 0) {
3386 i = 0;
3387 if (cmd_is_exec && argc > 1)
3388 i = VEXPORT;
3389 }
3390 listsetvar(list, i);
3391 }
3392 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
3393 int exit_status;
3394 int i, j;
3395
3396 i = exception;
3397 if (i == EXEXIT)
3398 goto raise;
3399
3400 exit_status = 2;
3401 j = 0;
3402 if (i == EXINT)
3403 j = SIGINT;
3404 if (i == EXSIG)
3405 j = pendingsigs;
3406 if (j)
3407 exit_status = j + 128;
3408 exitstatus = exit_status;
3409
3410 if (i == EXINT || spclbltin > 0) {
3411raise:
3412 longjmp(handler->loc, 1);
3413 }
3414 FORCEINTON;
3415 }
3416 break;
3417
3418 case CMDFUNCTION:
3419 listsetvar(varlist.list, 0);
3420 if (evalfun(cmdentry.u.func, argc, argv, flags))
3421 goto raise;
3422 break;
3423 }
3424
3425out:
3426 popredir(cmd_is_exec);
3427 if (lastarg)
3428 /* dsl: I think this is intended to be used to support
3429 * '_' in 'vi' command mode during line editing...
3430 * However I implemented that within libedit itself.
3431 */
3432 setvar("_", lastarg, 0);
3433 popstackmark(&smark);
3434}
3435
3436static int
3437evalbltin(const struct builtincmd *cmd, int argc, char **argv) {
3438 char *volatile savecmdname;
3439 struct jmploc *volatile savehandler;
3440 struct jmploc jmploc;
3441 int i;
3442
3443 savecmdname = commandname;
3444 if ((i = setjmp(jmploc.loc)))
3445 goto cmddone;
3446 savehandler = handler;
3447 handler = &jmploc;
3448 commandname = argv[0];
3449 argptr = argv + 1;
3450 optptr = NULL; /* initialize nextopt */
3451 exitstatus = (*cmd->builtin)(argc, argv);
3452 flushall();
3453cmddone:
3454 exitstatus |= ferror(stdout);
3455 commandname = savecmdname;
3456 exsig = 0;
3457 handler = savehandler;
3458
3459 return i;
3460}
3461
3462static int
3463evalfun(struct funcnode *func, int argc, char **argv, int flags)
3464{
3465 volatile struct shparam saveparam;
3466 struct localvar *volatile savelocalvars;
3467 struct jmploc *volatile savehandler;
3468 struct jmploc jmploc;
3469 int e;
3470
3471 saveparam = shellparam;
3472 savelocalvars = localvars;
3473 if ((e = setjmp(jmploc.loc))) {
3474 goto funcdone;
3475 }
3476 INTOFF;
3477 savehandler = handler;
3478 handler = &jmploc;
3479 localvars = NULL;
3480 shellparam.malloc = 0;
3481 func->count++;
3482 INTON;
3483 shellparam.nparam = argc - 1;
3484 shellparam.p = argv + 1;
3485#ifdef CONFIG_ASH_GETOPTS
3486 shellparam.optind = 1;
3487 shellparam.optoff = -1;
3488#endif
3489 funcnest++;
3490 evaltree(&func->n, flags & EV_TESTED);
3491 funcnest--;
3492funcdone:
3493 INTOFF;
3494 freefunc(func);
3495 poplocalvars();
3496 localvars = savelocalvars;
3497 freeparam(&shellparam);
3498 shellparam = saveparam;
3499 handler = savehandler;
3500 INTON;
3501 if (evalskip == SKIPFUNC) {
3502 evalskip = 0;
3503 skipcount = 0;
3504 }
3505 return e;
3506}
3507
3508
3509static inline int
3510goodname(const char *p)
3511{
3512 return !*endofname(p);
3513}
3514
3515/*
3516 * Search for a command. This is called before we fork so that the
3517 * location of the command will be available in the parent as well as
3518 * the child. The check for "goodname" is an overly conservative
3519 * check that the name will not be subject to expansion.
3520 */
3521
3522static void
3523prehash(union node *n)
3524{
3525 struct cmdentry entry;
3526
3527 if (n->type == NCMD && n->ncmd.args)
3528 if (goodname(n->ncmd.args->narg.text))
3529 find_command(n->ncmd.args->narg.text, &entry, 0,
3530 pathval());
3531}
3532
3533
3534
3535/*
3536 * Builtin commands. Builtin commands whose functions are closely
3537 * tied to evaluation are implemented here.
3538 */
3539
3540/*
3541 * No command given.
3542 */
3543
3544static int
3545bltincmd(int argc, char **argv)
3546{
3547 /*
3548 * Preserve exitstatus of a previous possible redirection
3549 * as POSIX mandates
3550 */
3551 return back_exitstatus;
3552}
3553
3554
3555/*
3556 * Handle break and continue commands. Break, continue, and return are
3557 * all handled by setting the evalskip flag. The evaluation routines
3558 * above all check this flag, and if it is set they start skipping
3559 * commands rather than executing them. The variable skipcount is
3560 * the number of loops to break/continue, or the number of function
3561 * levels to return. (The latter is always 1.) It should probably
3562 * be an error to break out of more loops than exist, but it isn't
3563 * in the standard shell so we don't make it one here.
3564 */
3565
3566static int
3567breakcmd(int argc, char **argv)
3568{
3569 int n = argc > 1 ? number(argv[1]) : 1;
3570
3571 if (n <= 0)
3572 error(illnum, argv[1]);
3573 if (n > loopnest)
3574 n = loopnest;
3575 if (n > 0) {
3576 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
3577 skipcount = n;
3578 }
3579 return 0;
3580}
3581
3582
3583/*
3584 * The return command.
3585 */
3586
3587static int
3588returncmd(int argc, char **argv)
3589{
3590 int ret = argc > 1 ? number(argv[1]) : exitstatus;
3591
3592 if (funcnest) {
3593 evalskip = SKIPFUNC;
3594 skipcount = 1;
3595 return ret;
3596 }
3597 else {
3598 /* Do what ksh does; skip the rest of the file */
3599 evalskip = SKIPFILE;
3600 skipcount = 1;
3601 return ret;
3602 }
3603}
3604
3605
3606static int
3607falsecmd(int argc, char **argv)
3608{
3609 return 1;
3610}
3611
3612
3613static int
3614truecmd(int argc, char **argv)
3615{
3616 return 0;
3617}
3618
3619
3620static int
3621execcmd(int argc, char **argv)
3622{
3623 if (argc > 1) {
3624 iflag = 0; /* exit on error */
3625 mflag = 0;
3626 optschanged();
3627 shellexec(argv + 1, pathval(), 0);
3628 }
3629 return 0;
3630}
3631
3632
3633/* $NetBSD: exec.c,v 1.35 2003/01/22 20:36:04 dsl Exp $ */
3634
3635/*
3636 * When commands are first encountered, they are entered in a hash table.
3637 * This ensures that a full path search will not have to be done for them
3638 * on each invocation.
3639 *
3640 * We should investigate converting to a linear search, even though that
3641 * would make the command name "hash" a misnomer.
3642 */
3643
3644#define CMDTABLESIZE 31 /* should be prime */
3645#define ARB 1 /* actual size determined at run time */
3646
3647
3648
3649struct tblentry {
3650 struct tblentry *next; /* next entry in hash chain */
3651 union param param; /* definition of builtin function */
3652 short cmdtype; /* index identifying command */
3653 char rehash; /* if set, cd done since entry created */
3654 char cmdname[ARB]; /* name of command */
3655};
3656
3657
3658static struct tblentry *cmdtable[CMDTABLESIZE];
3659static int builtinloc = -1; /* index in path of %builtin, or -1 */
3660
3661
3662static void tryexec(char *, char **, char **);
3663static void clearcmdentry(int);
3664static struct tblentry *cmdlookup(const char *, int);
3665static void delete_cmd_entry(void);
3666
3667
3668/*
3669 * Exec a program. Never returns. If you change this routine, you may
3670 * have to change the find_command routine as well.
3671 */
3672
3673static void
3674shellexec(char **argv, const char *path, int idx)
3675{
3676 char *cmdname;
3677 int e;
3678 char **envp;
3679
3680 clearredir(1);
3681 envp = environment();
3682 if (strchr(argv[0], '/') != NULL
3683#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
3684 || find_applet_by_name(argv[0])
3685#endif
3686 ) {
3687 tryexec(argv[0], argv, envp);
3688 e = errno;
3689 } else {
3690 e = ENOENT;
3691 while ((cmdname = padvance(&path, argv[0])) != NULL) {
3692 if (--idx < 0 && pathopt == NULL) {
3693 tryexec(cmdname, argv, envp);
3694 if (errno != ENOENT && errno != ENOTDIR)
3695 e = errno;
3696 }
3697 stunalloc(cmdname);
3698 }
3699 }
3700
3701 /* Map to POSIX errors */
3702 switch (e) {
3703 case EACCES:
3704 exerrno = 126;
3705 break;
3706 case ENOENT:
3707 exerrno = 127;
3708 break;
3709 default:
3710 exerrno = 2;
3711 break;
3712 }
3713 TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
3714 argv[0], e, suppressint ));
3715 exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
3716 /* NOTREACHED */
3717}
3718
3719
3720static void
3721tryexec(char *cmd, char **argv, char **envp)
3722{
3723 int repeated = 0;
3724#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
3725 int flg_bb = 0;
3726 char *name = cmd;
3727
3728 if(strchr(name, '/') == NULL && find_applet_by_name(name) != NULL) {
3729 flg_bb = 1;
3730 }
3731 if(flg_bb) {
3732 char **ap;
3733 char **new;
3734
3735 *argv = name;
3736 if(strcmp(name, "busybox")) {
3737 for (ap = argv; *ap; ap++);
3738 ap = new = xmalloc((ap - argv + 2) * sizeof(char *));
3739 *ap++ = cmd = "/bin/busybox";
3740 while ((*ap++ = *argv++));
3741 argv = new;
3742 repeated++;
3743 } else {
3744 cmd = "/bin/busybox";
3745 }
3746 }
3747#endif
3748
3749repeat:
3750#ifdef SYSV
3751 do {
3752 execve(cmd, argv, envp);
3753 } while (errno == EINTR);
3754#else
3755 execve(cmd, argv, envp);
3756#endif
3757 if (repeated++) {
3758 ckfree(argv);
3759 } else if (errno == ENOEXEC) {
3760 char **ap;
3761 char **new;
3762
3763 for (ap = argv; *ap; ap++)
3764 ;
3765 ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
3766 ap[1] = cmd;
3767 *ap = cmd = (char *)DEFAULT_SHELL;
3768 ap += 2;
3769 argv++;
3770 while ((*ap++ = *argv++))
3771 ;
3772 argv = new;
3773 goto repeat;
3774 }
3775}
3776
3777
3778
3779/*
3780 * Do a path search. The variable path (passed by reference) should be
3781 * set to the start of the path before the first call; padvance will update
3782 * this value as it proceeds. Successive calls to padvance will return
3783 * the possible path expansions in sequence. If an option (indicated by
3784 * a percent sign) appears in the path entry then the global variable
3785 * pathopt will be set to point to it; otherwise pathopt will be set to
3786 * NULL.
3787 */
3788
3789static char *
3790padvance(const char **path, const char *name)
3791{
3792 const char *p;
3793 char *q;
3794 const char *start;
3795 size_t len;
3796
3797 if (*path == NULL)
3798 return NULL;
3799 start = *path;
3800 for (p = start ; *p && *p != ':' && *p != '%' ; p++);
3801 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
3802 while (stackblocksize() < len)
3803 growstackblock();
3804 q = stackblock();
3805 if (p != start) {
3806 memcpy(q, start, p - start);
3807 q += p - start;
3808 *q++ = '/';
3809 }
3810 strcpy(q, name);
3811 pathopt = NULL;
3812 if (*p == '%') {
3813 pathopt = ++p;
3814 while (*p && *p != ':') p++;
3815 }
3816 if (*p == ':')
3817 *path = p + 1;
3818 else
3819 *path = NULL;
3820 return stalloc(len);
3821}
3822
3823
3824/*** Command hashing code ***/
3825
3826static void
3827printentry(struct tblentry *cmdp)
3828{
3829 int idx;
3830 const char *path;
3831 char *name;
3832
3833 idx = cmdp->param.index;
3834 path = pathval();
3835 do {
3836 name = padvance(&path, cmdp->cmdname);
3837 stunalloc(name);
3838 } while (--idx >= 0);
3839 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
3840}
3841
3842
3843static int
3844hashcmd(int argc, char **argv)
3845{
3846 struct tblentry **pp;
3847 struct tblentry *cmdp;
3848 int c;
3849 struct cmdentry entry;
3850 char *name;
3851
3852 while ((c = nextopt("r")) != '\0') {
3853 clearcmdentry(0);
3854 return 0;
3855 }
3856 if (*argptr == NULL) {
3857 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
3858 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
3859 if (cmdp->cmdtype == CMDNORMAL)
3860 printentry(cmdp);
3861 }
3862 }
3863 return 0;
3864 }
3865 c = 0;
3866 while ((name = *argptr) != NULL) {
3867 if ((cmdp = cmdlookup(name, 0)) != NULL
3868 && (cmdp->cmdtype == CMDNORMAL
3869 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
3870 delete_cmd_entry();
3871 find_command(name, &entry, DO_ERR, pathval());
3872 if (entry.cmdtype == CMDUNKNOWN)
3873 c = 1;
3874 argptr++;
3875 }
3876 return c;
3877}
3878
3879
3880/*
3881 * Resolve a command name. If you change this routine, you may have to
3882 * change the shellexec routine as well.
3883 */
3884
3885static void
3886find_command(char *name, struct cmdentry *entry, int act, const char *path)
3887{
3888 struct tblentry *cmdp;
3889 int idx;
3890 int prev;
3891 char *fullname;
3892 struct stat statb;
3893 int e;
3894 int updatetbl;
3895 struct builtincmd *bcmd;
3896
3897 /* If name contains a slash, don't use PATH or hash table */
3898 if (strchr(name, '/') != NULL) {
3899 entry->u.index = -1;
3900 if (act & DO_ABS) {
3901 while (stat(name, &statb) < 0) {
3902#ifdef SYSV
3903 if (errno == EINTR)
3904 continue;
3905#endif
3906 entry->cmdtype = CMDUNKNOWN;
3907 return;
3908 }
3909 }
3910 entry->cmdtype = CMDNORMAL;
3911 return;
3912 }
3913
3914#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
3915 if (find_applet_by_name(name)) {
3916 entry->cmdtype = CMDNORMAL;
3917 entry->u.index = -1;
3918 return;
3919 }
3920#endif
3921
3922 updatetbl = (path == pathval());
3923 if (!updatetbl) {
3924 act |= DO_ALTPATH;
3925 if (strstr(path, "%builtin") != NULL)
3926 act |= DO_ALTBLTIN;
3927 }
3928
3929 /* If name is in the table, check answer will be ok */
3930 if ((cmdp = cmdlookup(name, 0)) != NULL) {
3931 int bit;
3932
3933 switch (cmdp->cmdtype) {
3934 default:
3935#if DEBUG
3936 abort();
3937#endif
3938 case CMDNORMAL:
3939 bit = DO_ALTPATH;
3940 break;
3941 case CMDFUNCTION:
3942 bit = DO_NOFUNC;
3943 break;
3944 case CMDBUILTIN:
3945 bit = DO_ALTBLTIN;
3946 break;
3947 }
3948 if (act & bit) {
3949 updatetbl = 0;
3950 cmdp = NULL;
3951 } else if (cmdp->rehash == 0)
3952 /* if not invalidated by cd, we're done */
3953 goto success;
3954 }
3955
3956 /* If %builtin not in path, check for builtin next */
3957 bcmd = find_builtin(name);
3958 if (bcmd && (IS_BUILTIN_REGULAR(bcmd) || (
3959 act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0
3960 )))
3961 goto builtin_success;
3962
3963 /* We have to search path. */
3964 prev = -1; /* where to start */
3965 if (cmdp && cmdp->rehash) { /* doing a rehash */
3966 if (cmdp->cmdtype == CMDBUILTIN)
3967 prev = builtinloc;
3968 else
3969 prev = cmdp->param.index;
3970 }
3971
3972 e = ENOENT;
3973 idx = -1;
3974loop:
3975 while ((fullname = padvance(&path, name)) != NULL) {
3976 stunalloc(fullname);
3977 idx++;
3978 if (pathopt) {
3979 if (prefix(pathopt, "builtin")) {
3980 if (bcmd)
3981 goto builtin_success;
3982 continue;
3983 } else if (!(act & DO_NOFUNC) &&
3984 prefix(pathopt, "func")) {
3985 /* handled below */
3986 } else {
3987 /* ignore unimplemented options */
3988 continue;
3989 }
3990 }
3991 /* if rehash, don't redo absolute path names */
3992 if (fullname[0] == '/' && idx <= prev) {
3993 if (idx < prev)
3994 continue;
3995 TRACE(("searchexec \"%s\": no change\n", name));
3996 goto success;
3997 }
3998 while (stat(fullname, &statb) < 0) {
3999#ifdef SYSV
4000 if (errno == EINTR)
4001 continue;
4002#endif
4003 if (errno != ENOENT && errno != ENOTDIR)
4004 e = errno;
4005 goto loop;
4006 }
4007 e = EACCES; /* if we fail, this will be the error */
4008 if (!S_ISREG(statb.st_mode))
4009 continue;
4010 if (pathopt) { /* this is a %func directory */
4011 stalloc(strlen(fullname) + 1);
4012 readcmdfile(fullname);
4013 if ((cmdp = cmdlookup(name, 0)) == NULL ||
4014 cmdp->cmdtype != CMDFUNCTION)
4015 error("%s not defined in %s", name, fullname);
4016 stunalloc(fullname);
4017 goto success;
4018 }
4019 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
4020 if (!updatetbl) {
4021 entry->cmdtype = CMDNORMAL;
4022 entry->u.index = idx;
4023 return;
4024 }
4025 INTOFF;
4026 cmdp = cmdlookup(name, 1);
4027 cmdp->cmdtype = CMDNORMAL;
4028 cmdp->param.index = idx;
4029 INTON;
4030 goto success;
4031 }
4032
4033 /* We failed. If there was an entry for this command, delete it */
4034 if (cmdp && updatetbl)
4035 delete_cmd_entry();
4036 if (act & DO_ERR)
4037 sh_warnx("%s: %s", name, errmsg(e, E_EXEC));
4038 entry->cmdtype = CMDUNKNOWN;
4039 return;
4040
4041builtin_success:
4042 if (!updatetbl) {
4043 entry->cmdtype = CMDBUILTIN;
4044 entry->u.cmd = bcmd;
4045 return;
4046 }
4047 INTOFF;
4048 cmdp = cmdlookup(name, 1);
4049 cmdp->cmdtype = CMDBUILTIN;
4050 cmdp->param.cmd = bcmd;
4051 INTON;
4052success:
4053 cmdp->rehash = 0;
4054 entry->cmdtype = cmdp->cmdtype;
4055 entry->u = cmdp->param;
4056}
4057
4058
4059/*
4060 * Wrapper around strcmp for qsort/bsearch/...
4061 */
4062static int pstrcmp(const void *a, const void *b)
4063{
4064 return strcmp((const char *) a, (*(const char *const *) b) + 1);
4065}
4066
4067/*
4068 * Search the table of builtin commands.
4069 */
4070
4071static struct builtincmd *
4072find_builtin(const char *name)
4073{
4074 struct builtincmd *bp;
4075
4076 bp = bsearch(
4077 name, builtincmd, NUMBUILTINS, sizeof(struct builtincmd),
4078 pstrcmp
4079 );
4080 return bp;
4081}
4082
4083
4084
4085/*
4086 * Called when a cd is done. Marks all commands so the next time they
4087 * are executed they will be rehashed.
4088 */
4089
4090static void
4091hashcd(void)
4092{
4093 struct tblentry **pp;
4094 struct tblentry *cmdp;
4095
4096 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
4097 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
4098 if (cmdp->cmdtype == CMDNORMAL || (
4099 cmdp->cmdtype == CMDBUILTIN &&
4100 !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
4101 builtinloc > 0
4102 ))
4103 cmdp->rehash = 1;
4104 }
4105 }
4106}
4107
4108
4109
4110/*
4111 * Fix command hash table when PATH changed.
4112 * Called before PATH is changed. The argument is the new value of PATH;
4113 * pathval() still returns the old value at this point.
4114 * Called with interrupts off.
4115 */
4116
4117static void
4118changepath(const char *newval)
4119{
4120 const char *old, *new;
4121 int idx;
4122 int firstchange;
4123 int idx_bltin;
4124
4125 old = pathval();
4126 new = newval;
4127 firstchange = 9999; /* assume no change */
4128 idx = 0;
4129 idx_bltin = -1;
4130 for (;;) {
4131 if (*old != *new) {
4132 firstchange = idx;
4133 if ((*old == '\0' && *new == ':')
4134 || (*old == ':' && *new == '\0'))
4135 firstchange++;
4136 old = new; /* ignore subsequent differences */
4137 }
4138 if (*new == '\0')
4139 break;
4140 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
4141 idx_bltin = idx;
4142 if (*new == ':') {
4143 idx++;
4144 }
4145 new++, old++;
4146 }
4147 if (builtinloc < 0 && idx_bltin >= 0)
4148 builtinloc = idx_bltin; /* zap builtins */
4149 if (builtinloc >= 0 && idx_bltin < 0)
4150 firstchange = 0;
4151 clearcmdentry(firstchange);
4152 builtinloc = idx_bltin;
4153}
4154
4155
4156/*
4157 * Clear out command entries. The argument specifies the first entry in
4158 * PATH which has changed.
4159 */
4160
4161static void
4162clearcmdentry(int firstchange)
4163{
4164 struct tblentry **tblp;
4165 struct tblentry **pp;
4166 struct tblentry *cmdp;
4167
4168 INTOFF;
4169 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
4170 pp = tblp;
4171 while ((cmdp = *pp) != NULL) {
4172 if ((cmdp->cmdtype == CMDNORMAL &&
4173 cmdp->param.index >= firstchange)
4174 || (cmdp->cmdtype == CMDBUILTIN &&
4175 builtinloc >= firstchange)) {
4176 *pp = cmdp->next;
4177 ckfree(cmdp);
4178 } else {
4179 pp = &cmdp->next;
4180 }
4181 }
4182 }
4183 INTON;
4184}
4185
4186
4187
4188/*
4189 * Locate a command in the command hash table. If "add" is nonzero,
4190 * add the command to the table if it is not already present. The
4191 * variable "lastcmdentry" is set to point to the address of the link
4192 * pointing to the entry, so that delete_cmd_entry can delete the
4193 * entry.
4194 *
4195 * Interrupts must be off if called with add != 0.
4196 */
4197
4198static struct tblentry **lastcmdentry;
4199
4200
4201static struct tblentry *
4202cmdlookup(const char *name, int add)
4203{
4204 unsigned int hashval;
4205 const char *p;
4206 struct tblentry *cmdp;
4207 struct tblentry **pp;
4208
4209 p = name;
4210 hashval = (unsigned char)*p << 4;
4211 while (*p)
4212 hashval += (unsigned char)*p++;
4213 hashval &= 0x7FFF;
4214 pp = &cmdtable[hashval % CMDTABLESIZE];
4215 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
4216 if (equal(cmdp->cmdname, name))
4217 break;
4218 pp = &cmdp->next;
4219 }
4220 if (add && cmdp == NULL) {
4221 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
4222 + strlen(name) + 1);
4223 cmdp->next = NULL;
4224 cmdp->cmdtype = CMDUNKNOWN;
4225 strcpy(cmdp->cmdname, name);
4226 }
4227 lastcmdentry = pp;
4228 return cmdp;
4229}
4230
4231/*
4232 * Delete the command entry returned on the last lookup.
4233 */
4234
4235static void
4236delete_cmd_entry(void)
4237{
4238 struct tblentry *cmdp;
4239
4240 INTOFF;
4241 cmdp = *lastcmdentry;
4242 *lastcmdentry = cmdp->next;
4243 if (cmdp->cmdtype == CMDFUNCTION)
4244 freefunc(cmdp->param.func);
4245 ckfree(cmdp);
4246 INTON;
4247}
4248
4249
4250/*
4251 * Add a new command entry, replacing any existing command entry for
4252 * the same name - except special builtins.
4253 */
4254
4255static inline void
4256addcmdentry(char *name, struct cmdentry *entry)
4257{
4258 struct tblentry *cmdp;
4259
4260 cmdp = cmdlookup(name, 1);
4261 if (cmdp->cmdtype == CMDFUNCTION) {
4262 freefunc(cmdp->param.func);
4263 }
4264 cmdp->cmdtype = entry->cmdtype;
4265 cmdp->param = entry->u;
4266 cmdp->rehash = 0;
4267}
4268
4269/*
4270 * Make a copy of a parse tree.
4271 */
4272
4273static inline struct funcnode *
4274copyfunc(union node *n)
4275{
4276 struct funcnode *f;
4277 size_t blocksize;
4278
4279 funcblocksize = offsetof(struct funcnode, n);
4280 funcstringsize = 0;
4281 calcsize(n);
4282 blocksize = funcblocksize;
4283 f = ckmalloc(blocksize + funcstringsize);
4284 funcblock = (char *) f + offsetof(struct funcnode, n);
4285 funcstring = (char *) f + blocksize;
4286 copynode(n);
4287 f->count = 0;
4288 return f;
4289}
4290
4291/*
4292 * Define a shell function.
4293 */
4294
4295static void
4296defun(char *name, union node *func)
4297{
4298 struct cmdentry entry;
4299
4300 INTOFF;
4301 entry.cmdtype = CMDFUNCTION;
4302 entry.u.func = copyfunc(func);
4303 addcmdentry(name, &entry);
4304 INTON;
4305}
4306
4307
4308/*
4309 * Delete a function if it exists.
4310 */
4311
4312static void
4313unsetfunc(const char *name)
4314{
4315 struct tblentry *cmdp;
4316
4317 if ((cmdp = cmdlookup(name, 0)) != NULL &&
4318 cmdp->cmdtype == CMDFUNCTION)
4319 delete_cmd_entry();
4320}
4321
4322/*
4323 * Locate and print what a word is...
4324 */
4325
4326
4327#ifdef CONFIG_ASH_CMDCMD
4328static int
4329describe_command(char *command, int describe_command_verbose)
4330#else
4331#define describe_command_verbose 1
4332static int
4333describe_command(char *command)
4334#endif
4335{
4336 struct cmdentry entry;
4337 struct tblentry *cmdp;
4338#ifdef CONFIG_ASH_ALIAS
4339 const struct alias *ap;
4340#endif
4341 const char *path = pathval();
4342
4343 if (describe_command_verbose) {
4344 out1str(command);
4345 }
4346
4347 /* First look at the keywords */
4348 if (findkwd(command)) {
4349 out1str(describe_command_verbose ? " is a shell keyword" : command);
4350 goto out;
4351 }
4352
4353#ifdef CONFIG_ASH_ALIAS
4354 /* Then look at the aliases */
4355 if ((ap = lookupalias(command, 0)) != NULL) {
4356 if (describe_command_verbose) {
4357 out1fmt(" is an alias for %s", ap->val);
4358 } else {
4359 out1str("alias ");
4360 printalias(ap);
4361 return 0;
4362 }
4363 goto out;
4364 }
4365#endif
4366 /* Then check if it is a tracked alias */
4367 if ((cmdp = cmdlookup(command, 0)) != NULL) {
4368 entry.cmdtype = cmdp->cmdtype;
4369 entry.u = cmdp->param;
4370 } else {
4371 /* Finally use brute force */
4372 find_command(command, &entry, DO_ABS, path);
4373 }
4374
4375 switch (entry.cmdtype) {
4376 case CMDNORMAL: {
4377 int j = entry.u.index;
4378 char *p;
4379 if (j == -1) {
4380 p = command;
4381 } else {
4382 do {
4383 p = padvance(&path, command);
4384 stunalloc(p);
4385 } while (--j >= 0);
4386 }
4387 if (describe_command_verbose) {
4388 out1fmt(" is%s %s",
4389 (cmdp ? " a tracked alias for" : nullstr), p
4390 );
4391 } else {
4392 out1str(p);
4393 }
4394 break;
4395 }
4396
4397 case CMDFUNCTION:
4398 if (describe_command_verbose) {
4399 out1str(" is a shell function");
4400 } else {
4401 out1str(command);
4402 }
4403 break;
4404
4405 case CMDBUILTIN:
4406 if (describe_command_verbose) {
4407 out1fmt(" is a %sshell builtin",
4408 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
4409 "special " : nullstr
4410 );
4411 } else {
4412 out1str(command);
4413 }
4414 break;
4415
4416 default:
4417 if (describe_command_verbose) {
4418 out1str(": not found\n");
4419 }
4420 return 127;
4421 }
4422
4423out:
4424 outstr("\n", stdout);
4425 return 0;
4426}
4427
4428static int
4429typecmd(int argc, char **argv)
4430{
4431 int i;
4432 int err = 0;
4433
4434 for (i = 1; i < argc; i++) {
4435#ifdef CONFIG_ASH_CMDCMD
4436 err |= describe_command(argv[i], 1);
4437#else
4438 err |= describe_command(argv[i]);
4439#endif
4440 }
4441 return err;
4442}
4443
4444#ifdef CONFIG_ASH_CMDCMD
4445static int
4446commandcmd(int argc, char **argv)
4447{
4448 int c;
4449 int default_path = 0;
4450 int verify_only = 0;
4451 int verbose_verify_only = 0;
4452
4453 while ((c = nextopt("pvV")) != '\0')
4454 switch (c) {
4455 default:
4456#ifdef DEBUG
4457 fprintf(stderr,
4458"command: nextopt returned character code 0%o\n", c);
4459 return EX_SOFTWARE;
4460#endif
4461 case 'p':
4462 default_path = 1;
4463 break;
4464 case 'v':
4465 verify_only = 1;
4466 break;
4467 case 'V':
4468 verbose_verify_only = 1;
4469 break;
4470 }
4471
4472 if (default_path + verify_only + verbose_verify_only > 1 ||
4473 !*argptr) {
4474 fprintf(stderr,
4475 "command [-p] command [arg ...]\n"
4476 "command {-v|-V} command\n");
4477 return EX_USAGE;
4478 }
4479
4480 if (verify_only || verbose_verify_only) {
4481 return describe_command(*argptr, verbose_verify_only);
4482 }
4483
4484 return 0;
4485}
4486#endif
4487
4488/* $NetBSD: expand.c,v 1.56 2002/11/24 22:35:39 christos Exp $ */
4489
4490/*
4491 * Routines to expand arguments to commands. We have to deal with
4492 * backquotes, shell variables, and file metacharacters.
4493 */
4494
4495/*
4496 * _rmescape() flags
4497 */
4498#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
4499#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
4500#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
4501#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
4502#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
4503
4504/*
4505 * Structure specifying which parts of the string should be searched
4506 * for IFS characters.
4507 */
4508
4509struct ifsregion {
4510 struct ifsregion *next; /* next region in list */
4511 int begoff; /* offset of start of region */
4512 int endoff; /* offset of end of region */
4513 int nulonly; /* search for nul bytes only */
4514};
4515
4516/* output of current string */
4517static char *expdest;
4518/* list of back quote expressions */
4519static struct nodelist *argbackq;
4520/* first struct in list of ifs regions */
4521static struct ifsregion ifsfirst;
4522/* last struct in list */
4523static struct ifsregion *ifslastp;
4524/* holds expanded arg list */
4525static struct arglist exparg;
4526
4527static void argstr(char *, int);
4528static char *exptilde(char *, char *, int);
4529static void expbackq(union node *, int, int);
4530static const char *subevalvar(char *, char *, int, int, int, int, int);
4531static char *evalvar(char *, int);
4532static void strtodest(const char *, int, int);
4533static void memtodest(const char *p, size_t len, int syntax, int quotes);
4534static ssize_t varvalue(char *, int, int);
4535static void recordregion(int, int, int);
4536static void removerecordregions(int);
4537static void ifsbreakup(char *, struct arglist *);
4538static void ifsfree(void);
4539static void expandmeta(struct strlist *, int);
4540static int patmatch(char *, const char *);
4541
4542static int cvtnum(arith_t);
4543static size_t esclen(const char *, const char *);
4544static char *scanleft(char *, char *, char *, char *, int, int);
4545static char *scanright(char *, char *, char *, char *, int, int);
4546static void varunset(const char *, const char *, const char *, int)
4547 __attribute__((__noreturn__));
4548
4549
4550#define pmatch(a, b) !fnmatch((a), (b), 0)
4551/*
4552 * Prepare a pattern for a expmeta (internal glob(3)) call.
4553 *
4554 * Returns an stalloced string.
4555 */
4556
4557static inline char *
4558preglob(const char *pattern, int quoted, int flag) {
4559 flag |= RMESCAPE_GLOB;
4560 if (quoted) {
4561 flag |= RMESCAPE_QUOTED;
4562 }
4563 return _rmescapes((char *)pattern, flag);
4564}
4565
4566
4567static size_t
4568esclen(const char *start, const char *p) {
4569 size_t esc = 0;
4570
4571 while (p > start && *--p == CTLESC) {
4572 esc++;
4573 }
4574 return esc;
4575}
4576
4577
4578/*
4579 * Expand shell variables and backquotes inside a here document.
4580 */
4581
4582static inline void
4583expandhere(union node *arg, int fd)
4584{
4585 herefd = fd;
4586 expandarg(arg, (struct arglist *)NULL, 0);
4587 bb_full_write(fd, stackblock(), expdest - (char *)stackblock());
4588}
4589
4590
4591/*
4592 * Perform variable substitution and command substitution on an argument,
4593 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
4594 * perform splitting and file name expansion. When arglist is NULL, perform
4595 * here document expansion.
4596 */
4597
4598void
4599expandarg(union node *arg, struct arglist *arglist, int flag)
4600{
4601 struct strlist *sp;
4602 char *p;
4603
4604 argbackq = arg->narg.backquote;
4605 STARTSTACKSTR(expdest);
4606 ifsfirst.next = NULL;
4607 ifslastp = NULL;
4608 argstr(arg->narg.text, flag);
4609 if (arglist == NULL) {
4610 return; /* here document expanded */
4611 }
4612 STPUTC('\0', expdest);
4613 p = grabstackstr(expdest);
4614 exparg.lastp = &exparg.list;
4615 /*
4616 * TODO - EXP_REDIR
4617 */
4618 if (flag & EXP_FULL) {
4619 ifsbreakup(p, &exparg);
4620 *exparg.lastp = NULL;
4621 exparg.lastp = &exparg.list;
4622 expandmeta(exparg.list, flag);
4623 } else {
4624 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
4625 rmescapes(p);
4626 sp = (struct strlist *)stalloc(sizeof (struct strlist));
4627 sp->text = p;
4628 *exparg.lastp = sp;
4629 exparg.lastp = &sp->next;
4630 }
4631 if (ifsfirst.next)
4632 ifsfree();
4633 *exparg.lastp = NULL;
4634 if (exparg.list) {
4635 *arglist->lastp = exparg.list;
4636 arglist->lastp = exparg.lastp;
4637 }
4638}
4639
4640
4641/*
4642 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
4643 * characters to allow for further processing. Otherwise treat
4644 * $@ like $* since no splitting will be performed.
4645 */
4646
4647static void
4648argstr(char *p, int flag)
4649{
4650 static const char spclchars[] = {
4651 '=',
4652 ':',
4653 CTLQUOTEMARK,
4654 CTLENDVAR,
4655 CTLESC,
4656 CTLVAR,
4657 CTLBACKQ,
4658 CTLBACKQ | CTLQUOTE,
4659#ifdef CONFIG_ASH_MATH_SUPPORT
4660 CTLENDARI,
4661#endif
4662 0
4663 };
4664 const char *reject = spclchars;
4665 int c;
4666 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
4667 int breakall = flag & EXP_WORD;
4668 int inquotes;
4669 size_t length;
4670 int startloc;
4671
4672 if (!(flag & EXP_VARTILDE)) {
4673 reject += 2;
4674 } else if (flag & EXP_VARTILDE2) {
4675 reject++;
4676 }
4677 inquotes = 0;
4678 length = 0;
4679 if (flag & EXP_TILDE) {
4680 char *q;
4681
4682 flag &= ~EXP_TILDE;
4683tilde:
4684 q = p;
4685 if (*q == CTLESC && (flag & EXP_QWORD))
4686 q++;
4687 if (*q == '~')
4688 p = exptilde(p, q, flag);
4689 }
4690start:
4691 startloc = expdest - (char *)stackblock();
4692 for (;;) {
4693 length += strcspn(p + length, reject);
4694 c = p[length];
4695 if (c && (!(c & 0x80)
4696#ifdef CONFIG_ASH_MATH_SUPPORT
4697 || c == CTLENDARI
4698#endif
4699 )) {
4700 /* c == '=' || c == ':' || c == CTLENDARI */
4701 length++;
4702 }
4703 if (length > 0) {
4704 int newloc;
4705 expdest = stnputs(p, length, expdest);
4706 newloc = expdest - (char *)stackblock();
4707 if (breakall && !inquotes && newloc > startloc) {
4708 recordregion(startloc, newloc, 0);
4709 }
4710 startloc = newloc;
4711 }
4712 p += length + 1;
4713 length = 0;
4714
4715 switch (c) {
4716 case '\0':
4717 goto breakloop;
4718 case '=':
4719 if (flag & EXP_VARTILDE2) {
4720 p--;
4721 continue;
4722 }
4723 flag |= EXP_VARTILDE2;
4724 reject++;
4725 /* fall through */
4726 case ':':
4727 /*
4728 * sort of a hack - expand tildes in variable
4729 * assignments (after the first '=' and after ':'s).
4730 */
4731 if (*--p == '~') {
4732 goto tilde;
4733 }
4734 continue;
4735 }
4736
4737 switch (c) {
4738 case CTLENDVAR: /* ??? */
4739 goto breakloop;
4740 case CTLQUOTEMARK:
4741 /* "$@" syntax adherence hack */
4742 if (
4743 !inquotes &&
4744 !memcmp(p, dolatstr, DOLATSTRLEN) &&
4745 (p[4] == CTLQUOTEMARK || (
4746 p[4] == CTLENDVAR &&
4747 p[5] == CTLQUOTEMARK
4748 ))
4749 ) {
4750 p = evalvar(p + 1, flag) + 1;
4751 goto start;
4752 }
4753 inquotes = !inquotes;
4754addquote:
4755 if (quotes) {
4756 p--;
4757 length++;
4758 startloc++;
4759 }
4760 break;
4761 case CTLESC:
4762 startloc++;
4763 length++;
4764 goto addquote;
4765 case CTLVAR:
4766 p = evalvar(p, flag);
4767 goto start;
4768 case CTLBACKQ:
4769 c = 0;
4770 case CTLBACKQ|CTLQUOTE:
4771 expbackq(argbackq->n, c, quotes);
4772 argbackq = argbackq->next;
4773 goto start;
4774#ifdef CONFIG_ASH_MATH_SUPPORT
4775 case CTLENDARI:
4776 p--;
4777 expari(quotes);
4778 goto start;
4779#endif
4780 }
4781 }
4782breakloop:
4783 ;
4784}
4785
4786static char *
4787exptilde(char *startp, char *p, int flag)
4788{
4789 char c;
4790 char *name;
4791 struct passwd *pw;
4792 const char *home;
4793 int quotes = flag & (EXP_FULL | EXP_CASE);
4794 int startloc;
4795
4796 name = p + 1;
4797
4798 while ((c = *++p) != '\0') {
4799 switch(c) {
4800 case CTLESC:
4801 return (startp);
4802 case CTLQUOTEMARK:
4803 return (startp);
4804 case ':':
4805 if (flag & EXP_VARTILDE)
4806 goto done;
4807 break;
4808 case '/':
4809 case CTLENDVAR:
4810 goto done;
4811 }
4812 }
4813done:
4814 *p = '\0';
4815 if (*name == '\0') {
4816 if ((home = lookupvar(homestr)) == NULL)
4817 goto lose;
4818 } else {
4819 if ((pw = getpwnam(name)) == NULL)
4820 goto lose;
4821 home = pw->pw_dir;
4822 }
4823 if (*home == '\0')
4824 goto lose;
4825 *p = c;
4826 startloc = expdest - (char *)stackblock();
4827 strtodest(home, SQSYNTAX, quotes);
4828 recordregion(startloc, expdest - (char *)stackblock(), 0);
4829 return (p);
4830lose:
4831 *p = c;
4832 return (startp);
4833}
4834
4835
4836static void
4837removerecordregions(int endoff)
4838{
4839 if (ifslastp == NULL)
4840 return;
4841
4842 if (ifsfirst.endoff > endoff) {
4843 while (ifsfirst.next != NULL) {
4844 struct ifsregion *ifsp;
4845 INTOFF;
4846 ifsp = ifsfirst.next->next;
4847 ckfree(ifsfirst.next);
4848 ifsfirst.next = ifsp;
4849 INTON;
4850 }
4851 if (ifsfirst.begoff > endoff)
4852 ifslastp = NULL;
4853 else {
4854 ifslastp = &ifsfirst;
4855 ifsfirst.endoff = endoff;
4856 }
4857 return;
4858 }
4859
4860 ifslastp = &ifsfirst;
4861 while (ifslastp->next && ifslastp->next->begoff < endoff)
4862 ifslastp=ifslastp->next;
4863 while (ifslastp->next != NULL) {
4864 struct ifsregion *ifsp;
4865 INTOFF;
4866 ifsp = ifslastp->next->next;
4867 ckfree(ifslastp->next);
4868 ifslastp->next = ifsp;
4869 INTON;
4870 }
4871 if (ifslastp->endoff > endoff)
4872 ifslastp->endoff = endoff;
4873}
4874
4875
4876#ifdef CONFIG_ASH_MATH_SUPPORT
4877/*
4878 * Expand arithmetic expression. Backup to start of expression,
4879 * evaluate, place result in (backed up) result, adjust string position.
4880 */
4881void
4882expari(int quotes)
4883{
4884 char *p, *start;
4885 int begoff;
4886 int flag;
4887 int len;
4888
4889 /* ifsfree(); */
4890
4891 /*
4892 * This routine is slightly over-complicated for
4893 * efficiency. Next we scan backwards looking for the
4894 * start of arithmetic.
4895 */
4896 start = stackblock();
4897 p = expdest - 1;
4898 *p = '\0';
4899 p--;
4900 do {
4901 int esc;
4902
4903 while (*p != CTLARI) {
4904 p--;
4905#ifdef DEBUG
4906 if (p < start) {
4907 error("missing CTLARI (shouldn't happen)");
4908 }
4909#endif
4910 }
4911
4912 esc = esclen(start, p);
4913 if (!(esc % 2)) {
4914 break;
4915 }
4916
4917 p -= esc + 1;
4918 } while (1);
4919
4920 begoff = p - start;
4921
4922 removerecordregions(begoff);
4923
4924 flag = p[1];
4925
4926 expdest = p;
4927
4928 if (quotes)
4929 rmescapes(p + 2);
4930
4931 len = cvtnum(dash_arith(p + 2));
4932
4933 if (flag != '"')
4934 recordregion(begoff, begoff + len, 0);
4935}
4936#endif
4937
4938/*
4939 * Expand stuff in backwards quotes.
4940 */
4941
4942static void
4943expbackq(union node *cmd, int quoted, int quotes)
4944{
4945 struct backcmd in;
4946 int i;
4947 char buf[128];
4948 char *p;
4949 char *dest;
4950 int startloc;
4951 int syntax = quoted? DQSYNTAX : BASESYNTAX;
4952 struct stackmark smark;
4953
4954 INTOFF;
4955 setstackmark(&smark);
4956 dest = expdest;
4957 startloc = dest - (char *)stackblock();
4958 grabstackstr(dest);
4959 evalbackcmd(cmd, (struct backcmd *) &in);
4960 popstackmark(&smark);
4961
4962 p = in.buf;
4963 i = in.nleft;
4964 if (i == 0)
4965 goto read;
4966 for (;;) {
4967 memtodest(p, i, syntax, quotes);
4968read:
4969 if (in.fd < 0)
4970 break;
4971 i = safe_read(in.fd, buf, sizeof buf);
4972 TRACE(("expbackq: read returns %d\n", i));
4973 if (i <= 0)
4974 break;
4975 p = buf;
4976 }
4977
4978 if (in.buf)
4979 ckfree(in.buf);
4980 if (in.fd >= 0) {
4981 close(in.fd);
4982 back_exitstatus = waitforjob(in.jp);
4983 }
4984 INTON;
4985
4986 /* Eat all trailing newlines */
4987 dest = expdest;
4988 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
4989 STUNPUTC(dest);
4990 expdest = dest;
4991
4992 if (quoted == 0)
4993 recordregion(startloc, dest - (char *)stackblock(), 0);
4994 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
4995 (dest - (char *)stackblock()) - startloc,
4996 (dest - (char *)stackblock()) - startloc,
4997 stackblock() + startloc));
4998}
4999
5000
5001static char *
5002scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5003 int zero)
5004{
5005 char *loc;
5006 char *loc2;
5007 char c;
5008
5009 loc = startp;
5010 loc2 = rmesc;
5011 do {
5012 int match;
5013 const char *s = loc2;
5014 c = *loc2;
5015 if (zero) {
5016 *loc2 = '\0';
5017 s = rmesc;
5018 }
5019 match = pmatch(str, s);
5020 *loc2 = c;
5021 if (match)
5022 return loc;
5023 if (quotes && *loc == CTLESC)
5024 loc++;
5025 loc++;
5026 loc2++;
5027 } while (c);
5028 return 0;
5029}
5030
5031
5032static char *
5033scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
5034 int zero)
5035{
5036 int esc = 0;
5037 char *loc;
5038 char *loc2;
5039
5040 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
5041 int match;
5042 char c = *loc2;
5043 const char *s = loc2;
5044 if (zero) {
5045 *loc2 = '\0';
5046 s = rmesc;
5047 }
5048 match = pmatch(str, s);
5049 *loc2 = c;
5050 if (match)
5051 return loc;
5052 loc--;
5053 if (quotes) {
5054 if (--esc < 0) {
5055 esc = esclen(startp, loc);
5056 }
5057 if (esc % 2) {
5058 esc--;
5059 loc--;
5060 }
5061 }
5062 }
5063 return 0;
5064}
5065
5066static const char *
5067subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
5068{
5069 char *startp;
5070 char *loc;
5071 int saveherefd = herefd;
5072 struct nodelist *saveargbackq = argbackq;
5073 int amount;
5074 char *rmesc, *rmescend;
5075 int zero;
5076 char *(*scan)(char *, char *, char *, char *, int , int);
5077
5078 herefd = -1;
5079 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
5080 STPUTC('\0', expdest);
5081 herefd = saveherefd;
5082 argbackq = saveargbackq;
5083 startp = stackblock() + startloc;
5084
5085 switch (subtype) {
5086 case VSASSIGN:
5087 setvar(str, startp, 0);
5088 amount = startp - expdest;
5089 STADJUST(amount, expdest);
5090 return startp;
5091
5092 case VSQUESTION:
5093 varunset(p, str, startp, varflags);
5094 /* NOTREACHED */
5095 }
5096
5097 subtype -= VSTRIMRIGHT;
5098#ifdef DEBUG
5099 if (subtype < 0 || subtype > 3)
5100 abort();
5101#endif
5102
5103 rmesc = startp;
5104 rmescend = stackblock() + strloc;
5105 if (quotes) {
5106 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
5107 if (rmesc != startp) {
5108 rmescend = expdest;
5109 startp = stackblock() + startloc;
5110 }
5111 }
5112 rmescend--;
5113 str = stackblock() + strloc;
5114 preglob(str, varflags & VSQUOTE, 0);
5115
5116 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
5117 zero = subtype >> 1;
5118 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
5119 scan = (subtype & 1) ^ zero ? scanleft : scanright;
5120
5121 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
5122 if (loc) {
5123 if (zero) {
5124 memmove(startp, loc, str - loc);
5125 loc = startp + (str - loc) - 1;
5126 }
5127 *loc = '\0';
5128 amount = loc - expdest;
5129 STADJUST(amount, expdest);
5130 }
5131 return loc;
5132}
5133
5134
5135/*
5136 * Expand a variable, and return a pointer to the next character in the
5137 * input string.
5138 */
5139static char *
5140evalvar(char *p, int flag)
5141{
5142 int subtype;
5143 int varflags;
5144 char *var;
5145 int patloc;
5146 int c;
5147 int startloc;
5148 ssize_t varlen;
5149 int easy;
5150 int quotes;
5151 int quoted;
5152
5153 quotes = flag & (EXP_FULL | EXP_CASE);
5154 varflags = *p++;
5155 subtype = varflags & VSTYPE;
5156 quoted = varflags & VSQUOTE;
5157 var = p;
5158 easy = (!quoted || (*var == '@' && shellparam.nparam));
5159 startloc = expdest - (char *)stackblock();
5160 p = strchr(p, '=') + 1;
5161
5162again:
5163 varlen = varvalue(var, varflags, flag);
5164 if (varflags & VSNUL)
5165 varlen--;
5166
5167 if (subtype == VSPLUS) {
5168 varlen = -1 - varlen;
5169 goto vsplus;
5170 }
5171
5172 if (subtype == VSMINUS) {
5173vsplus:
5174 if (varlen < 0) {
5175 argstr(
5176 p, flag | EXP_TILDE |
5177 (quoted ? EXP_QWORD : EXP_WORD)
5178 );
5179 goto end;
5180 }
5181 if (easy)
5182 goto record;
5183 goto end;
5184 }
5185
5186 if (subtype == VSASSIGN || subtype == VSQUESTION) {
5187 if (varlen < 0) {
5188 if (subevalvar(p, var, 0, subtype, startloc,
5189 varflags, 0)) {
5190 varflags &= ~VSNUL;
5191 /*
5192 * Remove any recorded regions beyond
5193 * start of variable
5194 */
5195 removerecordregions(startloc);
5196 goto again;
5197 }
5198 goto end;
5199 }
5200 if (easy)
5201 goto record;
5202 goto end;
5203 }
5204
5205 if (varlen < 0 && uflag)
5206 varunset(p, var, 0, 0);
5207
5208 if (subtype == VSLENGTH) {
5209 cvtnum(varlen > 0 ? varlen : 0);
5210 goto record;
5211 }
5212
5213 if (subtype == VSNORMAL) {
5214 if (!easy)
5215 goto end;
5216record:
5217 recordregion(startloc, expdest - (char *)stackblock(), quoted);
5218 goto end;
5219 }
5220
5221#ifdef DEBUG
5222 switch (subtype) {
5223 case VSTRIMLEFT:
5224 case VSTRIMLEFTMAX:
5225 case VSTRIMRIGHT:
5226 case VSTRIMRIGHTMAX:
5227 break;
5228 default:
5229 abort();
5230 }
5231#endif
5232
5233 if (varlen >= 0) {
5234 /*
5235 * Terminate the string and start recording the pattern
5236 * right after it
5237 */
5238 STPUTC('\0', expdest);
5239 patloc = expdest - (char *)stackblock();
5240 if (subevalvar(p, NULL, patloc, subtype,
5241 startloc, varflags, quotes) == 0) {
5242 int amount = expdest - (
5243 (char *)stackblock() + patloc - 1
5244 );
5245 STADJUST(-amount, expdest);
5246 }
5247 /* Remove any recorded regions beyond start of variable */
5248 removerecordregions(startloc);
5249 goto record;
5250 }
5251
5252end:
5253 if (subtype != VSNORMAL) { /* skip to end of alternative */
5254 int nesting = 1;
5255 for (;;) {
5256 if ((c = *p++) == CTLESC)
5257 p++;
5258 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
5259 if (varlen >= 0)
5260 argbackq = argbackq->next;
5261 } else if (c == CTLVAR) {
5262 if ((*p++ & VSTYPE) != VSNORMAL)
5263 nesting++;
5264 } else if (c == CTLENDVAR) {
5265 if (--nesting == 0)
5266 break;
5267 }
5268 }
5269 }
5270 return p;
5271}
5272
5273
5274/*
5275 * Put a string on the stack.
5276 */
5277
5278static void
5279memtodest(const char *p, size_t len, int syntax, int quotes) {
5280 char *q = expdest;
5281
5282 q = makestrspace(len * 2, q);
5283
5284 while (len--) {
5285 int c = *p++;
5286 if (!c)
5287 continue;
5288 if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
5289 USTPUTC(CTLESC, q);
5290 USTPUTC(c, q);
5291 }
5292
5293 expdest = q;
5294}
5295
5296
5297static void
5298strtodest(const char *p, int syntax, int quotes)
5299{
5300 memtodest(p, strlen(p), syntax, quotes);
5301}
5302
5303
5304/*
5305 * Add the value of a specialized variable to the stack string.
5306 */
5307
5308static ssize_t
5309varvalue(char *name, int varflags, int flags)
5310{
5311 int num;
5312 char *p;
5313 int i;
5314 int sep = 0;
5315 int sepq = 0;
5316 ssize_t len = 0;
5317 char **ap;
5318 int syntax;
5319 int quoted = varflags & VSQUOTE;
5320 int subtype = varflags & VSTYPE;
5321 int quotes = flags & (EXP_FULL | EXP_CASE);
5322
5323 if (quoted && (flags & EXP_FULL))
5324 sep = 1 << CHAR_BIT;
5325
5326 syntax = quoted ? DQSYNTAX : BASESYNTAX;
5327 switch (*name) {
5328 case '$':
5329 num = rootpid;
5330 goto numvar;
5331 case '?':
5332 num = exitstatus;
5333 goto numvar;
5334 case '#':
5335 num = shellparam.nparam;
5336 goto numvar;
5337 case '!':
5338 num = backgndpid;
5339 if (num == 0)
5340 return -1;
5341numvar:
5342 len = cvtnum(num);
5343 break;
5344 case '-':
5345 p = makestrspace(NOPTS, expdest);
5346 for (i = NOPTS - 1; i >= 0; i--) {
5347 if (optlist[i]) {
5348 USTPUTC(optletters(i), p);
5349 len++;
5350 }
5351 }
5352 expdest = p;
5353 break;
5354 case '@':
5355 if (sep)
5356 goto param;
5357 /* fall through */
5358 case '*':
5359 sep = ifsset() ? ifsval()[0] : ' ';
5360 if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
5361 sepq = 1;
5362param:
5363 if (!(ap = shellparam.p))
5364 return -1;
5365 while ((p = *ap++)) {
5366 size_t partlen;
5367
5368 partlen = strlen(p);
5369
5370 len += partlen;
5371 if (len > partlen && sep) {
5372 char *q;
5373
5374 len++;
5375 if (subtype == VSPLUS || subtype == VSLENGTH) {
5376 continue;
5377 }
5378 q = expdest;
5379 if (sepq)
5380 STPUTC(CTLESC, q);
5381 STPUTC(sep, q);
5382 expdest = q;
5383 }
5384
5385 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5386 memtodest(p, partlen, syntax, quotes);
5387 }
5388 return len;
5389 case '0':
5390 case '1':
5391 case '2':
5392 case '3':
5393 case '4':
5394 case '5':
5395 case '6':
5396 case '7':
5397 case '8':
5398 case '9':
5399 num = atoi(name);
5400 if (num < 0 || num > shellparam.nparam)
5401 return -1;
5402 p = num ? shellparam.p[num - 1] : arg0;
5403 goto value;
5404 default:
5405 p = lookupvar(name);
5406value:
5407 if (!p)
5408 return -1;
5409
5410 len = strlen(p);
5411 if (!(subtype == VSPLUS || subtype == VSLENGTH))
5412 memtodest(p, len, syntax, quotes);
5413 return len;
5414 }
5415
5416 if (subtype == VSPLUS || subtype == VSLENGTH)
5417 STADJUST(-len, expdest);
5418 return len;
5419}
5420
5421
5422/*
5423 * Record the fact that we have to scan this region of the
5424 * string for IFS characters.
5425 */
5426
5427static void
5428recordregion(int start, int end, int nulonly)
5429{
5430 struct ifsregion *ifsp;
5431
5432 if (ifslastp == NULL) {
5433 ifsp = &ifsfirst;
5434 } else {
5435 INTOFF;
5436 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
5437 ifsp->next = NULL;
5438 ifslastp->next = ifsp;
5439 INTON;
5440 }
5441 ifslastp = ifsp;
5442 ifslastp->begoff = start;
5443 ifslastp->endoff = end;
5444 ifslastp->nulonly = nulonly;
5445}
5446
5447
5448/*
5449 * Break the argument string into pieces based upon IFS and add the
5450 * strings to the argument list. The regions of the string to be
5451 * searched for IFS characters have been stored by recordregion.
5452 */
5453static void
5454ifsbreakup(char *string, struct arglist *arglist)
5455{
5456 struct ifsregion *ifsp;
5457 struct strlist *sp;
5458 char *start;
5459 char *p;
5460 char *q;
5461 const char *ifs, *realifs;
5462 int ifsspc;
5463 int nulonly;
5464
5465
5466 start = string;
5467 if (ifslastp != NULL) {
5468 ifsspc = 0;
5469 nulonly = 0;
5470 realifs = ifsset() ? ifsval() : defifs;
5471 ifsp = &ifsfirst;
5472 do {
5473 p = string + ifsp->begoff;
5474 nulonly = ifsp->nulonly;
5475 ifs = nulonly ? nullstr : realifs;
5476 ifsspc = 0;
5477 while (p < string + ifsp->endoff) {
5478 q = p;
5479 if (*p == CTLESC)
5480 p++;
5481 if (strchr(ifs, *p)) {
5482 if (!nulonly)
5483 ifsspc = (strchr(defifs, *p) != NULL);
5484 /* Ignore IFS whitespace at start */
5485 if (q == start && ifsspc) {
5486 p++;
5487 start = p;
5488 continue;
5489 }
5490 *q = '\0';
5491 sp = (struct strlist *)stalloc(sizeof *sp);
5492 sp->text = start;
5493 *arglist->lastp = sp;
5494 arglist->lastp = &sp->next;
5495 p++;
5496 if (!nulonly) {
5497 for (;;) {
5498 if (p >= string + ifsp->endoff) {
5499 break;
5500 }
5501 q = p;
5502 if (*p == CTLESC)
5503 p++;
5504 if (strchr(ifs, *p) == NULL ) {
5505 p = q;
5506 break;
5507 } else if (strchr(defifs, *p) == NULL) {
5508 if (ifsspc) {
5509 p++;
5510 ifsspc = 0;
5511 } else {
5512 p = q;
5513 break;
5514 }
5515 } else
5516 p++;
5517 }
5518 }
5519 start = p;
5520 } else
5521 p++;
5522 }
5523 } while ((ifsp = ifsp->next) != NULL);
5524 if (nulonly)
5525 goto add;
5526 }
5527
5528 if (!*start)
5529 return;
5530
5531add:
5532 sp = (struct strlist *)stalloc(sizeof *sp);
5533 sp->text = start;
5534 *arglist->lastp = sp;
5535 arglist->lastp = &sp->next;
5536}
5537
5538static void
5539ifsfree(void)
5540{
5541 struct ifsregion *p;
5542
5543 INTOFF;
5544 p = ifsfirst.next;
5545 do {
5546 struct ifsregion *ifsp;
5547 ifsp = p->next;
5548 ckfree(p);
5549 p = ifsp;
5550 } while (p);
5551 ifslastp = NULL;
5552 ifsfirst.next = NULL;
5553 INTON;
5554}
5555
5556static void expmeta(char *, char *);
5557static struct strlist *expsort(struct strlist *);
5558static struct strlist *msort(struct strlist *, int);
5559
5560static char *expdir;
5561
5562
5563static void
5564expandmeta(struct strlist *str, int flag)
5565{
5566 static const char metachars[] = {
5567 '*', '?', '[', 0
5568 };
5569 /* TODO - EXP_REDIR */
5570
5571 while (str) {
5572 struct strlist **savelastp;
5573 struct strlist *sp;
5574 char *p;
5575
5576 if (fflag)
5577 goto nometa;
5578 if (!strpbrk(str->text, metachars))
5579 goto nometa;
5580 savelastp = exparg.lastp;
5581
5582 INTOFF;
5583 p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
5584 {
5585 int i = strlen(str->text);
5586 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
5587 }
5588
5589 expmeta(expdir, p);
5590 ckfree(expdir);
5591 if (p != str->text)
5592 ckfree(p);
5593 INTON;
5594 if (exparg.lastp == savelastp) {
5595 /*
5596 * no matches
5597 */
5598nometa:
5599 *exparg.lastp = str;
5600 rmescapes(str->text);
5601 exparg.lastp = &str->next;
5602 } else {
5603 *exparg.lastp = NULL;
5604 *savelastp = sp = expsort(*savelastp);
5605 while (sp->next != NULL)
5606 sp = sp->next;
5607 exparg.lastp = &sp->next;
5608 }
5609 str = str->next;
5610 }
5611}
5612
5613/*
5614 * Add a file name to the list.
5615 */
5616
5617static void
5618addfname(const char *name)
5619{
5620 struct strlist *sp;
5621
5622 sp = (struct strlist *)stalloc(sizeof *sp);
5623 sp->text = sstrdup(name);
5624 *exparg.lastp = sp;
5625 exparg.lastp = &sp->next;
5626}
5627
5628
5629/*
5630 * Do metacharacter (i.e. *, ?, [...]) expansion.
5631 */
5632
5633static void
5634expmeta(char *enddir, char *name)
5635{
5636 char *p;
5637 const char *cp;
5638 char *start;
5639 char *endname;
5640 int metaflag;
5641 struct stat statb;
5642 DIR *dirp;
5643 struct dirent *dp;
5644 int atend;
5645 int matchdot;
5646
5647 metaflag = 0;
5648 start = name;
5649 for (p = name; *p; p++) {
5650 if (*p == '*' || *p == '?')
5651 metaflag = 1;
5652 else if (*p == '[') {
5653 char *q = p + 1;
5654 if (*q == '!')
5655 q++;
5656 for (;;) {
5657 if (*q == '\\')
5658 q++;
5659 if (*q == '/' || *q == '\0')
5660 break;
5661 if (*++q == ']') {
5662 metaflag = 1;
5663 break;
5664 }
5665 }
5666 } else if (*p == '\\')
5667 p++;
5668 else if (*p == '/') {
5669 if (metaflag)
5670 goto out;
5671 start = p + 1;
5672 }
5673 }
5674out:
5675 if (metaflag == 0) { /* we've reached the end of the file name */
5676 if (enddir != expdir)
5677 metaflag++;
5678 p = name;
5679 do {
5680 if (*p == '\\')
5681 p++;
5682 *enddir++ = *p;
5683 } while (*p++);
5684 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
5685 addfname(expdir);
5686 return;
5687 }
5688 endname = p;
5689 if (name < start) {
5690 p = name;
5691 do {
5692 if (*p == '\\')
5693 p++;
5694 *enddir++ = *p++;
5695 } while (p < start);
5696 }
5697 if (enddir == expdir) {
5698 cp = ".";
5699 } else if (enddir == expdir + 1 && *expdir == '/') {
5700 cp = "/";
5701 } else {
5702 cp = expdir;
5703 enddir[-1] = '\0';
5704 }
5705 if ((dirp = opendir(cp)) == NULL)
5706 return;
5707 if (enddir != expdir)
5708 enddir[-1] = '/';
5709 if (*endname == 0) {
5710 atend = 1;
5711 } else {
5712 atend = 0;
5713 *endname++ = '\0';
5714 }
5715 matchdot = 0;
5716 p = start;
5717 if (*p == '\\')
5718 p++;
5719 if (*p == '.')
5720 matchdot++;
5721 while (! intpending && (dp = readdir(dirp)) != NULL) {
5722 if (dp->d_name[0] == '.' && ! matchdot)
5723 continue;
5724 if (pmatch(start, dp->d_name)) {
5725 if (atend) {
5726 scopy(dp->d_name, enddir);
5727 addfname(expdir);
5728 } else {
5729 for (p = enddir, cp = dp->d_name;
5730 (*p++ = *cp++) != '\0';)
5731 continue;
5732 p[-1] = '/';
5733 expmeta(p, endname);
5734 }
5735 }
5736 }
5737 closedir(dirp);
5738 if (! atend)
5739 endname[-1] = '/';
5740}
5741
5742/*
5743 * Sort the results of file name expansion. It calculates the number of
5744 * strings to sort and then calls msort (short for merge sort) to do the
5745 * work.
5746 */
5747
5748static struct strlist *
5749expsort(struct strlist *str)
5750{
5751 int len;
5752 struct strlist *sp;
5753
5754 len = 0;
5755 for (sp = str ; sp ; sp = sp->next)
5756 len++;
5757 return msort(str, len);
5758}
5759
5760
5761static struct strlist *
5762msort(struct strlist *list, int len)
5763{
5764 struct strlist *p, *q = NULL;
5765 struct strlist **lpp;
5766 int half;
5767 int n;
5768
5769 if (len <= 1)
5770 return list;
5771 half = len >> 1;
5772 p = list;
5773 for (n = half ; --n >= 0 ; ) {
5774 q = p;
5775 p = p->next;
5776 }
5777 q->next = NULL; /* terminate first half of list */
5778 q = msort(list, half); /* sort first half of list */
5779 p = msort(p, len - half); /* sort second half */
5780 lpp = &list;
5781 for (;;) {
5782#ifdef CONFIG_LOCALE_SUPPORT
5783 if (strcoll(p->text, q->text) < 0)
5784#else
5785 if (strcmp(p->text, q->text) < 0)
5786#endif
5787 {
5788 *lpp = p;
5789 lpp = &p->next;
5790 if ((p = *lpp) == NULL) {
5791 *lpp = q;
5792 break;
5793 }
5794 } else {
5795 *lpp = q;
5796 lpp = &q->next;
5797 if ((q = *lpp) == NULL) {
5798 *lpp = p;
5799 break;
5800 }
5801 }
5802 }
5803 return list;
5804}
5805
5806
5807/*
5808 * Returns true if the pattern matches the string.
5809 */
5810
5811static inline int
5812patmatch(char *pattern, const char *string)
5813{
5814 return pmatch(preglob(pattern, 0, 0), string);
5815}
5816
5817
5818/*
5819 * Remove any CTLESC characters from a string.
5820 */
5821
5822static char *
5823_rmescapes(char *str, int flag)
5824{
5825 char *p, *q, *r;
5826 static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
5827 unsigned inquotes;
5828 int notescaped;
5829 int globbing;
5830
5831 p = strpbrk(str, qchars);
5832 if (!p) {
5833 return str;
5834 }
5835 q = p;
5836 r = str;
5837 if (flag & RMESCAPE_ALLOC) {
5838 size_t len = p - str;
5839 size_t fulllen = len + strlen(p) + 1;
5840
5841 if (flag & RMESCAPE_GROW) {
5842 r = makestrspace(fulllen, expdest);
5843 } else if (flag & RMESCAPE_HEAP) {
5844 r = ckmalloc(fulllen);
5845 } else {
5846 r = stalloc(fulllen);
5847 }
5848 q = r;
5849 if (len > 0) {
5850 q = mempcpy(q, str, len);
5851 }
5852 }
5853 inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
5854 globbing = flag & RMESCAPE_GLOB;
5855 notescaped = globbing;
5856 while (*p) {
5857 if (*p == CTLQUOTEMARK) {
5858 inquotes = ~inquotes;
5859 p++;
5860 notescaped = globbing;
5861 continue;
5862 }
5863 if (*p == '\\') {
5864 /* naked back slash */
5865 notescaped = 0;
5866 goto copy;
5867 }
5868 if (*p == CTLESC) {
5869 p++;
5870 if (notescaped && inquotes && *p != '/') {
5871 *q++ = '\\';
5872 }
5873 }
5874 notescaped = globbing;
5875copy:
5876 *q++ = *p++;
5877 }
5878 *q = '\0';
5879 if (flag & RMESCAPE_GROW) {
5880 expdest = r;
5881 STADJUST(q - r + 1, expdest);
5882 }
5883 return r;
5884}
5885
5886
5887/*
5888 * See if a pattern matches in a case statement.
5889 */
5890
5891int
5892casematch(union node *pattern, char *val)
5893{
5894 struct stackmark smark;
5895 int result;
5896
5897 setstackmark(&smark);
5898 argbackq = pattern->narg.backquote;
5899 STARTSTACKSTR(expdest);
5900 ifslastp = NULL;
5901 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
5902 STACKSTRNUL(expdest);
5903 result = patmatch(stackblock(), val);
5904 popstackmark(&smark);
5905 return result;
5906}
5907
5908/*
5909 * Our own itoa().
5910 */
5911
5912static int
5913cvtnum(arith_t num)
5914{
5915 int len;
5916
5917 expdest = makestrspace(32, expdest);
5918#ifdef CONFIG_ASH_MATH_SUPPORT_64
5919 len = fmtstr(expdest, 32, "%lld", (long long) num);
5920#else
5921 len = fmtstr(expdest, 32, "%ld", num);
5922#endif
5923 STADJUST(len, expdest);
5924 return len;
5925}
5926
5927static void
5928varunset(const char *end, const char *var, const char *umsg, int varflags)
5929{
5930 const char *msg;
5931 const char *tail;
5932
5933 tail = nullstr;
5934 msg = "parameter not set";
5935 if (umsg) {
5936 if (*end == CTLENDVAR) {
5937 if (varflags & VSNUL)
5938 tail = " or null";
5939 } else
5940 msg = umsg;
5941 }
5942 error("%.*s: %s%s", end - var - 1, var, msg, tail);
5943}
5944
5945
5946/* $NetBSD: input.c,v 1.37 2002/11/24 22:35:40 christos Exp $ */
5947
5948/*
5949 * This implements the input routines used by the parser.
5950 */
5951
5952#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
5953#define IBUFSIZ (BUFSIZ + 1)
5954
5955static void pushfile(void);
5956
5957/*
5958 * Read a character from the script, returning PEOF on end of file.
5959 * Nul characters in the input are silently discarded.
5960 */
5961
5962#define pgetc_as_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer())
5963
5964#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
5965#define pgetc_macro() pgetc()
5966static int
5967pgetc(void)
5968{
5969 return pgetc_as_macro();
5970}
5971#else
5972#define pgetc_macro() pgetc_as_macro()
5973static int
5974pgetc(void)
5975{
5976 return pgetc_macro();
5977}
5978#endif
5979
5980
5981/*
5982 * Same as pgetc(), but ignores PEOA.
5983 */
5984#ifdef CONFIG_ASH_ALIAS
5985static int pgetc2(void)
5986{
5987 int c;
5988
5989 do {
5990 c = pgetc_macro();
5991 } while (c == PEOA);
5992 return c;
5993}
5994#else
5995static inline int pgetc2(void)
5996{
5997 return pgetc_macro();
5998}
5999#endif
6000
6001/*
6002 * Read a line from the script.
6003 */
6004
6005static inline char *
6006pfgets(char *line, int len)
6007{
6008 char *p = line;
6009 int nleft = len;
6010 int c;
6011
6012 while (--nleft > 0) {
6013 c = pgetc2();
6014 if (c == PEOF) {
6015 if (p == line)
6016 return NULL;
6017 break;
6018 }
6019 *p++ = c;
6020 if (c == '\n')
6021 break;
6022 }
6023 *p = '\0';
6024 return line;
6025}
6026
6027
6028
6029#ifdef CONFIG_FEATURE_COMMAND_EDITING
6030static const char *cmdedit_prompt;
6031static inline void putprompt(const char *s)
6032{
6033 cmdedit_prompt = s;
6034}
6035#else
6036static inline void putprompt(const char *s)
6037{
6038 out2str(s);
6039}
6040#endif
6041
6042static inline int
6043preadfd(void)
6044{
6045 int nr;
6046 char *buf = parsefile->buf;
6047 parsenextc = buf;
6048
6049retry:
6050#ifdef CONFIG_FEATURE_COMMAND_EDITING
6051 if (!iflag || parsefile->fd)
6052 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
6053 else {
6054#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
6055 cmdedit_path_lookup = pathval();
6056#endif
6057 nr = cmdedit_read_input((char *) cmdedit_prompt, buf);
6058 if(nr == 0) {
6059 /* Ctrl+C presend */
6060 if(trap[SIGINT]) {
6061 buf[0] = '\n';
6062 buf[1] = 0;
6063 raise(SIGINT);
6064 return 1;
6065 }
6066 goto retry;
6067 }
6068 if(nr < 0 && errno == 0) {
6069 /* Ctrl+D presend */
6070 nr = 0;
6071 }
6072 }
6073#else
6074 nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
6075#endif
6076
6077 if (nr < 0) {
6078 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
6079 int flags = fcntl(0, F_GETFL, 0);
6080 if (flags >= 0 && flags & O_NONBLOCK) {
6081 flags &=~ O_NONBLOCK;
6082 if (fcntl(0, F_SETFL, flags) >= 0) {
6083 out2str("sh: turning off NDELAY mode\n");
6084 goto retry;
6085 }
6086 }
6087 }
6088 }
6089 return nr;
6090}
6091
6092/*
6093 * Refill the input buffer and return the next input character:
6094 *
6095 * 1) If a string was pushed back on the input, pop it;
6096 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
6097 * from a string so we can't refill the buffer, return EOF.
6098 * 3) If the is more stuff in this buffer, use it else call read to fill it.
6099 * 4) Process input up to the next newline, deleting nul characters.
6100 */
6101
6102int
6103preadbuffer(void)
6104{
6105 char *p, *q;
6106 int more;
6107 char savec;
6108
6109 while (parsefile->strpush) {
6110#ifdef CONFIG_ASH_ALIAS
6111 if (parsenleft == -1 && parsefile->strpush->ap &&
6112 parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
6113 return PEOA;
6114 }
6115#endif
6116 popstring();
6117 if (--parsenleft >= 0)
6118 return (*parsenextc++);
6119 }
6120 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
6121 return PEOF;
6122 flushall();
6123
6124again:
6125 if (parselleft <= 0) {
6126 if ((parselleft = preadfd()) <= 0) {
6127 parselleft = parsenleft = EOF_NLEFT;
6128 return PEOF;
6129 }
6130 }
6131
6132 q = p = parsenextc;
6133
6134 /* delete nul characters */
6135 for (more = 1; more;) {
6136 switch (*p) {
6137 case '\0':
6138 p++; /* Skip nul */
6139 goto check;
6140
6141 case '\n':
6142 parsenleft = q - parsenextc;
6143 more = 0; /* Stop processing here */
6144 break;
6145
6146 }
6147
6148 *q++ = *p++;
6149check:
6150 if (--parselleft <= 0 && more) {
6151 parsenleft = q - parsenextc - 1;
6152 if (parsenleft < 0)
6153 goto again;
6154 more = 0;
6155 }
6156 }
6157
6158 savec = *q;
6159 *q = '\0';
6160
6161 if (vflag) {
6162 out2str(parsenextc);
6163 }
6164
6165 *q = savec;
6166
6167 return *parsenextc++;
6168}
6169
6170/*
6171 * Undo the last call to pgetc. Only one character may be pushed back.
6172 * PEOF may be pushed back.
6173 */
6174
6175void
6176pungetc(void)
6177{
6178 parsenleft++;
6179 parsenextc--;
6180}
6181
6182/*
6183 * Push a string back onto the input at this current parsefile level.
6184 * We handle aliases this way.
6185 */
6186void
6187pushstring(char *s, void *ap)
6188{
6189 struct strpush *sp;
6190 size_t len;
6191
6192 len = strlen(s);
6193 INTOFF;
6194/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
6195 if (parsefile->strpush) {
6196 sp = ckmalloc(sizeof (struct strpush));
6197 sp->prev = parsefile->strpush;
6198 parsefile->strpush = sp;
6199 } else
6200 sp = parsefile->strpush = &(parsefile->basestrpush);
6201 sp->prevstring = parsenextc;
6202 sp->prevnleft = parsenleft;
6203#ifdef CONFIG_ASH_ALIAS
6204 sp->ap = (struct alias *)ap;
6205 if (ap) {
6206 ((struct alias *)ap)->flag |= ALIASINUSE;
6207 sp->string = s;
6208 }
6209#endif
6210 parsenextc = s;
6211 parsenleft = len;
6212 INTON;
6213}
6214
6215void
6216popstring(void)
6217{
6218 struct strpush *sp = parsefile->strpush;
6219
6220 INTOFF;
6221#ifdef CONFIG_ASH_ALIAS
6222 if (sp->ap) {
6223 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
6224 checkkwd |= CHKALIAS;
6225 }
6226 if (sp->string != sp->ap->val) {
6227 ckfree(sp->string);
6228 }
6229 sp->ap->flag &= ~ALIASINUSE;
6230 if (sp->ap->flag & ALIASDEAD) {
6231 unalias(sp->ap->name);
6232 }
6233 }
6234#endif
6235 parsenextc = sp->prevstring;
6236 parsenleft = sp->prevnleft;
6237/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
6238 parsefile->strpush = sp->prev;
6239 if (sp != &(parsefile->basestrpush))
6240 ckfree(sp);
6241 INTON;
6242}
6243
6244/*
6245 * Set the input to take input from a file. If push is set, push the
6246 * old input onto the stack first.
6247 */
6248
6249void
6250setinputfile(const char *fname, int push)
6251{
6252 int fd;
6253 int fd2;
6254
6255 INTOFF;
6256 if ((fd = open(fname, O_RDONLY)) < 0)
6257 error("Can't open %s", fname);
6258 if (fd < 10) {
6259 fd2 = copyfd(fd, 10);
6260 close(fd);
6261 if (fd2 < 0)
6262 error("Out of file descriptors");
6263 fd = fd2;
6264 }
6265 setinputfd(fd, push);
6266 INTON;
6267}
6268
6269
6270/*
6271 * Like setinputfile, but takes an open file descriptor. Call this with
6272 * interrupts off.
6273 */
6274
6275static void
6276setinputfd(int fd, int push)
6277{
6278 (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
6279 if (push) {
6280 pushfile();
6281 parsefile->buf = 0;
6282 }
6283 parsefile->fd = fd;
6284 if (parsefile->buf == NULL)
6285 parsefile->buf = ckmalloc(IBUFSIZ);
6286 parselleft = parsenleft = 0;
6287 plinno = 1;
6288}
6289
6290
6291/*
6292 * Like setinputfile, but takes input from a string.
6293 */
6294
6295static void
6296setinputstring(char *string)
6297{
6298 INTOFF;
6299 pushfile();
6300 parsenextc = string;
6301 parsenleft = strlen(string);
6302 parsefile->buf = NULL;
6303 plinno = 1;
6304 INTON;
6305}
6306
6307
6308/*
6309 * To handle the "." command, a stack of input files is used. Pushfile
6310 * adds a new entry to the stack and popfile restores the previous level.
6311 */
6312
6313static void
6314pushfile(void)
6315{
6316 struct parsefile *pf;
6317
6318 parsefile->nleft = parsenleft;
6319 parsefile->lleft = parselleft;
6320 parsefile->nextc = parsenextc;
6321 parsefile->linno = plinno;
6322 pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
6323 pf->prev = parsefile;
6324 pf->fd = -1;
6325 pf->strpush = NULL;
6326 pf->basestrpush.prev = NULL;
6327 parsefile = pf;
6328}
6329
6330
6331static void
6332popfile(void)
6333{
6334 struct parsefile *pf = parsefile;
6335
6336 INTOFF;
6337 if (pf->fd >= 0)
6338 close(pf->fd);
6339 if (pf->buf)
6340 ckfree(pf->buf);
6341 while (pf->strpush)
6342 popstring();
6343 parsefile = pf->prev;
6344 ckfree(pf);
6345 parsenleft = parsefile->nleft;
6346 parselleft = parsefile->lleft;
6347 parsenextc = parsefile->nextc;
6348 plinno = parsefile->linno;
6349 INTON;
6350}
6351
6352
6353/*
6354 * Return to top level.
6355 */
6356
6357static void
6358popallfiles(void)
6359{
6360 while (parsefile != &basepf)
6361 popfile();
6362}
6363
6364
6365/*
6366 * Close the file(s) that the shell is reading commands from. Called
6367 * after a fork is done.
6368 */
6369
6370static void
6371closescript(void)
6372{
6373 popallfiles();
6374 if (parsefile->fd > 0) {
6375 close(parsefile->fd);
6376 parsefile->fd = 0;
6377 }
6378}
6379
6380/* $NetBSD: jobs.c,v 1.56 2002/11/25 12:13:03 agc Exp $ */
6381
6382/* mode flags for set_curjob */
6383#define CUR_DELETE 2
6384#define CUR_RUNNING 1
6385#define CUR_STOPPED 0
6386
6387/* mode flags for dowait */
6388#define DOWAIT_NORMAL 0
6389#define DOWAIT_BLOCK 1
6390
6391/* array of jobs */
6392static struct job *jobtab;
6393/* size of array */
6394static unsigned njobs;
6395#if JOBS
6396/* pgrp of shell on invocation */
6397static int initialpgrp;
6398static int ttyfd = -1;
6399#endif
6400/* current job */
6401static struct job *curjob;
6402/* number of presumed living untracked jobs */
6403static int jobless;
6404
6405static void set_curjob(struct job *, unsigned);
6406#if JOBS
6407static int restartjob(struct job *, int);
6408static void xtcsetpgrp(int, pid_t);
6409static char *commandtext(union node *);
6410static void cmdlist(union node *, int);
6411static void cmdtxt(union node *);
6412static void cmdputs(const char *);
6413static void showpipe(struct job *, FILE *);
6414#endif
6415static int sprint_status(char *, int, int);
6416static void freejob(struct job *);
6417static struct job *getjob(const char *, int);
6418static struct job *growjobtab(void);
6419static void forkchild(struct job *, union node *, int);
6420static void forkparent(struct job *, union node *, int, pid_t);
6421static int dowait(int, struct job *);
6422static int getstatus(struct job *);
6423
6424static void
6425set_curjob(struct job *jp, unsigned mode)
6426{
6427 struct job *jp1;
6428 struct job **jpp, **curp;
6429
6430 /* first remove from list */
6431 jpp = curp = &curjob;
6432 do {
6433 jp1 = *jpp;
6434 if (jp1 == jp)
6435 break;
6436 jpp = &jp1->prev_job;
6437 } while (1);
6438 *jpp = jp1->prev_job;
6439
6440 /* Then re-insert in correct position */
6441 jpp = curp;
6442 switch (mode) {
6443 default:
6444#ifdef DEBUG
6445 abort();
6446#endif
6447 case CUR_DELETE:
6448 /* job being deleted */
6449 break;
6450 case CUR_RUNNING:
6451 /* newly created job or backgrounded job,
6452 put after all stopped jobs. */
6453 do {
6454 jp1 = *jpp;
6455#ifdef JOBS
6456 if (!jp1 || jp1->state != JOBSTOPPED)
6457#endif
6458 break;
6459 jpp = &jp1->prev_job;
6460 } while (1);
6461 /* FALLTHROUGH */
6462#ifdef JOBS
6463 case CUR_STOPPED:
6464#endif
6465 /* newly stopped job - becomes curjob */
6466 jp->prev_job = *jpp;
6467 *jpp = jp;
6468 break;
6469 }
6470}
6471
6472#if JOBS
6473/*
6474 * Turn job control on and off.
6475 *
6476 * Note: This code assumes that the third arg to ioctl is a character
6477 * pointer, which is true on Berkeley systems but not System V. Since
6478 * System V doesn't have job control yet, this isn't a problem now.
6479 *
6480 * Called with interrupts off.
6481 */
6482
6483void
6484setjobctl(int on)
6485{
6486 int fd;
6487 int pgrp;
6488
6489 if (on == jobctl || rootshell == 0)
6490 return;
6491 if (on) {
6492 int ofd;
6493 ofd = fd = open(_PATH_TTY, O_RDWR);
6494 if (fd < 0) {
6495 fd += 3;
6496 while (!isatty(fd) && --fd >= 0)
6497 ;
6498 }
6499 fd = fcntl(fd, F_DUPFD, 10);
6500 close(ofd);
6501 if (fd < 0)
6502 goto out;
6503 fcntl(fd, F_SETFD, FD_CLOEXEC);
6504 do { /* while we are in the background */
6505 if ((pgrp = tcgetpgrp(fd)) < 0) {
6506out:
6507 sh_warnx("can't access tty; job control turned off");
6508 mflag = on = 0;
6509 goto close;
6510 }
6511 if (pgrp == getpgrp())
6512 break;
6513 killpg(0, SIGTTIN);
6514 } while (1);
6515 initialpgrp = pgrp;
6516
6517 setsignal(SIGTSTP);
6518 setsignal(SIGTTOU);
6519 setsignal(SIGTTIN);
6520 pgrp = rootpid;
6521 setpgid(0, pgrp);
6522 xtcsetpgrp(fd, pgrp);
6523 } else {
6524 /* turning job control off */
6525 fd = ttyfd;
6526 pgrp = initialpgrp;
6527 xtcsetpgrp(fd, pgrp);
6528 setpgid(0, pgrp);
6529 setsignal(SIGTSTP);
6530 setsignal(SIGTTOU);
6531 setsignal(SIGTTIN);
6532close:
6533 close(fd);
6534 fd = -1;
6535 }
6536 ttyfd = fd;
6537 jobctl = on;
6538}
6539
6540static int
6541killcmd(int argc, char **argv)
6542{
6543 int signo = -1;
6544 int list = 0;
6545 int i;
6546 pid_t pid;
6547 struct job *jp;
6548
6549 if (argc <= 1) {
6550usage:
6551 error(
6552"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
6553"kill -l [exitstatus]"
6554 );
6555 }
6556
6557 if (**++argv == '-') {
6558 signo = decode_signal(*argv + 1, 1);
6559 if (signo < 0) {
6560 int c;
6561
6562 while ((c = nextopt("ls:")) != '\0')
6563 switch (c) {
6564 default:
6565#ifdef DEBUG
6566 abort();
6567#endif
6568 case 'l':
6569 list = 1;
6570 break;
6571 case 's':
6572 signo = decode_signal(optionarg, 1);
6573 if (signo < 0) {
6574 error(
6575 "invalid signal number or name: %s",
6576 optionarg
6577 );
6578 }
6579 break;
6580 }
6581 argv = argptr;
6582 } else
6583 argv++;
6584 }
6585
6586 if (!list && signo < 0)
6587 signo = SIGTERM;
6588
6589 if ((signo < 0 || !*argv) ^ list) {
6590 goto usage;
6591 }
6592
6593 if (list) {
6594 const char *name;
6595
6596 if (!*argv) {
6597 for (i = 1; i < NSIG; i++) {
6598 name = u_signal_names(0, &i, 1);
6599 if (name)
6600 out1fmt(snlfmt, name);
6601 }
6602 return 0;
6603 }
6604 name = u_signal_names(*argptr, &signo, -1);
6605 if (name)
6606 out1fmt(snlfmt, name);
6607 else
6608 error("invalid signal number or exit status: %s", *argptr);
6609 return 0;
6610 }
6611
6612 i = 0;
6613 do {
6614 if (**argv == '%') {
6615 jp = getjob(*argv, 0);
6616 pid = -jp->ps[0].pid;
6617 } else
6618 pid = number(*argv);
6619 if (kill(pid, signo) != 0) {
6620 sh_warnx("%m\n");
6621 i = 1;
6622 }
6623 } while (*++argv);
6624
6625 return i;
6626}
6627#endif /* JOBS */
6628
6629#if defined(JOBS) || defined(DEBUG)
6630static int
6631jobno(const struct job *jp)
6632{
6633 return jp - jobtab + 1;
6634}
6635#endif
6636
6637#ifdef JOBS
6638static int
6639fgcmd(int argc, char **argv)
6640{
6641 struct job *jp;
6642 FILE *out;
6643 int mode;
6644 int retval;
6645
6646 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
6647 nextopt(nullstr);
6648 argv = argptr;
6649 out = stdout;
6650 do {
6651 jp = getjob(*argv, 1);
6652 if (mode == FORK_BG) {
6653 set_curjob(jp, CUR_RUNNING);
6654 fprintf(out, "[%d] ", jobno(jp));
6655 }
6656 outstr(jp->ps->cmd, out);
6657 showpipe(jp, out);
6658 retval = restartjob(jp, mode);
6659 } while (*argv && *++argv);
6660 return retval;
6661}
6662
6663static int bgcmd(int, char **) __attribute__((__alias__("fgcmd")));
6664
6665
6666static int
6667restartjob(struct job *jp, int mode)
6668{
6669 struct procstat *ps;
6670 int i;
6671 int status;
6672 pid_t pgid;
6673
6674 INTOFF;
6675 if (jp->state == JOBDONE)
6676 goto out;
6677 jp->state = JOBRUNNING;
6678 pgid = jp->ps->pid;
6679 if (mode == FORK_FG)
6680 xtcsetpgrp(ttyfd, pgid);
6681 killpg(pgid, SIGCONT);
6682 ps = jp->ps;
6683 i = jp->nprocs;
6684 do {
6685 if (WIFSTOPPED(ps->status)) {
6686 ps->status = -1;
6687 }
6688 } while (ps++, --i);
6689out:
6690 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
6691 INTON;
6692 return status;
6693}
6694#endif
6695
6696static int
6697sprint_status(char *s, int status, int sigonly)
6698{
6699 int col;
6700 int st;
6701
6702 col = 0;
6703 if (!WIFEXITED(status)) {
6704#if JOBS
6705 if (WIFSTOPPED(status))
6706 st = WSTOPSIG(status);
6707 else
6708#endif
6709 st = WTERMSIG(status);
6710 if (sigonly) {
6711 if (st == SIGINT || st == SIGPIPE)
6712 goto out;
6713#if JOBS
6714 if (WIFSTOPPED(status))
6715 goto out;
6716#endif
6717 }
6718 st &= 0x7f;
6719 col = fmtstr(s, 32, strsignal(st));
6720 if (WCOREDUMP(status)) {
6721 col += fmtstr(s + col, 16, " (core dumped)");
6722 }
6723 } else if (!sigonly) {
6724 st = WEXITSTATUS(status);
6725 if (st)
6726 col = fmtstr(s, 16, "Done(%d)", st);
6727 else
6728 col = fmtstr(s, 16, "Done");
6729 }
6730
6731out:
6732 return col;
6733}
6734
6735#if JOBS
6736static void
6737showjob(FILE *out, struct job *jp, int mode)
6738{
6739 struct procstat *ps;
6740 struct procstat *psend;
6741 int col;
6742 int indent;
6743 char s[80];
6744
6745 ps = jp->ps;
6746
6747 if (mode & SHOW_PGID) {
6748 /* just output process (group) id of pipeline */
6749 fprintf(out, "%d\n", ps->pid);
6750 return;
6751 }
6752
6753 col = fmtstr(s, 16, "[%d] ", jobno(jp));
6754 indent = col;
6755
6756 if (jp == curjob)
6757 s[col - 2] = '+';
6758 else if (curjob && jp == curjob->prev_job)
6759 s[col - 2] = '-';
6760
6761 if (mode & SHOW_PID)
6762 col += fmtstr(s + col, 16, "%d ", ps->pid);
6763
6764 psend = ps + jp->nprocs;
6765
6766 if (jp->state == JOBRUNNING) {
6767 scopy("Running", s + col);
6768 col += strlen("Running");
6769 } else {
6770 int status = psend[-1].status;
6771#if JOBS
6772 if (jp->state == JOBSTOPPED)
6773 status = jp->stopstatus;
6774#endif
6775 col += sprint_status(s + col, status, 0);
6776 }
6777
6778 goto start;
6779
6780 do {
6781 /* for each process */
6782 col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
6783
6784start:
6785 fprintf(out, "%s%*c%s",
6786 s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
6787 );
6788 if (!(mode & SHOW_PID)) {
6789 showpipe(jp, out);
6790 break;
6791 }
6792 if (++ps == psend) {
6793 outcslow('\n', out);
6794 break;
6795 }
6796 } while (1);
6797
6798 jp->changed = 0;
6799
6800 if (jp->state == JOBDONE) {
6801 TRACE(("showjob: freeing job %d\n", jobno(jp)));
6802 freejob(jp);
6803 }
6804}
6805
6806
6807static int
6808jobscmd(int argc, char **argv)
6809{
6810 int mode, m;
6811 FILE *out;
6812
6813 mode = 0;
6814 while ((m = nextopt("lp")))
6815 if (m == 'l')
6816 mode = SHOW_PID;
6817 else
6818 mode = SHOW_PGID;
6819
6820 out = stdout;
6821 argv = argptr;
6822 if (*argv)
6823 do
6824 showjob(out, getjob(*argv,0), mode);
6825 while (*++argv);
6826 else
6827 showjobs(out, mode);
6828
6829 return 0;
6830}
6831
6832
6833/*
6834 * Print a list of jobs. If "change" is nonzero, only print jobs whose
6835 * statuses have changed since the last call to showjobs.
6836 */
6837
6838static void
6839showjobs(FILE *out, int mode)
6840{
6841 struct job *jp;
6842
6843 TRACE(("showjobs(%x) called\n", mode));
6844
6845 /* If not even one one job changed, there is nothing to do */
6846 while (dowait(DOWAIT_NORMAL, NULL) > 0)
6847 continue;
6848
6849 for (jp = curjob; jp; jp = jp->prev_job) {
6850 if (!(mode & SHOW_CHANGED) || jp->changed)
6851 showjob(out, jp, mode);
6852 }
6853}
6854#endif /* JOBS */
6855
6856/*
6857 * Mark a job structure as unused.
6858 */
6859
6860static void
6861freejob(struct job *jp)
6862{
6863 struct procstat *ps;
6864 int i;
6865
6866 INTOFF;
6867 for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
6868 if (ps->cmd != nullstr)
6869 ckfree(ps->cmd);
6870 }
6871 if (jp->ps != &jp->ps0)
6872 ckfree(jp->ps);
6873 jp->used = 0;
6874 set_curjob(jp, CUR_DELETE);
6875 INTON;
6876}
6877
6878
6879static int
6880waitcmd(int argc, char **argv)
6881{
6882 struct job *job;
6883 int retval;
6884 struct job *jp;
6885
6886 EXSIGON();
6887
6888 nextopt(nullstr);
6889 retval = 0;
6890
6891 argv = argptr;
6892 if (!*argv) {
6893 /* wait for all jobs */
6894 for (;;) {
6895 jp = curjob;
6896 while (1) {
6897 if (!jp) {
6898 /* no running procs */
6899 goto out;
6900 }
6901 if (jp->state == JOBRUNNING)
6902 break;
6903 jp->waited = 1;
6904 jp = jp->prev_job;
6905 }
6906 dowait(DOWAIT_BLOCK, 0);
6907 }
6908 }
6909
6910 retval = 127;
6911 do {
6912 if (**argv != '%') {
6913 pid_t pid = number(*argv);
6914 job = curjob;
6915 goto start;
6916 do {
6917 if (job->ps[job->nprocs - 1].pid == pid)
6918 break;
6919 job = job->prev_job;
6920start:
6921 if (!job)
6922 goto repeat;
6923 } while (1);
6924 } else
6925 job = getjob(*argv, 0);
6926 /* loop until process terminated or stopped */
6927 while (job->state == JOBRUNNING)
6928 dowait(DOWAIT_BLOCK, 0);
6929 job->waited = 1;
6930 retval = getstatus(job);
6931repeat:
6932 ;
6933 } while (*++argv);
6934
6935out:
6936 return retval;
6937}
6938
6939
6940/*
6941 * Convert a job name to a job structure.
6942 */
6943
6944static struct job *
6945getjob(const char *name, int getctl)
6946{
6947 struct job *jp;
6948 struct job *found;
6949 const char *err_msg = "No such job: %s";
6950 unsigned num;
6951 int c;
6952 const char *p;
6953 char *(*match)(const char *, const char *);
6954
6955 jp = curjob;
6956 p = name;
6957 if (!p)
6958 goto currentjob;
6959
6960 if (*p != '%')
6961 goto err;
6962
6963 c = *++p;
6964 if (!c)
6965 goto currentjob;
6966
6967 if (!p[1]) {
6968 if (c == '+' || c == '%') {
6969currentjob:
6970 err_msg = "No current job";
6971 goto check;
6972 } else if (c == '-') {
6973 if (jp)
6974 jp = jp->prev_job;
6975 err_msg = "No previous job";
6976check:
6977 if (!jp)
6978 goto err;
6979 goto gotit;
6980 }
6981 }
6982
6983 if (is_number(p)) {
6984 num = atoi(p);
6985 if (num < njobs) {
6986 jp = jobtab + num - 1;
6987 if (jp->used)
6988 goto gotit;
6989 goto err;
6990 }
6991 }
6992
6993 match = prefix;
6994 if (*p == '?') {
6995 match = strstr;
6996 p++;
6997 }
6998
6999 found = 0;
7000 while (1) {
7001 if (!jp)
7002 goto err;
7003 if (match(jp->ps[0].cmd, p)) {
7004 if (found)
7005 goto err;
7006 found = jp;
7007 err_msg = "%s: ambiguous";
7008 }
7009 jp = jp->prev_job;
7010 }
7011
7012gotit:
7013#if JOBS
7014 err_msg = "job %s not created under job control";
7015 if (getctl && jp->jobctl == 0)
7016 goto err;
7017#endif
7018 return jp;
7019err:
7020 error(err_msg, name);
7021}
7022
7023
7024/*
7025 * Return a new job structure.
7026 * Called with interrupts off.
7027 */
7028
7029static struct job *
7030makejob(union node *node, int nprocs)
7031{
7032 int i;
7033 struct job *jp;
7034
7035 for (i = njobs, jp = jobtab ; ; jp++) {
7036 if (--i < 0) {
7037 jp = growjobtab();
7038 break;
7039 }
7040 if (jp->used == 0)
7041 break;
7042 if (jp->state != JOBDONE || !jp->waited)
7043 continue;
7044#if JOBS
7045 if (jobctl)
7046 continue;
7047#endif
7048 freejob(jp);
7049 break;
7050 }
7051 memset(jp, 0, sizeof(*jp));
7052#if JOBS
7053 if (jobctl)
7054 jp->jobctl = 1;
7055#endif
7056 jp->prev_job = curjob;
7057 curjob = jp;
7058 jp->used = 1;
7059 jp->ps = &jp->ps0;
7060 if (nprocs > 1) {
7061 jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
7062 }
7063 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
7064 jobno(jp)));
7065 return jp;
7066}
7067
7068static struct job *
7069growjobtab(void)
7070{
7071 size_t len;
7072 ptrdiff_t offset;
7073 struct job *jp, *jq;
7074
7075 len = njobs * sizeof(*jp);
7076 jq = jobtab;
7077 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
7078
7079 offset = (char *)jp - (char *)jq;
7080 if (offset) {
7081 /* Relocate pointers */
7082 size_t l = len;
7083
7084 jq = (struct job *)((char *)jq + l);
7085 while (l) {
7086 l -= sizeof(*jp);
7087 jq--;
7088#define joff(p) ((struct job *)((char *)(p) + l))
7089#define jmove(p) (p) = (void *)((char *)(p) + offset)
7090 if (xlikely(joff(jp)->ps == &jq->ps0))
7091 jmove(joff(jp)->ps);
7092 if (joff(jp)->prev_job)
7093 jmove(joff(jp)->prev_job);
7094 }
7095 if (curjob)
7096 jmove(curjob);
7097#undef joff
7098#undef jmove
7099 }
7100
7101 njobs += 4;
7102 jobtab = jp;
7103 jp = (struct job *)((char *)jp + len);
7104 jq = jp + 3;
7105 do {
7106 jq->used = 0;
7107 } while (--jq >= jp);
7108 return jp;
7109}
7110
7111
7112/*
7113 * Fork off a subshell. If we are doing job control, give the subshell its
7114 * own process group. Jp is a job structure that the job is to be added to.
7115 * N is the command that will be evaluated by the child. Both jp and n may
7116 * be NULL. The mode parameter can be one of the following:
7117 * FORK_FG - Fork off a foreground process.
7118 * FORK_BG - Fork off a background process.
7119 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
7120 * process group even if job control is on.
7121 *
7122 * When job control is turned off, background processes have their standard
7123 * input redirected to /dev/null (except for the second and later processes
7124 * in a pipeline).
7125 *
7126 * Called with interrupts off.
7127 */
7128
7129static inline void
7130forkchild(struct job *jp, union node *n, int mode)
7131{
7132 int wasroot;
7133
7134 TRACE(("Child shell %d\n", getpid()));
7135 wasroot = rootshell;
7136 rootshell = 0;
7137
7138 closescript();
7139 clear_traps();
7140#if JOBS
7141 /* do job control only in root shell */
7142 jobctl = 0;
7143 if (mode != FORK_NOJOB && jp->jobctl && wasroot) {
7144 pid_t pgrp;
7145
7146 if (jp->nprocs == 0)
7147 pgrp = getpid();
7148 else
7149 pgrp = jp->ps[0].pid;
7150 /* This can fail because we are doing it in the parent also */
7151 (void)setpgid(0, pgrp);
7152 if (mode == FORK_FG)
7153 xtcsetpgrp(ttyfd, pgrp);
7154 setsignal(SIGTSTP);
7155 setsignal(SIGTTOU);
7156 } else
7157#endif
7158 if (mode == FORK_BG) {
7159 ignoresig(SIGINT);
7160 ignoresig(SIGQUIT);
7161 if (jp->nprocs == 0) {
7162 close(0);
7163 if (open(_PATH_DEVNULL, O_RDONLY) != 0)
7164 error("Can't open %s", _PATH_DEVNULL);
7165 }
7166 }
7167 if (wasroot && iflag) {
7168 setsignal(SIGINT);
7169 setsignal(SIGQUIT);
7170 setsignal(SIGTERM);
7171 }
7172 for (jp = curjob; jp; jp = jp->prev_job)
7173 freejob(jp);
7174 jobless = 0;
7175}
7176
7177static inline void
7178forkparent(struct job *jp, union node *n, int mode, pid_t pid)
7179{
7180 TRACE(("In parent shell: child = %d\n", pid));
7181 if (!jp) {
7182 while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
7183 jobless++;
7184 return;
7185 }
7186#if JOBS
7187 if (mode != FORK_NOJOB && jp->jobctl) {
7188 int pgrp;
7189
7190 if (jp->nprocs == 0)
7191 pgrp = pid;
7192 else
7193 pgrp = jp->ps[0].pid;
7194 /* This can fail because we are doing it in the child also */
7195 (void)setpgid(pid, pgrp);
7196 }
7197#endif
7198 if (mode == FORK_BG) {
7199 backgndpid = pid; /* set $! */
7200 set_curjob(jp, CUR_RUNNING);
7201 }
7202 if (jp) {
7203 struct procstat *ps = &jp->ps[jp->nprocs++];
7204 ps->pid = pid;
7205 ps->status = -1;
7206 ps->cmd = nullstr;
7207#if JOBS
7208 if (jobctl && n)
7209 ps->cmd = commandtext(n);
7210#endif
7211 }
7212}
7213
7214static int
7215forkshell(struct job *jp, union node *n, int mode)
7216{
7217 int pid;
7218
7219 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
7220 pid = fork();
7221 if (pid < 0) {
7222 TRACE(("Fork failed, errno=%d", errno));
7223 if (jp)
7224 freejob(jp);
7225 error("Cannot fork");
7226 }
7227 if (pid == 0)
7228 forkchild(jp, n, mode);
7229 else
7230 forkparent(jp, n, mode, pid);
7231 return pid;
7232}
7233
7234/*
7235 * Wait for job to finish.
7236 *
7237 * Under job control we have the problem that while a child process is
7238 * running interrupts generated by the user are sent to the child but not
7239 * to the shell. This means that an infinite loop started by an inter-
7240 * active user may be hard to kill. With job control turned off, an
7241 * interactive user may place an interactive program inside a loop. If
7242 * the interactive program catches interrupts, the user doesn't want
7243 * these interrupts to also abort the loop. The approach we take here
7244 * is to have the shell ignore interrupt signals while waiting for a
7245 * foreground process to terminate, and then send itself an interrupt
7246 * signal if the child process was terminated by an interrupt signal.
7247 * Unfortunately, some programs want to do a bit of cleanup and then
7248 * exit on interrupt; unless these processes terminate themselves by
7249 * sending a signal to themselves (instead of calling exit) they will
7250 * confuse this approach.
7251 *
7252 * Called with interrupts off.
7253 */
7254
7255int
7256waitforjob(struct job *jp)
7257{
7258 int st;
7259
7260 TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
7261 while (jp->state == JOBRUNNING) {
7262 dowait(DOWAIT_BLOCK, jp);
7263 }
7264 st = getstatus(jp);
7265#if JOBS
7266 if (jp->jobctl) {
7267 xtcsetpgrp(ttyfd, rootpid);
7268 /*
7269 * This is truly gross.
7270 * If we're doing job control, then we did a TIOCSPGRP which
7271 * caused us (the shell) to no longer be in the controlling
7272 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
7273 * intuit from the subprocess exit status whether a SIGINT
7274 * occurred, and if so interrupt ourselves. Yuck. - mycroft
7275 */
7276 if (jp->sigint)
7277 raise(SIGINT);
7278 }
7279 if (jp->state == JOBDONE)
7280#endif
7281 freejob(jp);
7282 return st;
7283}
7284
7285
7286/*
7287 * Do a wait system call. If job control is compiled in, we accept
7288 * stopped processes. If block is zero, we return a value of zero
7289 * rather than blocking.
7290 *
7291 * System V doesn't have a non-blocking wait system call. It does
7292 * have a SIGCLD signal that is sent to a process when one of it's
7293 * children dies. The obvious way to use SIGCLD would be to install
7294 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
7295 * was received, and have waitproc bump another counter when it got
7296 * the status of a process. Waitproc would then know that a wait
7297 * system call would not block if the two counters were different.
7298 * This approach doesn't work because if a process has children that
7299 * have not been waited for, System V will send it a SIGCLD when it
7300 * installs a signal handler for SIGCLD. What this means is that when
7301 * a child exits, the shell will be sent SIGCLD signals continuously
7302 * until is runs out of stack space, unless it does a wait call before
7303 * restoring the signal handler. The code below takes advantage of
7304 * this (mis)feature by installing a signal handler for SIGCLD and
7305 * then checking to see whether it was called. If there are any
7306 * children to be waited for, it will be.
7307 *
7308 * If neither SYSV nor BSD is defined, we don't implement nonblocking
7309 * waits at all. In this case, the user will not be informed when
7310 * a background process until the next time she runs a real program
7311 * (as opposed to running a builtin command or just typing return),
7312 * and the jobs command may give out of date information.
7313 */
7314
7315static inline int
7316waitproc(int block, int *status)
7317{
7318 int flags = 0;
7319
7320#if JOBS
7321 if (jobctl)
7322 flags |= WUNTRACED;
7323#endif
7324 if (block == 0)
7325 flags |= WNOHANG;
7326 return wait3(status, flags, (struct rusage *)NULL);
7327}
7328
7329/*
7330 * Wait for a process to terminate.
7331 */
7332
7333static int
7334dowait(int block, struct job *job)
7335{
7336 int pid;
7337 int status;
7338 struct job *jp;
7339 struct job *thisjob;
7340 int state;
7341
7342 TRACE(("dowait(%d) called\n", block));
7343 pid = waitproc(block, &status);
7344 TRACE(("wait returns pid %d, status=%d\n", pid, status));
7345 if (pid <= 0)
7346 return pid;
7347 INTOFF;
7348 thisjob = NULL;
7349 for (jp = curjob; jp; jp = jp->prev_job) {
7350 struct procstat *sp;
7351 struct procstat *spend;
7352 if (jp->state == JOBDONE)
7353 continue;
7354 state = JOBDONE;
7355 spend = jp->ps + jp->nprocs;
7356 sp = jp->ps;
7357 do {
7358 if (sp->pid == pid) {
7359 TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jobno(jp), pid, sp->status, status));
7360 sp->status = status;
7361 thisjob = jp;
7362 }
7363 if (sp->status == -1)
7364 state = JOBRUNNING;
7365#ifdef JOBS
7366 if (state == JOBRUNNING)
7367 continue;
7368 if (WIFSTOPPED(sp->status)) {
7369 jp->stopstatus = sp->status;
7370 state = JOBSTOPPED;
7371 }
7372#endif
7373 } while (++sp < spend);
7374 if (thisjob)
7375 goto gotjob;
7376 }
7377#ifdef JOBS
7378 if (!WIFSTOPPED(status))
7379#endif
7380
7381 jobless--;
7382 goto out;
7383
7384gotjob:
7385 if (state != JOBRUNNING) {
7386 thisjob->changed = 1;
7387
7388 if (thisjob->state != state) {
7389 TRACE(("Job %d: changing state from %d to %d\n", jobno(thisjob), thisjob->state, state));
7390 thisjob->state = state;
7391#ifdef JOBS
7392 if (state == JOBSTOPPED) {
7393 set_curjob(thisjob, CUR_STOPPED);
7394 }
7395#endif
7396 }
7397 }
7398
7399out:
7400 INTON;
7401
7402 if (thisjob && thisjob == job) {
7403 char s[48 + 1];
7404 int len;
7405
7406 len = sprint_status(s, status, 1);
7407 if (len) {
7408 s[len] = '\n';
7409 s[len + 1] = 0;
7410 out2str(s);
7411 }
7412 }
7413 return pid;
7414}
7415
7416
7417/*
7418 * return 1 if there are stopped jobs, otherwise 0
7419 */
7420
7421int
7422stoppedjobs(void)
7423{
7424 struct job *jp;
7425 int retval;
7426
7427 retval = 0;
7428 if (job_warning)
7429 goto out;
7430 jp = curjob;
7431 if (jp && jp->state == JOBSTOPPED) {
7432 out2str("You have stopped jobs.\n");
7433 job_warning = 2;
7434 retval++;
7435 }
7436
7437out:
7438 return retval;
7439}
7440
7441/*
7442 * Return a string identifying a command (to be printed by the
7443 * jobs command).
7444 */
7445
7446#if JOBS
7447static char *cmdnextc;
7448
7449static char *
7450commandtext(union node *n)
7451{
7452 char *name;
7453
7454 STARTSTACKSTR(cmdnextc);
7455 cmdtxt(n);
7456 name = stackblock();
7457 TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
7458 name, cmdnextc, cmdnextc));
7459 return savestr(name);
7460}
7461
7462static void
7463cmdtxt(union node *n)
7464{
7465 union node *np;
7466 struct nodelist *lp;
7467 const char *p;
7468 char s[2];
7469
7470 if (!n)
7471 return;
7472 switch (n->type) {
7473 default:
7474#if DEBUG
7475 abort();
7476#endif
7477 case NPIPE:
7478 lp = n->npipe.cmdlist;
7479 for (;;) {
7480 cmdtxt(lp->n);
7481 lp = lp->next;
7482 if (!lp)
7483 break;
7484 cmdputs(" | ");
7485 }
7486 break;
7487 case NSEMI:
7488 p = "; ";
7489 goto binop;
7490 case NAND:
7491 p = " && ";
7492 goto binop;
7493 case NOR:
7494 p = " || ";
7495binop:
7496 cmdtxt(n->nbinary.ch1);
7497 cmdputs(p);
7498 n = n->nbinary.ch2;
7499 goto donode;
7500 case NREDIR:
7501 case NBACKGND:
7502 n = n->nredir.n;
7503 goto donode;
7504 case NNOT:
7505 cmdputs("!");
7506 n = n->nnot.com;
7507donode:
7508 cmdtxt(n);
7509 break;
7510 case NIF:
7511 cmdputs("if ");
7512 cmdtxt(n->nif.test);
7513 cmdputs("; then ");
7514 n = n->nif.ifpart;
7515 if (n->nif.elsepart) {
7516 cmdtxt(n);
7517 cmdputs("; else ");
7518 n = n->nif.elsepart;
7519 }
7520 p = "; fi";
7521 goto dotail;
7522 case NSUBSHELL:
7523 cmdputs("(");
7524 n = n->nredir.n;
7525 p = ")";
7526 goto dotail;
7527 case NWHILE:
7528 p = "while ";
7529 goto until;
7530 case NUNTIL:
7531 p = "until ";
7532until:
7533 cmdputs(p);
7534 cmdtxt(n->nbinary.ch1);
7535 n = n->nbinary.ch2;
7536 p = "; done";
7537dodo:
7538 cmdputs("; do ");
7539dotail:
7540 cmdtxt(n);
7541 goto dotail2;
7542 case NFOR:
7543 cmdputs("for ");
7544 cmdputs(n->nfor.var);
7545 cmdputs(" in ");
7546 cmdlist(n->nfor.args, 1);
7547 n = n->nfor.body;
7548 p = "; done";
7549 goto dodo;
7550 case NDEFUN:
7551 cmdputs(n->narg.text);
7552 p = "() { ... }";
7553 goto dotail2;
7554 case NCMD:
7555 cmdlist(n->ncmd.args, 1);
7556 cmdlist(n->ncmd.redirect, 0);
7557 break;
7558 case NARG:
7559 p = n->narg.text;
7560dotail2:
7561 cmdputs(p);
7562 break;
7563 case NHERE:
7564 case NXHERE:
7565 p = "<<...";
7566 goto dotail2;
7567 case NCASE:
7568 cmdputs("case ");
7569 cmdputs(n->ncase.expr->narg.text);
7570 cmdputs(" in ");
7571 for (np = n->ncase.cases; np; np = np->nclist.next) {
7572 cmdtxt(np->nclist.pattern);
7573 cmdputs(") ");
7574 cmdtxt(np->nclist.body);
7575 cmdputs(";; ");
7576 }
7577 p = "esac";
7578 goto dotail2;
7579 case NTO:
7580 p = ">";
7581 goto redir;
7582 case NCLOBBER:
7583 p = ">|";
7584 goto redir;
7585 case NAPPEND:
7586 p = ">>";
7587 goto redir;
7588 case NTOFD:
7589 p = ">&";
7590 goto redir;
7591 case NFROM:
7592 p = "<";
7593 goto redir;
7594 case NFROMFD:
7595 p = "<&";
7596 goto redir;
7597 case NFROMTO:
7598 p = "<>";
7599redir:
7600 s[0] = n->nfile.fd + '0';
7601 s[1] = '\0';
7602 cmdputs(s);
7603 cmdputs(p);
7604 if (n->type == NTOFD || n->type == NFROMFD) {
7605 s[0] = n->ndup.dupfd + '0';
7606 p = s;
7607 goto dotail2;
7608 } else {
7609 n = n->nfile.fname;
7610 goto donode;
7611 }
7612 }
7613}
7614
7615static void
7616cmdlist(union node *np, int sep)
7617{
7618 for (; np; np = np->narg.next) {
7619 if (!sep)
7620 cmdputs(spcstr);
7621 cmdtxt(np);
7622 if (sep && np->narg.next)
7623 cmdputs(spcstr);
7624 }
7625}
7626
7627static void
7628cmdputs(const char *s)
7629{
7630 const char *p, *str;
7631 char c, cc[2] = " ";
7632 char *nextc;
7633 int subtype = 0;
7634 int quoted = 0;
7635 static const char *const vstype[16] = {
7636 nullstr, "}", "-", "+", "?", "=",
7637 "%", "%%", "#", "##", nullstr
7638 };
7639
7640 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
7641 p = s;
7642 while ((c = *p++) != 0) {
7643 str = 0;
7644 switch (c) {
7645 case CTLESC:
7646 c = *p++;
7647 break;
7648 case CTLVAR:
7649 subtype = *p++;
7650 if ((subtype & VSTYPE) == VSLENGTH)
7651 str = "${#";
7652 else
7653 str = "${";
7654 if (!(subtype & VSQUOTE) != !(quoted & 1)) {
7655 quoted ^= 1;
7656 c = '"';
7657 } else
7658 goto dostr;
7659 break;
7660 case CTLENDVAR:
7661 quoted >>= 1;
7662 subtype = 0;
7663 if (quoted & 1) {
7664 str = "\"}";
7665 goto dostr;
7666 }
7667 c = '}';
7668 break;
7669 case CTLBACKQ:
7670 str = "$(...)";
7671 goto dostr;
7672 case CTLBACKQ+CTLQUOTE:
7673 str = "\"$(...)\"";
7674 goto dostr;
7675#ifdef CONFIG_ASH_MATH_SUPPORT
7676 case CTLARI:
7677 str = "$((";
7678 goto dostr;
7679 case CTLENDARI:
7680 str = "))";
7681 goto dostr;
7682#endif
7683 case CTLQUOTEMARK:
7684 quoted ^= 1;
7685 c = '"';
7686 break;
7687 case '=':
7688 if (subtype == 0)
7689 break;
7690 str = vstype[subtype & VSTYPE];
7691 if (subtype & VSNUL)
7692 c = ':';
7693 else
7694 c = *str++;
7695 if (c != '}')
7696 quoted <<= 1;
7697 break;
7698 case '\'':
7699 case '\\':
7700 case '"':
7701 case '$':
7702 /* These can only happen inside quotes */
7703 cc[0] = c;
7704 str = cc;
7705 c = '\\';
7706 break;
7707 default:
7708 break;
7709 }
7710 USTPUTC(c, nextc);
7711 if (!str)
7712 continue;
7713dostr:
7714 while ((c = *str++)) {
7715 USTPUTC(c, nextc);
7716 }
7717 }
7718 if (quoted & 1) {
7719 USTPUTC('"', nextc);
7720 }
7721 *nextc = 0;
7722 cmdnextc = nextc;
7723}
7724
7725
7726static void
7727showpipe(struct job *jp, FILE *out)
7728{
7729 struct procstat *sp;
7730 struct procstat *spend;
7731
7732 spend = jp->ps + jp->nprocs;
7733 for (sp = jp->ps + 1; sp < spend; sp++)
7734 fprintf(out, " | %s", sp->cmd);
7735 outcslow('\n', out);
7736 flushall();
7737}
7738
7739static void
7740xtcsetpgrp(int fd, pid_t pgrp)
7741{
7742 if (tcsetpgrp(fd, pgrp))
7743 error("Cannot set tty process group (%m)");
7744}
7745#endif /* JOBS */
7746
7747static int
7748getstatus(struct job *job) {
7749 int status;
7750 int retval;
7751
7752 status = job->ps[job->nprocs - 1].status;
7753 retval = WEXITSTATUS(status);
7754 if (!WIFEXITED(status)) {
7755#if JOBS
7756 retval = WSTOPSIG(status);
7757 if (!WIFSTOPPED(status))
7758#endif
7759 {
7760 /* XXX: limits number of signals */
7761 retval = WTERMSIG(status);
7762#if JOBS
7763 if (retval == SIGINT)
7764 job->sigint = 1;
7765#endif
7766 }
7767 retval += 128;
7768 }
7769 TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
7770 jobno(job), job->nprocs, status, retval));
7771 return retval;
7772}
7773
7774#ifdef CONFIG_ASH_MAIL
7775/* $NetBSD: mail.c,v 1.15 2002/11/24 22:35:40 christos Exp $ */
7776
7777/*
7778 * Routines to check for mail. (Perhaps make part of main.c?)
7779 */
7780
7781#define MAXMBOXES 10
7782
7783/* times of mailboxes */
7784static time_t mailtime[MAXMBOXES];
7785/* Set if MAIL or MAILPATH is changed. */
7786static int mail_var_path_changed;
7787
7788
7789
7790/*
7791 * Print appropriate message(s) if mail has arrived.
7792 * If mail_var_path_changed is set,
7793 * then the value of MAIL has mail_var_path_changed,
7794 * so we just update the values.
7795 */
7796
7797static void
7798chkmail(void)
7799{
7800 const char *mpath;
7801 char *p;
7802 char *q;
7803 time_t *mtp;
7804 struct stackmark smark;
7805 struct stat statb;
7806
7807 setstackmark(&smark);
7808 mpath = mpathset() ? mpathval() : mailval();
7809 for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
7810 p = padvance(&mpath, nullstr);
7811 if (p == NULL)
7812 break;
7813 if (*p == '\0')
7814 continue;
7815 for (q = p ; *q ; q++);
7816#ifdef DEBUG
7817 if (q[-1] != '/')
7818 abort();
7819#endif
7820 q[-1] = '\0'; /* delete trailing '/' */
7821 if (stat(p, &statb) < 0) {
7822 *mtp = 0;
7823 continue;
7824 }
7825 if (!mail_var_path_changed && statb.st_mtime != *mtp) {
7826 fprintf(
7827 stderr, snlfmt,
7828 pathopt ? pathopt : "you have mail"
7829 );
7830 }
7831 *mtp = statb.st_mtime;
7832 }
7833 mail_var_path_changed = 0;
7834 popstackmark(&smark);
7835}
7836
7837
7838static void
7839changemail(const char *val)
7840{
7841 mail_var_path_changed++;
7842}
7843
7844#endif /* CONFIG_ASH_MAIL */
7845
7846/* $NetBSD: main.c,v 1.46 2002/12/11 19:12:18 christos Exp $ */
7847
7848
7849#if PROFILE
7850static short profile_buf[16384];
7851extern int etext();
7852#endif
7853
7854static int isloginsh;
7855
7856static void read_profile(const char *);
7857
7858/*
7859 * Main routine. We initialize things, parse the arguments, execute
7860 * profiles if we're a login shell, and then call cmdloop to execute
7861 * commands. The setjmp call sets up the location to jump to when an
7862 * exception occurs. When an exception occurs the variable "state"
7863 * is used to figure out how far we had gotten.
7864 */
7865
7866int
7867ash_main(int argc, char **argv)
7868{
7869 char *shinit;
7870 volatile int state;
7871 struct jmploc jmploc;
7872 struct stackmark smark;
7873
7874#ifdef __GLIBC__
7875 dash_errno = __errno_location();
7876#endif
7877
7878#if PROFILE
7879 monitor(4, etext, profile_buf, sizeof profile_buf, 50);
7880#endif
7881 state = 0;
7882 if (setjmp(jmploc.loc)) {
7883 int status;
7884 int e;
7885
7886 reset();
7887
7888 e = exception;
7889 switch (exception) {
7890 case EXEXEC:
7891 status = exerrno;
7892 break;
7893
7894 case EXERROR:
7895 status = 2;
7896 break;
7897
7898 default:
7899 status = exitstatus;
7900 break;
7901 }
7902 exitstatus = status;
7903
7904 if (e == EXEXIT || state == 0 || iflag == 0 || ! rootshell)
7905 exitshell();
7906
7907 if (e == EXINT) {
7908 outcslow('\n', stderr);
7909 }
7910 popstackmark(&smark);
7911 FORCEINTON; /* enable interrupts */
7912 if (state == 1)
7913 goto state1;
7914 else if (state == 2)
7915 goto state2;
7916 else if (state == 3)
7917 goto state3;
7918 else
7919 goto state4;
7920 }
7921 handler = &jmploc;
7922#ifdef DEBUG
7923 opentrace();
7924 trputs("Shell args: "); trargs(argv);
7925#endif
7926 rootpid = getpid();
7927
7928#ifdef CONFIG_ASH_RANDOM_SUPPORT
7929 rseed = rootpid + ((time_t)time((time_t *)0));
7930#endif
7931 rootshell = 1;
7932 init();
7933 setstackmark(&smark);
7934 procargs(argc, argv);
7935#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
7936 if ( iflag ) {
7937 const char *hp = lookupvar("HISTFILE");
7938
7939 if(hp == NULL ) {
7940 hp = lookupvar("HOME");
7941 if(hp != NULL) {
7942 char *defhp = concat_path_file(hp, ".ash_history");
7943 setvar("HISTFILE", defhp, 0);
7944 free(defhp);
7945 }
7946 }
7947 }
7948#endif
7949 if (argv[0] && argv[0][0] == '-')
7950 isloginsh = 1;
7951 if (isloginsh) {
7952 state = 1;
7953 read_profile("/etc/profile");
7954state1:
7955 state = 2;
7956 read_profile(".profile");
7957 }
7958state2:
7959 state = 3;
7960 if (
7961#ifndef linux
7962 getuid() == geteuid() && getgid() == getegid() &&
7963#endif
7964 iflag
7965 ) {
7966 if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
7967 read_profile(shinit);
7968 }
7969 }
7970state3:
7971 state = 4;
7972 if (minusc)
7973 evalstring(minusc);
7974
7975 if (sflag || minusc == NULL) {
7976#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
7977 if ( iflag ) {
7978 const char *hp = lookupvar("HISTFILE");
7979
7980 if(hp != NULL )
7981 load_history ( hp );
7982 }
7983#endif
7984state4: /* XXX ??? - why isn't this before the "if" statement */
7985 cmdloop(1);
7986 }
7987#if PROFILE
7988 monitor(0);
7989#endif
7990#if GPROF
7991 {
7992 extern void _mcleanup(void);
7993 _mcleanup();
7994 }
7995#endif
7996 exitshell();
7997 /* NOTREACHED */
7998}
7999
8000
8001/*
8002 * Read and execute commands. "Top" is nonzero for the top level command
8003 * loop; it turns on prompting if the shell is interactive.
8004 */
8005
8006static void
8007cmdloop(int top)
8008{
8009 union node *n;
8010 struct stackmark smark;
8011 int inter;
8012 int numeof = 0;
8013
8014 TRACE(("cmdloop(%d) called\n", top));
8015 for (;;) {
8016 setstackmark(&smark);
8017 if (pendingsigs)
8018 dotrap();
8019#if JOBS
8020 if (jobctl)
8021 showjobs(stderr, SHOW_CHANGED);
8022#endif
8023 inter = 0;
8024 if (iflag && top) {
8025 inter++;
8026#ifdef CONFIG_ASH_MAIL
8027 chkmail();
8028#endif
8029 }
8030 n = parsecmd(inter);
8031 /* showtree(n); DEBUG */
8032 if (n == NEOF) {
8033 if (!top || numeof >= 50)
8034 break;
8035 if (!stoppedjobs()) {
8036 if (!Iflag)
8037 break;
8038 out2str("\nUse \"exit\" to leave shell.\n");
8039 }
8040 numeof++;
8041 } else if (n != NULL && nflag == 0) {
8042 job_warning = (job_warning == 2) ? 1 : 0;
8043 numeof = 0;
8044 evaltree(n, 0);
8045 }
8046 popstackmark(&smark);
8047 if (evalskip) {
8048 evalskip = 0;
8049 break;
8050 }
8051 }
8052}
8053
8054
8055/*
8056 * Read /etc/profile or .profile. Return on error.
8057 */
8058
8059static void
8060read_profile(const char *name)
8061{
8062 int fd;
8063 int xflag_set = 0;
8064 int vflag_set = 0;
8065
8066 INTOFF;
8067 if ((fd = open(name, O_RDONLY)) >= 0)
8068 setinputfd(fd, 1);
8069 INTON;
8070 if (fd < 0)
8071 return;
8072 /* -q turns off -x and -v just when executing init files */
8073 if (qflag) {
8074 if (xflag)
8075 xflag = 0, xflag_set = 1;
8076 if (vflag)
8077 vflag = 0, vflag_set = 1;
8078 }
8079 cmdloop(0);
8080 if (qflag) {
8081 if (xflag_set)
8082 xflag = 1;
8083 if (vflag_set)
8084 vflag = 1;
8085 }
8086 popfile();
8087}
8088
8089
8090/*
8091 * Read a file containing shell functions.
8092 */
8093
8094static void
8095readcmdfile(char *name)
8096{
8097 int fd;
8098
8099 INTOFF;
8100 if ((fd = open(name, O_RDONLY)) >= 0)
8101 setinputfd(fd, 1);
8102 else
8103 error("Can't open %s", name);
8104 INTON;
8105 cmdloop(0);
8106 popfile();
8107}
8108
8109
8110/*
8111 * Take commands from a file. To be compatible we should do a path
8112 * search for the file, which is necessary to find sub-commands.
8113 */
8114
8115static inline char *
8116find_dot_file(char *name)
8117{
8118 char *fullname;
8119 const char *path = pathval();
8120 struct stat statb;
8121
8122 /* don't try this for absolute or relative paths */
8123 if (strchr(name, '/'))
8124 return name;
8125
8126 while ((fullname = padvance(&path, name)) != NULL) {
8127 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
8128 /*
8129 * Don't bother freeing here, since it will
8130 * be freed by the caller.
8131 */
8132 return fullname;
8133 }
8134 stunalloc(fullname);
8135 }
8136
8137 /* not found in the PATH */
8138 error(not_found_msg, name);
8139 /* NOTREACHED */
8140}
8141
8142static int dotcmd(int argc, char **argv)
8143{
8144 struct strlist *sp;
8145 volatile struct shparam saveparam;
8146
8147 exitstatus = 0;
8148
8149 for (sp = cmdenviron; sp; sp = sp->next)
8150 setvareq(bb_xstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
8151
8152 if (argc >= 2) { /* That's what SVR2 does */
8153 char *fullname;
8154 struct stackmark smark;
8155
8156 setstackmark(&smark);
8157 fullname = find_dot_file(argv[1]);
8158
8159 if (argc > 2) {
8160 saveparam = shellparam;
8161 shellparam.malloc = 0;
8162 shellparam.nparam = argc - 2;
8163 shellparam.p = argv + 2;
8164 };
8165
8166 setinputfile(fullname, 1);
8167 commandname = fullname;
8168 cmdloop(0);
8169 popfile();
8170
8171 if (argc > 2) {
8172 freeparam(&shellparam);
8173 shellparam = saveparam;
8174 };
8175
8176 popstackmark(&smark);
8177 }
8178 return exitstatus;
8179}
8180
8181
8182static int
8183exitcmd(int argc, char **argv)
8184{
8185 if (stoppedjobs())
8186 return 0;
8187 if (argc > 1)
8188 exitstatus = number(argv[1]);
8189 exraise(EXEXIT);
8190 /* NOTREACHED */
8191}
8192
8193/* $NetBSD: memalloc.c,v 1.27 2003/01/22 20:36:04 dsl Exp $ */
8194
8195/*
8196 * Same for malloc, realloc, but returns an error when out of space.
8197 */
8198
8199static pointer
8200ckrealloc(pointer p, size_t nbytes)
8201{
8202 p = realloc(p, nbytes);
8203 if (p == NULL)
8204 error(bb_msg_memory_exhausted);
8205 return p;
8206}
8207
8208static pointer
8209ckmalloc(size_t nbytes)
8210{
8211 return ckrealloc(NULL, nbytes);
8212}
8213
8214/*
8215 * Make a copy of a string in safe storage.
8216 */
8217
8218static char *
8219savestr(const char *s)
8220{
8221 char *p = strdup(s);
8222 if (!p)
8223 error(bb_msg_memory_exhausted);
8224 return p;
8225}
8226
8227
8228/*
8229 * Parse trees for commands are allocated in lifo order, so we use a stack
8230 * to make this more efficient, and also to avoid all sorts of exception
8231 * handling code to handle interrupts in the middle of a parse.
8232 *
8233 * The size 504 was chosen because the Ultrix malloc handles that size
8234 * well.
8235 */
8236
8237
8238static pointer
8239stalloc(size_t nbytes)
8240{
8241 char *p;
8242 size_t aligned;
8243
8244 aligned = SHELL_ALIGN(nbytes);
8245 if (aligned > stacknleft) {
8246 size_t len;
8247 size_t blocksize;
8248 struct stack_block *sp;
8249
8250 blocksize = aligned;
8251 if (blocksize < MINSIZE)
8252 blocksize = MINSIZE;
8253 len = sizeof(struct stack_block) - MINSIZE + blocksize;
8254 if (len < blocksize)
8255 error(bb_msg_memory_exhausted);
8256 INTOFF;
8257 sp = ckmalloc(len);
8258 sp->prev = stackp;
8259 stacknxt = sp->space;
8260 stacknleft = blocksize;
8261 sstrend = stacknxt + blocksize;
8262 stackp = sp;
8263 INTON;
8264 }
8265 p = stacknxt;
8266 stacknxt += aligned;
8267 stacknleft -= aligned;
8268 return p;
8269}
8270
8271
8272void
8273stunalloc(pointer p)
8274{
8275#ifdef DEBUG
8276 if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
8277 write(2, "stunalloc\n", 10);
8278 abort();
8279 }
8280#endif
8281 stacknleft += stacknxt - (char *)p;
8282 stacknxt = p;
8283}
8284
8285
8286void
8287setstackmark(struct stackmark *mark)
8288{
8289 mark->stackp = stackp;
8290 mark->stacknxt = stacknxt;
8291 mark->stacknleft = stacknleft;
8292 mark->marknext = markp;
8293 markp = mark;
8294}
8295
8296
8297void
8298popstackmark(struct stackmark *mark)
8299{
8300 struct stack_block *sp;
8301
8302 INTOFF;
8303 markp = mark->marknext;
8304 while (stackp != mark->stackp) {
8305 sp = stackp;
8306 stackp = sp->prev;
8307 ckfree(sp);
8308 }
8309 stacknxt = mark->stacknxt;
8310 stacknleft = mark->stacknleft;
8311 sstrend = mark->stacknxt + mark->stacknleft;
8312 INTON;
8313}
8314
8315
8316/*
8317 * When the parser reads in a string, it wants to stick the string on the
8318 * stack and only adjust the stack pointer when it knows how big the
8319 * string is. Stackblock (defined in stack.h) returns a pointer to a block
8320 * of space on top of the stack and stackblocklen returns the length of
8321 * this block. Growstackblock will grow this space by at least one byte,
8322 * possibly moving it (like realloc). Grabstackblock actually allocates the
8323 * part of the block that has been used.
8324 */
8325
8326void
8327growstackblock(void)
8328{
8329 size_t newlen;
8330
8331 newlen = stacknleft * 2;
8332 if (newlen < stacknleft)
8333 error(bb_msg_memory_exhausted);
8334 if (newlen < 128)
8335 newlen += 128;
8336
8337 if (stacknxt == stackp->space && stackp != &stackbase) {
8338 struct stack_block *oldstackp;
8339 struct stackmark *xmark;
8340 struct stack_block *sp;
8341 struct stack_block *prevstackp;
8342 size_t grosslen;
8343
8344 INTOFF;
8345 oldstackp = stackp;
8346 sp = stackp;
8347 prevstackp = sp->prev;
8348 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
8349 sp = ckrealloc((pointer)sp, grosslen);
8350 sp->prev = prevstackp;
8351 stackp = sp;
8352 stacknxt = sp->space;
8353 stacknleft = newlen;
8354 sstrend = sp->space + newlen;
8355
8356 /*
8357 * Stack marks pointing to the start of the old block
8358 * must be relocated to point to the new block
8359 */
8360 xmark = markp;
8361 while (xmark != NULL && xmark->stackp == oldstackp) {
8362 xmark->stackp = stackp;
8363 xmark->stacknxt = stacknxt;
8364 xmark->stacknleft = stacknleft;
8365 xmark = xmark->marknext;
8366 }
8367 INTON;
8368 } else {
8369 char *oldspace = stacknxt;
8370 int oldlen = stacknleft;
8371 char *p = stalloc(newlen);
8372
8373 /* free the space we just allocated */
8374 stacknxt = memcpy(p, oldspace, oldlen);
8375 stacknleft += newlen;
8376 }
8377}
8378
8379static inline void
8380grabstackblock(size_t len)
8381{
8382 len = SHELL_ALIGN(len);
8383 stacknxt += len;
8384 stacknleft -= len;
8385}
8386
8387/*
8388 * The following routines are somewhat easier to use than the above.
8389 * The user declares a variable of type STACKSTR, which may be declared
8390 * to be a register. The macro STARTSTACKSTR initializes things. Then
8391 * the user uses the macro STPUTC to add characters to the string. In
8392 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
8393 * grown as necessary. When the user is done, she can just leave the
8394 * string there and refer to it using stackblock(). Or she can allocate
8395 * the space for it using grabstackstr(). If it is necessary to allow
8396 * someone else to use the stack temporarily and then continue to grow
8397 * the string, the user should use grabstack to allocate the space, and
8398 * then call ungrabstr(p) to return to the previous mode of operation.
8399 *
8400 * USTPUTC is like STPUTC except that it doesn't check for overflow.
8401 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
8402 * is space for at least one character.
8403 */
8404
8405void *
8406growstackstr(void)
8407{
8408 size_t len = stackblocksize();
8409 if (herefd >= 0 && len >= 1024) {
8410 bb_full_write(herefd, stackblock(), len);
8411 return stackblock();
8412 }
8413 growstackblock();
8414 return stackblock() + len;
8415}
8416
8417/*
8418 * Called from CHECKSTRSPACE.
8419 */
8420
8421char *
8422makestrspace(size_t newlen, char *p)
8423{
8424 size_t len = p - stacknxt;
8425 size_t size = stackblocksize();
8426
8427 for (;;) {
8428 size_t nleft;
8429
8430 size = stackblocksize();
8431 nleft = size - len;
8432 if (nleft >= newlen)
8433 break;
8434 growstackblock();
8435 }
8436 return stackblock() + len;
8437}
8438
8439char *
8440stnputs(const char *s, size_t n, char *p)
8441{
8442 p = makestrspace(n, p);
8443 p = mempcpy(p, s, n);
8444 return p;
8445}
8446
8447char *
8448stputs(const char *s, char *p)
8449{
8450 return stnputs(s, strlen(s), p);
8451}
8452
8453/* $NetBSD: mystring.c,v 1.15 2002/11/24 22:35:42 christos Exp $ */
8454
8455/*
8456 * String functions.
8457 *
8458 * number(s) Convert a string of digits to an integer.
8459 * is_number(s) Return true if s is a string of digits.
8460 */
8461
8462/*
8463 * prefix -- see if pfx is a prefix of string.
8464 */
8465
8466char *
8467prefix(const char *string, const char *pfx)
8468{
8469 while (*pfx) {
8470 if (*pfx++ != *string++)
8471 return 0;
8472 }
8473 return (char *) string;
8474}
8475
8476
8477/*
8478 * Convert a string of digits to an integer, printing an error message on
8479 * failure.
8480 */
8481
8482int
8483number(const char *s)
8484{
8485
8486 if (! is_number(s))
8487 error(illnum, s);
8488 return atoi(s);
8489}
8490
8491
8492/*
8493 * Check for a valid number. This should be elsewhere.
8494 */
8495
8496int
8497is_number(const char *p)
8498{
8499 do {
8500 if (! is_digit(*p))
8501 return 0;
8502 } while (*++p != '\0');
8503 return 1;
8504}
8505
8506
8507/*
8508 * Produce a possibly single quoted string suitable as input to the shell.
8509 * The return string is allocated on the stack.
8510 */
8511
8512char *
8513single_quote(const char *s) {
8514 char *p;
8515
8516 STARTSTACKSTR(p);
8517
8518 do {
8519 char *q;
8520 size_t len;
8521
8522 len = strchrnul(s, '\'') - s;
8523
8524 q = p = makestrspace(len + 3, p);
8525
8526 *q++ = '\'';
8527 q = mempcpy(q, s, len);
8528 *q++ = '\'';
8529 s += len;
8530
8531 STADJUST(q - p, p);
8532
8533 len = strspn(s, "'");
8534 if (!len)
8535 break;
8536
8537 q = p = makestrspace(len + 3, p);
8538
8539 *q++ = '"';
8540 q = mempcpy(q, s, len);
8541 *q++ = '"';
8542 s += len;
8543
8544 STADJUST(q - p, p);
8545 } while (*s);
8546
8547 USTPUTC(0, p);
8548
8549 return stackblock();
8550}
8551
8552/*
8553 * Like strdup but works with the ash stack.
8554 */
8555
8556char *
8557sstrdup(const char *p)
8558{
8559 size_t len = strlen(p) + 1;
8560 return memcpy(stalloc(len), p, len);
8561}
8562
8563
8564static void
8565calcsize(union node *n)
8566{
8567 if (n == NULL)
8568 return;
8569 funcblocksize += nodesize[n->type];
8570 switch (n->type) {
8571 case NCMD:
8572 calcsize(n->ncmd.redirect);
8573 calcsize(n->ncmd.args);
8574 calcsize(n->ncmd.assign);
8575 break;
8576 case NPIPE:
8577 sizenodelist(n->npipe.cmdlist);
8578 break;
8579 case NREDIR:
8580 case NBACKGND:
8581 case NSUBSHELL:
8582 calcsize(n->nredir.redirect);
8583 calcsize(n->nredir.n);
8584 break;
8585 case NAND:
8586 case NOR:
8587 case NSEMI:
8588 case NWHILE:
8589 case NUNTIL:
8590 calcsize(n->nbinary.ch2);
8591 calcsize(n->nbinary.ch1);
8592 break;
8593 case NIF:
8594 calcsize(n->nif.elsepart);
8595 calcsize(n->nif.ifpart);
8596 calcsize(n->nif.test);
8597 break;
8598 case NFOR:
8599 funcstringsize += strlen(n->nfor.var) + 1;
8600 calcsize(n->nfor.body);
8601 calcsize(n->nfor.args);
8602 break;
8603 case NCASE:
8604 calcsize(n->ncase.cases);
8605 calcsize(n->ncase.expr);
8606 break;
8607 case NCLIST:
8608 calcsize(n->nclist.body);
8609 calcsize(n->nclist.pattern);
8610 calcsize(n->nclist.next);
8611 break;
8612 case NDEFUN:
8613 case NARG:
8614 sizenodelist(n->narg.backquote);
8615 funcstringsize += strlen(n->narg.text) + 1;
8616 calcsize(n->narg.next);
8617 break;
8618 case NTO:
8619 case NCLOBBER:
8620 case NFROM:
8621 case NFROMTO:
8622 case NAPPEND:
8623 calcsize(n->nfile.fname);
8624 calcsize(n->nfile.next);
8625 break;
8626 case NTOFD:
8627 case NFROMFD:
8628 calcsize(n->ndup.vname);
8629 calcsize(n->ndup.next);
8630 break;
8631 case NHERE:
8632 case NXHERE:
8633 calcsize(n->nhere.doc);
8634 calcsize(n->nhere.next);
8635 break;
8636 case NNOT:
8637 calcsize(n->nnot.com);
8638 break;
8639 };
8640}
8641
8642
8643static void
8644sizenodelist(struct nodelist *lp)
8645{
8646 while (lp) {
8647 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
8648 calcsize(lp->n);
8649 lp = lp->next;
8650 }
8651}
8652
8653
8654static union node *
8655copynode(union node *n)
8656{
8657 union node *new;
8658
8659 if (n == NULL)
8660 return NULL;
8661 new = funcblock;
8662 funcblock = (char *) funcblock + nodesize[n->type];
8663 switch (n->type) {
8664 case NCMD:
8665 new->ncmd.redirect = copynode(n->ncmd.redirect);
8666 new->ncmd.args = copynode(n->ncmd.args);
8667 new->ncmd.assign = copynode(n->ncmd.assign);
8668 break;
8669 case NPIPE:
8670 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
8671 new->npipe.backgnd = n->npipe.backgnd;
8672 break;
8673 case NREDIR:
8674 case NBACKGND:
8675 case NSUBSHELL:
8676 new->nredir.redirect = copynode(n->nredir.redirect);
8677 new->nredir.n = copynode(n->nredir.n);
8678 break;
8679 case NAND:
8680 case NOR:
8681 case NSEMI:
8682 case NWHILE:
8683 case NUNTIL:
8684 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8685 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8686 break;
8687 case NIF:
8688 new->nif.elsepart = copynode(n->nif.elsepart);
8689 new->nif.ifpart = copynode(n->nif.ifpart);
8690 new->nif.test = copynode(n->nif.test);
8691 break;
8692 case NFOR:
8693 new->nfor.var = nodesavestr(n->nfor.var);
8694 new->nfor.body = copynode(n->nfor.body);
8695 new->nfor.args = copynode(n->nfor.args);
8696 break;
8697 case NCASE:
8698 new->ncase.cases = copynode(n->ncase.cases);
8699 new->ncase.expr = copynode(n->ncase.expr);
8700 break;
8701 case NCLIST:
8702 new->nclist.body = copynode(n->nclist.body);
8703 new->nclist.pattern = copynode(n->nclist.pattern);
8704 new->nclist.next = copynode(n->nclist.next);
8705 break;
8706 case NDEFUN:
8707 case NARG:
8708 new->narg.backquote = copynodelist(n->narg.backquote);
8709 new->narg.text = nodesavestr(n->narg.text);
8710 new->narg.next = copynode(n->narg.next);
8711 break;
8712 case NTO:
8713 case NCLOBBER:
8714 case NFROM:
8715 case NFROMTO:
8716 case NAPPEND:
8717 new->nfile.fname = copynode(n->nfile.fname);
8718 new->nfile.fd = n->nfile.fd;
8719 new->nfile.next = copynode(n->nfile.next);
8720 break;
8721 case NTOFD:
8722 case NFROMFD:
8723 new->ndup.vname = copynode(n->ndup.vname);
8724 new->ndup.dupfd = n->ndup.dupfd;
8725 new->ndup.fd = n->ndup.fd;
8726 new->ndup.next = copynode(n->ndup.next);
8727 break;
8728 case NHERE:
8729 case NXHERE:
8730 new->nhere.doc = copynode(n->nhere.doc);
8731 new->nhere.fd = n->nhere.fd;
8732 new->nhere.next = copynode(n->nhere.next);
8733 break;
8734 case NNOT:
8735 new->nnot.com = copynode(n->nnot.com);
8736 break;
8737 };
8738 new->type = n->type;
8739 return new;
8740}
8741
8742
8743static struct nodelist *
8744copynodelist(struct nodelist *lp)
8745{
8746 struct nodelist *start;
8747 struct nodelist **lpp;
8748
8749 lpp = &start;
8750 while (lp) {
8751 *lpp = funcblock;
8752 funcblock = (char *) funcblock +
8753 SHELL_ALIGN(sizeof(struct nodelist));
8754 (*lpp)->n = copynode(lp->n);
8755 lp = lp->next;
8756 lpp = &(*lpp)->next;
8757 }
8758 *lpp = NULL;
8759 return start;
8760}
8761
8762
8763static char *
8764nodesavestr(char *s)
8765{
8766 char *rtn = funcstring;
8767
8768 funcstring = stpcpy(funcstring, s) + 1;
8769 return rtn;
8770}
8771
8772
8773/*
8774 * Free a parse tree.
8775 */
8776
8777static void
8778freefunc(struct funcnode *f)
8779{
8780 if (f && --f->count < 0)
8781 ckfree(f);
8782}
8783
8784
8785static void options(int);
8786static void setoption(int, int);
8787
8788
8789/*
8790 * Process the shell command line arguments.
8791 */
8792
8793void
8794procargs(int argc, char **argv)
8795{
8796 int i;
8797 const char *xminusc;
8798 char **xargv;
8799
8800 xargv = argv;
8801 arg0 = xargv[0];
8802 if (argc > 0)
8803 xargv++;
8804 for (i = 0; i < NOPTS; i++)
8805 optlist[i] = 2;
8806 argptr = xargv;
8807 options(1);
8808 xargv = argptr;
8809 xminusc = minusc;
8810 if (*xargv == NULL) {
8811 if (xminusc)
8812 error("-c requires an argument");
8813 sflag = 1;
8814 }
8815 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
8816 iflag = 1;
8817 if (mflag == 2)
8818 mflag = iflag;
8819 for (i = 0; i < NOPTS; i++)
8820 if (optlist[i] == 2)
8821 optlist[i] = 0;
8822#if DEBUG == 2
8823 debug = 1;
8824#endif
8825 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
8826 if (xminusc) {
8827 minusc = *xargv++;
8828 if (*xargv)
8829 goto setarg0;
8830 } else if (!sflag) {
8831 setinputfile(*xargv, 0);
8832setarg0:
8833 arg0 = *xargv++;
8834 commandname = arg0;
8835 }
8836
8837 shellparam.p = xargv;
8838#ifdef CONFIG_ASH_GETOPTS
8839 shellparam.optind = 1;
8840 shellparam.optoff = -1;
8841#endif
8842 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
8843 while (*xargv) {
8844 shellparam.nparam++;
8845 xargv++;
8846 }
8847 optschanged();
8848}
8849
8850
8851void
8852optschanged(void)
8853{
8854#ifdef DEBUG
8855 opentrace();
8856#endif
8857 setinteractive(iflag);
8858 setjobctl(mflag);
8859}
8860
8861static inline void
8862minus_o(char *name, int val)
8863{
8864 int i;
8865
8866 if (name == NULL) {
8867 out1str("Current option settings\n");
8868 for (i = 0; i < NOPTS; i++)
8869 out1fmt("%-16s%s\n", optnames(i),
8870 optlist[i] ? "on" : "off");
8871 } else {
8872 for (i = 0; i < NOPTS; i++)
8873 if (equal(name, optnames(i))) {
8874 optlist[i] = val;
8875 return;
8876 }
8877 error("Illegal option -o %s", name);
8878 }
8879}
8880
8881/*
8882 * Process shell options. The global variable argptr contains a pointer
8883 * to the argument list; we advance it past the options.
8884 */
8885
8886static void
8887options(int cmdline)
8888{
8889 char *p;
8890 int val;
8891 int c;
8892
8893 if (cmdline)
8894 minusc = NULL;
8895 while ((p = *argptr) != NULL) {
8896 argptr++;
8897 if ((c = *p++) == '-') {
8898 val = 1;
8899 if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
8900 if (!cmdline) {
8901 /* "-" means turn off -x and -v */
8902 if (p[0] == '\0')
8903 xflag = vflag = 0;
8904 /* "--" means reset params */
8905 else if (*argptr == NULL)
8906 setparam(argptr);
8907 }
8908 break; /* "-" or "--" terminates options */
8909 }
8910 } else if (c == '+') {
8911 val = 0;
8912 } else {
8913 argptr--;
8914 break;
8915 }
8916 while ((c = *p++) != '\0') {
8917 if (c == 'c' && cmdline) {
8918 minusc = p; /* command is after shell args*/
8919 } else if (c == 'o') {
8920 minus_o(*argptr, val);
8921 if (*argptr)
8922 argptr++;
8923 } else if (cmdline && (c == '-')) { // long options
8924 if (strcmp(p, "login") == 0)
8925 isloginsh = 1;
8926 break;
8927 } else {
8928 setoption(c, val);
8929 }
8930 }
8931 }
8932}
8933
8934
8935static void
8936setoption(int flag, int val)
8937{
8938 int i;
8939
8940 for (i = 0; i < NOPTS; i++)
8941 if (optletters(i) == flag) {
8942 optlist[i] = val;
8943 return;
8944 }
8945 error("Illegal option -%c", flag);
8946 /* NOTREACHED */
8947}
8948
8949
8950
8951/*
8952 * Set the shell parameters.
8953 */
8954
8955void
8956setparam(char **argv)
8957{
8958 char **newparam;
8959 char **ap;
8960 int nparam;
8961
8962 for (nparam = 0 ; argv[nparam] ; nparam++);
8963 ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
8964 while (*argv) {
8965 *ap++ = savestr(*argv++);
8966 }
8967 *ap = NULL;
8968 freeparam(&shellparam);
8969 shellparam.malloc = 1;
8970 shellparam.nparam = nparam;
8971 shellparam.p = newparam;
8972#ifdef CONFIG_ASH_GETOPTS
8973 shellparam.optind = 1;
8974 shellparam.optoff = -1;
8975#endif
8976}
8977
8978
8979/*
8980 * Free the list of positional parameters.
8981 */
8982
8983void
8984freeparam(volatile struct shparam *param)
8985{
8986 char **ap;
8987
8988 if (param->malloc) {
8989 for (ap = param->p ; *ap ; ap++)
8990 ckfree(*ap);
8991 ckfree(param->p);
8992 }
8993}
8994
8995
8996
8997/*
8998 * The shift builtin command.
8999 */
9000
9001int
9002shiftcmd(int argc, char **argv)
9003{
9004 int n;
9005 char **ap1, **ap2;
9006
9007 n = 1;
9008 if (argc > 1)
9009 n = number(argv[1]);
9010 if (n > shellparam.nparam)
9011 error("can't shift that many");
9012 INTOFF;
9013 shellparam.nparam -= n;
9014 for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
9015 if (shellparam.malloc)
9016 ckfree(*ap1);
9017 }
9018 ap2 = shellparam.p;
9019 while ((*ap2++ = *ap1++) != NULL);
9020#ifdef CONFIG_ASH_GETOPTS
9021 shellparam.optind = 1;
9022 shellparam.optoff = -1;
9023#endif
9024 INTON;
9025 return 0;
9026}
9027
9028
9029
9030/*
9031 * The set command builtin.
9032 */
9033
9034int
9035setcmd(int argc, char **argv)
9036{
9037 if (argc == 1)
9038 return showvars(nullstr, 0, VUNSET);
9039 INTOFF;
9040 options(0);
9041 optschanged();
9042 if (*argptr != NULL) {
9043 setparam(argptr);
9044 }
9045 INTON;
9046 return 0;
9047}
9048
9049
9050#ifdef CONFIG_ASH_GETOPTS
9051static void
9052getoptsreset(value)
9053 const char *value;
9054{
9055 shellparam.optind = number(value);
9056 shellparam.optoff = -1;
9057}
9058#endif
9059
9060#ifdef CONFIG_LOCALE_SUPPORT
9061static void change_lc_all(const char *value)
9062{
9063 if (value != 0 && *value != 0)
9064 setlocale(LC_ALL, value);
9065}
9066
9067static void change_lc_ctype(const char *value)
9068{
9069 if (value != 0 && *value != 0)
9070 setlocale(LC_CTYPE, value);
9071}
9072
9073#endif
9074
9075#ifdef CONFIG_ASH_RANDOM_SUPPORT
9076/* Roughly copied from bash.. */
9077static void change_random(const char *value)
9078{
9079 if(value == NULL) {
9080 /* "get", generate */
9081 char buf[16];
9082
9083 rseed = rseed * 1103515245 + 12345;
9084 sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
9085 /* set without recursion */
9086 setvar(vrandom.text, buf, VNOFUNC);
9087 vrandom.flags &= ~VNOFUNC;
9088 } else {
9089 /* set/reset */
9090 rseed = strtoul(value, (char **)NULL, 10);
9091 }
9092}
9093#endif
9094
9095
9096#ifdef CONFIG_ASH_GETOPTS
9097static int
9098getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
9099{
9100 char *p, *q;
9101 char c = '?';
9102 int done = 0;
9103 int err = 0;
9104 char s[12];
9105 char **optnext;
9106
9107 if(*param_optind < 1)
9108 return 1;
9109 optnext = optfirst + *param_optind - 1;
9110
9111 if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
9112 p = NULL;
9113 else
9114 p = optnext[-1] + *optoff;
9115 if (p == NULL || *p == '\0') {
9116 /* Current word is done, advance */
9117 p = *optnext;
9118 if (p == NULL || *p != '-' || *++p == '\0') {
9119atend:
9120 p = NULL;
9121 done = 1;
9122 goto out;
9123 }
9124 optnext++;
9125 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
9126 goto atend;
9127 }
9128
9129 c = *p++;
9130 for (q = optstr; *q != c; ) {
9131 if (*q == '\0') {
9132 if (optstr[0] == ':') {
9133 s[0] = c;
9134 s[1] = '\0';
9135 err |= setvarsafe("OPTARG", s, 0);
9136 } else {
9137 fprintf(stderr, "Illegal option -%c\n", c);
9138 (void) unsetvar("OPTARG");
9139 }
9140 c = '?';
9141 goto out;
9142 }
9143 if (*++q == ':')
9144 q++;
9145 }
9146
9147 if (*++q == ':') {
9148 if (*p == '\0' && (p = *optnext) == NULL) {
9149 if (optstr[0] == ':') {
9150 s[0] = c;
9151 s[1] = '\0';
9152 err |= setvarsafe("OPTARG", s, 0);
9153 c = ':';
9154 } else {
9155 fprintf(stderr, "No arg for -%c option\n", c);
9156 (void) unsetvar("OPTARG");
9157 c = '?';
9158 }
9159 goto out;
9160 }
9161
9162 if (p == *optnext)
9163 optnext++;
9164 err |= setvarsafe("OPTARG", p, 0);
9165 p = NULL;
9166 } else
9167 err |= setvarsafe("OPTARG", nullstr, 0);
9168
9169out:
9170 *optoff = p ? p - *(optnext - 1) : -1;
9171 *param_optind = optnext - optfirst + 1;
9172 fmtstr(s, sizeof(s), "%d", *param_optind);
9173 err |= setvarsafe("OPTIND", s, VNOFUNC);
9174 s[0] = c;
9175 s[1] = '\0';
9176 err |= setvarsafe(optvar, s, 0);
9177 if (err) {
9178 *param_optind = 1;
9179 *optoff = -1;
9180 flushall();
9181 exraise(EXERROR);
9182 }
9183 return done;
9184}
9185
9186/*
9187 * The getopts builtin. Shellparam.optnext points to the next argument
9188 * to be processed. Shellparam.optptr points to the next character to
9189 * be processed in the current argument. If shellparam.optnext is NULL,
9190 * then it's the first time getopts has been called.
9191 */
9192
9193int
9194getoptscmd(int argc, char **argv)
9195{
9196 char **optbase;
9197
9198 if (argc < 3)
9199 error("Usage: getopts optstring var [arg]");
9200 else if (argc == 3) {
9201 optbase = shellparam.p;
9202 if (shellparam.optind > shellparam.nparam + 1) {
9203 shellparam.optind = 1;
9204 shellparam.optoff = -1;
9205 }
9206 }
9207 else {
9208 optbase = &argv[3];
9209 if (shellparam.optind > argc - 2) {
9210 shellparam.optind = 1;
9211 shellparam.optoff = -1;
9212 }
9213 }
9214
9215 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
9216 &shellparam.optoff);
9217}
9218#endif /* CONFIG_ASH_GETOPTS */
9219
9220/*
9221 * XXX - should get rid of. have all builtins use getopt(3). the
9222 * library getopt must have the BSD extension static variable "optreset"
9223 * otherwise it can't be used within the shell safely.
9224 *
9225 * Standard option processing (a la getopt) for builtin routines. The
9226 * only argument that is passed to nextopt is the option string; the
9227 * other arguments are unnecessary. It return the character, or '\0' on
9228 * end of input.
9229 */
9230
9231static int
9232nextopt(const char *optstring)
9233{
9234 char *p;
9235 const char *q;
9236 char c;
9237
9238 if ((p = optptr) == NULL || *p == '\0') {
9239 p = *argptr;
9240 if (p == NULL || *p != '-' || *++p == '\0')
9241 return '\0';
9242 argptr++;
9243 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
9244 return '\0';
9245 }
9246 c = *p++;
9247 for (q = optstring ; *q != c ; ) {
9248 if (*q == '\0')
9249 error("Illegal option -%c", c);
9250 if (*++q == ':')
9251 q++;
9252 }
9253 if (*++q == ':') {
9254 if (*p == '\0' && (p = *argptr++) == NULL)
9255 error("No arg for -%c option", c);
9256 optionarg = p;
9257 p = NULL;
9258 }
9259 optptr = p;
9260 return c;
9261}
9262
9263
9264/* $NetBSD: output.c,v 1.27 2002/11/24 22:35:42 christos Exp $ */
9265
9266void
9267outstr(const char *p, FILE *file)
9268{
9269 INTOFF;
9270 fputs(p, file);
9271 INTON;
9272}
9273
9274void
9275flushall(void)
9276{
9277 INTOFF;
9278 fflush(stdout);
9279 fflush(stderr);
9280 INTON;
9281}
9282
9283void
9284flusherr(void)
9285{
9286 INTOFF;
9287 fflush(stderr);
9288 INTON;
9289}
9290
9291static void
9292outcslow(int c, FILE *dest)
9293{
9294 INTOFF;
9295 putc(c, dest);
9296 fflush(dest);
9297 INTON;
9298}
9299
9300
9301static int
9302out1fmt(const char *fmt, ...)
9303{
9304 va_list ap;
9305 int r;
9306
9307 INTOFF;
9308 va_start(ap, fmt);
9309 r = vprintf(fmt, ap);
9310 va_end(ap);
9311 INTON;
9312 return r;
9313}
9314
9315
9316int
9317fmtstr(char *outbuf, size_t length, const char *fmt, ...)
9318{
9319 va_list ap;
9320 int ret;
9321
9322 va_start(ap, fmt);
9323 INTOFF;
9324 ret = vsnprintf(outbuf, length, fmt, ap);
9325 va_end(ap);
9326 INTON;
9327 return ret;
9328}
9329
9330
9331
9332/* $NetBSD: parser.c,v 1.54 2002/11/24 22:35:42 christos Exp $ */
9333
9334
9335/*
9336 * Shell command parser.
9337 */
9338
9339#define EOFMARKLEN 79
9340
9341
9342struct heredoc {
9343 struct heredoc *next; /* next here document in list */
9344 union node *here; /* redirection node */
9345 char *eofmark; /* string indicating end of input */
9346 int striptabs; /* if set, strip leading tabs */
9347};
9348
9349
9350
9351static struct heredoc *heredoclist; /* list of here documents to read */
9352
9353
9354static union node *list(int);
9355static union node *andor(void);
9356static union node *pipeline(void);
9357static union node *command(void);
9358static union node *simplecmd(void);
9359static union node *makename(void);
9360static void parsefname(void);
9361static void parseheredoc(void);
9362static char peektoken(void);
9363static int readtoken(void);
9364static int xxreadtoken(void);
9365static int readtoken1(int firstc, int syntax, char *eofmark, int striptabs);
9366static int noexpand(char *);
9367static void synexpect(int) __attribute__((__noreturn__));
9368static void synerror(const char *) __attribute__((__noreturn__));
9369static void setprompt(int);
9370
9371
9372
9373static inline int
9374isassignment(const char *p)
9375{
9376 const char *q = endofname(p);
9377 if (p == q)
9378 return 0;
9379 return *q == '=';
9380}
9381
9382
9383/*
9384 * Read and parse a command. Returns NEOF on end of file. (NULL is a
9385 * valid parse tree indicating a blank line.)
9386 */
9387
9388union node *
9389parsecmd(int interact)
9390{
9391 int t;
9392
9393 tokpushback = 0;
9394 doprompt = interact;
9395 if (doprompt)
9396 setprompt(doprompt);
9397 needprompt = 0;
9398 t = readtoken();
9399 if (t == TEOF)
9400 return NEOF;
9401 if (t == TNL)
9402 return NULL;
9403 tokpushback++;
9404 return list(1);
9405}
9406
9407
9408static union node *
9409list(int nlflag)
9410{
9411 union node *n1, *n2, *n3;
9412 int tok;
9413
9414 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9415 if (nlflag == 2 && peektoken())
9416 return NULL;
9417 n1 = NULL;
9418 for (;;) {
9419 n2 = andor();
9420 tok = readtoken();
9421 if (tok == TBACKGND) {
9422 if (n2->type == NPIPE) {
9423 n2->npipe.backgnd = 1;
9424 } else {
9425 if (n2->type != NREDIR) {
9426 n3 = stalloc(sizeof(struct nredir));
9427 n3->nredir.n = n2;
9428 n3->nredir.redirect = NULL;
9429 n2 = n3;
9430 }
9431 n2->type = NBACKGND;
9432 }
9433 }
9434 if (n1 == NULL) {
9435 n1 = n2;
9436 }
9437 else {
9438 n3 = (union node *)stalloc(sizeof (struct nbinary));
9439 n3->type = NSEMI;
9440 n3->nbinary.ch1 = n1;
9441 n3->nbinary.ch2 = n2;
9442 n1 = n3;
9443 }
9444 switch (tok) {
9445 case TBACKGND:
9446 case TSEMI:
9447 tok = readtoken();
9448 /* fall through */
9449 case TNL:
9450 if (tok == TNL) {
9451 parseheredoc();
9452 if (nlflag == 1)
9453 return n1;
9454 } else {
9455 tokpushback++;
9456 }
9457 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9458 if (peektoken())
9459 return n1;
9460 break;
9461 case TEOF:
9462 if (heredoclist)
9463 parseheredoc();
9464 else
9465 pungetc(); /* push back EOF on input */
9466 return n1;
9467 default:
9468 if (nlflag == 1)
9469 synexpect(-1);
9470 tokpushback++;
9471 return n1;
9472 }
9473 }
9474}
9475
9476
9477
9478static union node *
9479andor(void)
9480{
9481 union node *n1, *n2, *n3;
9482 int t;
9483
9484 n1 = pipeline();
9485 for (;;) {
9486 if ((t = readtoken()) == TAND) {
9487 t = NAND;
9488 } else if (t == TOR) {
9489 t = NOR;
9490 } else {
9491 tokpushback++;
9492 return n1;
9493 }
9494 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9495 n2 = pipeline();
9496 n3 = (union node *)stalloc(sizeof (struct nbinary));
9497 n3->type = t;
9498 n3->nbinary.ch1 = n1;
9499 n3->nbinary.ch2 = n2;
9500 n1 = n3;
9501 }
9502}
9503
9504
9505
9506static union node *
9507pipeline(void)
9508{
9509 union node *n1, *n2, *pipenode;
9510 struct nodelist *lp, *prev;
9511 int negate;
9512
9513 negate = 0;
9514 TRACE(("pipeline: entered\n"));
9515 if (readtoken() == TNOT) {
9516 negate = !negate;
9517 checkkwd = CHKKWD | CHKALIAS;
9518 } else
9519 tokpushback++;
9520 n1 = command();
9521 if (readtoken() == TPIPE) {
9522 pipenode = (union node *)stalloc(sizeof (struct npipe));
9523 pipenode->type = NPIPE;
9524 pipenode->npipe.backgnd = 0;
9525 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
9526 pipenode->npipe.cmdlist = lp;
9527 lp->n = n1;
9528 do {
9529 prev = lp;
9530 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
9531 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9532 lp->n = command();
9533 prev->next = lp;
9534 } while (readtoken() == TPIPE);
9535 lp->next = NULL;
9536 n1 = pipenode;
9537 }
9538 tokpushback++;
9539 if (negate) {
9540 n2 = (union node *)stalloc(sizeof (struct nnot));
9541 n2->type = NNOT;
9542 n2->nnot.com = n1;
9543 return n2;
9544 } else
9545 return n1;
9546}
9547
9548
9549
9550static union node *
9551command(void)
9552{
9553 union node *n1, *n2;
9554 union node *ap, **app;
9555 union node *cp, **cpp;
9556 union node *redir, **rpp;
9557 union node **rpp2;
9558 int t;
9559
9560 redir = NULL;
9561 rpp2 = &redir;
9562
9563 switch (readtoken()) {
9564 default:
9565 synexpect(-1);
9566 /* NOTREACHED */
9567 case TIF:
9568 n1 = (union node *)stalloc(sizeof (struct nif));
9569 n1->type = NIF;
9570 n1->nif.test = list(0);
9571 if (readtoken() != TTHEN)
9572 synexpect(TTHEN);
9573 n1->nif.ifpart = list(0);
9574 n2 = n1;
9575 while (readtoken() == TELIF) {
9576 n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
9577 n2 = n2->nif.elsepart;
9578 n2->type = NIF;
9579 n2->nif.test = list(0);
9580 if (readtoken() != TTHEN)
9581 synexpect(TTHEN);
9582 n2->nif.ifpart = list(0);
9583 }
9584 if (lasttoken == TELSE)
9585 n2->nif.elsepart = list(0);
9586 else {
9587 n2->nif.elsepart = NULL;
9588 tokpushback++;
9589 }
9590 t = TFI;
9591 break;
9592 case TWHILE:
9593 case TUNTIL: {
9594 int got;
9595 n1 = (union node *)stalloc(sizeof (struct nbinary));
9596 n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
9597 n1->nbinary.ch1 = list(0);
9598 if ((got=readtoken()) != TDO) {
9599TRACE(("expecting DO got %s %s\n", tokname(got), got == TWORD ? wordtext : ""));
9600 synexpect(TDO);
9601 }
9602 n1->nbinary.ch2 = list(0);
9603 t = TDONE;
9604 break;
9605 }
9606 case TFOR:
9607 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
9608 synerror("Bad for loop variable");
9609 n1 = (union node *)stalloc(sizeof (struct nfor));
9610 n1->type = NFOR;
9611 n1->nfor.var = wordtext;
9612 checkkwd = CHKKWD | CHKALIAS;
9613 if (readtoken() == TIN) {
9614 app = &ap;
9615 while (readtoken() == TWORD) {
9616 n2 = (union node *)stalloc(sizeof (struct narg));
9617 n2->type = NARG;
9618 n2->narg.text = wordtext;
9619 n2->narg.backquote = backquotelist;
9620 *app = n2;
9621 app = &n2->narg.next;
9622 }
9623 *app = NULL;
9624 n1->nfor.args = ap;
9625 if (lasttoken != TNL && lasttoken != TSEMI)
9626 synexpect(-1);
9627 } else {
9628 n2 = (union node *)stalloc(sizeof (struct narg));
9629 n2->type = NARG;
9630 n2->narg.text = (char *)dolatstr;
9631 n2->narg.backquote = NULL;
9632 n2->narg.next = NULL;
9633 n1->nfor.args = n2;
9634 /*
9635 * Newline or semicolon here is optional (but note
9636 * that the original Bourne shell only allowed NL).
9637 */
9638 if (lasttoken != TNL && lasttoken != TSEMI)
9639 tokpushback++;
9640 }
9641 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9642 if (readtoken() != TDO)
9643 synexpect(TDO);
9644 n1->nfor.body = list(0);
9645 t = TDONE;
9646 break;
9647 case TCASE:
9648 n1 = (union node *)stalloc(sizeof (struct ncase));
9649 n1->type = NCASE;
9650 if (readtoken() != TWORD)
9651 synexpect(TWORD);
9652 n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
9653 n2->type = NARG;
9654 n2->narg.text = wordtext;
9655 n2->narg.backquote = backquotelist;
9656 n2->narg.next = NULL;
9657 do {
9658 checkkwd = CHKKWD | CHKALIAS;
9659 } while (readtoken() == TNL);
9660 if (lasttoken != TIN)
9661 synexpect(TIN);
9662 cpp = &n1->ncase.cases;
9663next_case:
9664 checkkwd = CHKNL | CHKKWD;
9665 t = readtoken();
9666 while(t != TESAC) {
9667 if (lasttoken == TLP)
9668 readtoken();
9669 *cpp = cp = (union node *)stalloc(sizeof (struct nclist));
9670 cp->type = NCLIST;
9671 app = &cp->nclist.pattern;
9672 for (;;) {
9673 *app = ap = (union node *)stalloc(sizeof (struct narg));
9674 ap->type = NARG;
9675 ap->narg.text = wordtext;
9676 ap->narg.backquote = backquotelist;
9677 if (readtoken() != TPIPE)
9678 break;
9679 app = &ap->narg.next;
9680 readtoken();
9681 }
9682 ap->narg.next = NULL;
9683 if (lasttoken != TRP)
9684 synexpect(TRP);
9685 cp->nclist.body = list(2);
9686
9687 cpp = &cp->nclist.next;
9688
9689 checkkwd = CHKNL | CHKKWD;
9690 if ((t = readtoken()) != TESAC) {
9691 if (t != TENDCASE)
9692 synexpect(TENDCASE);
9693 else
9694 goto next_case;
9695 }
9696 }
9697 *cpp = NULL;
9698 goto redir;
9699 case TLP:
9700 n1 = (union node *)stalloc(sizeof (struct nredir));
9701 n1->type = NSUBSHELL;
9702 n1->nredir.n = list(0);
9703 n1->nredir.redirect = NULL;
9704 t = TRP;
9705 break;
9706 case TBEGIN:
9707 n1 = list(0);
9708 t = TEND;
9709 break;
9710 case TWORD:
9711 case TREDIR:
9712 tokpushback++;
9713 return simplecmd();
9714 }
9715
9716 if (readtoken() != t)
9717 synexpect(t);
9718
9719redir:
9720 /* Now check for redirection which may follow command */
9721 checkkwd = CHKKWD | CHKALIAS;
9722 rpp = rpp2;
9723 while (readtoken() == TREDIR) {
9724 *rpp = n2 = redirnode;
9725 rpp = &n2->nfile.next;
9726 parsefname();
9727 }
9728 tokpushback++;
9729 *rpp = NULL;
9730 if (redir) {
9731 if (n1->type != NSUBSHELL) {
9732 n2 = (union node *)stalloc(sizeof (struct nredir));
9733 n2->type = NREDIR;
9734 n2->nredir.n = n1;
9735 n1 = n2;
9736 }
9737 n1->nredir.redirect = redir;
9738 }
9739
9740 return n1;
9741}
9742
9743
9744static union node *
9745simplecmd(void) {
9746 union node *args, **app;
9747 union node *n = NULL;
9748 union node *vars, **vpp;
9749 union node **rpp, *redir;
9750 int savecheckkwd;
9751
9752 args = NULL;
9753 app = &args;
9754 vars = NULL;
9755 vpp = &vars;
9756 redir = NULL;
9757 rpp = &redir;
9758
9759 savecheckkwd = CHKALIAS;
9760 for (;;) {
9761 checkkwd = savecheckkwd;
9762 switch (readtoken()) {
9763 case TWORD:
9764 n = (union node *)stalloc(sizeof (struct narg));
9765 n->type = NARG;
9766 n->narg.text = wordtext;
9767 n->narg.backquote = backquotelist;
9768 if (savecheckkwd && isassignment(wordtext)) {
9769 *vpp = n;
9770 vpp = &n->narg.next;
9771 } else {
9772 *app = n;
9773 app = &n->narg.next;
9774 savecheckkwd = 0;
9775 }
9776 break;
9777 case TREDIR:
9778 *rpp = n = redirnode;
9779 rpp = &n->nfile.next;
9780 parsefname(); /* read name of redirection file */
9781 break;
9782 case TLP:
9783 if (
9784 args && app == &args->narg.next &&
9785 !vars && !redir
9786 ) {
9787 struct builtincmd *bcmd;
9788 const char *name;
9789
9790 /* We have a function */
9791 if (readtoken() != TRP)
9792 synexpect(TRP);
9793 name = n->narg.text;
9794 if (
9795 !goodname(name) || (
9796 (bcmd = find_builtin(name)) &&
9797 IS_BUILTIN_SPECIAL(bcmd)
9798 )
9799 )
9800 synerror("Bad function name");
9801 n->type = NDEFUN;
9802 checkkwd = CHKNL | CHKKWD | CHKALIAS;
9803 n->narg.next = command();
9804 return n;
9805 }
9806 /* fall through */
9807 default:
9808 tokpushback++;
9809 goto out;
9810 }
9811 }
9812out:
9813 *app = NULL;
9814 *vpp = NULL;
9815 *rpp = NULL;
9816 n = (union node *)stalloc(sizeof (struct ncmd));
9817 n->type = NCMD;
9818 n->ncmd.args = args;
9819 n->ncmd.assign = vars;
9820 n->ncmd.redirect = redir;
9821 return n;
9822}
9823
9824static union node *
9825makename(void)
9826{
9827 union node *n;
9828
9829 n = (union node *)stalloc(sizeof (struct narg));
9830 n->type = NARG;
9831 n->narg.next = NULL;
9832 n->narg.text = wordtext;
9833 n->narg.backquote = backquotelist;
9834 return n;
9835}
9836
9837void fixredir(union node *n, const char *text, int err)
9838{
9839 TRACE(("Fix redir %s %d\n", text, err));
9840 if (!err)
9841 n->ndup.vname = NULL;
9842
9843 if (is_digit(text[0]) && text[1] == '\0')
9844 n->ndup.dupfd = digit_val(text[0]);
9845 else if (text[0] == '-' && text[1] == '\0')
9846 n->ndup.dupfd = -1;
9847 else {
9848
9849 if (err)
9850 synerror("Bad fd number");
9851 else
9852 n->ndup.vname = makename();
9853 }
9854}
9855
9856
9857static void
9858parsefname(void)
9859{
9860 union node *n = redirnode;
9861
9862 if (readtoken() != TWORD)
9863 synexpect(-1);
9864 if (n->type == NHERE) {
9865 struct heredoc *here = heredoc;
9866 struct heredoc *p;
9867 int i;
9868
9869 if (quoteflag == 0)
9870 n->type = NXHERE;
9871 TRACE(("Here document %d\n", n->type));
9872 if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9873 synerror("Illegal eof marker for << redirection");
9874 rmescapes(wordtext);
9875 here->eofmark = wordtext;
9876 here->next = NULL;
9877 if (heredoclist == NULL)
9878 heredoclist = here;
9879 else {
9880 for (p = heredoclist ; p->next ; p = p->next);
9881 p->next = here;
9882 }
9883 } else if (n->type == NTOFD || n->type == NFROMFD) {
9884 fixredir(n, wordtext, 0);
9885 } else {
9886 n->nfile.fname = makename();
9887 }
9888}
9889
9890
9891/*
9892 * Input any here documents.
9893 */
9894
9895static void
9896parseheredoc(void)
9897{
9898 struct heredoc *here;
9899 union node *n;
9900
9901 here = heredoclist;
9902 heredoclist = 0;
9903
9904 while (here) {
9905 if (needprompt) {
9906 setprompt(2);
9907 needprompt = 0;
9908 }
9909 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
9910 here->eofmark, here->striptabs);
9911 n = (union node *)stalloc(sizeof (struct narg));
9912 n->narg.type = NARG;
9913 n->narg.next = NULL;
9914 n->narg.text = wordtext;
9915 n->narg.backquote = backquotelist;
9916 here->here->nhere.doc = n;
9917 here = here->next;
9918 }
9919}
9920
9921static char peektoken(void)
9922{
9923 int t;
9924
9925 t = readtoken();
9926 tokpushback++;
9927 return tokname_array[t][0];
9928}
9929
9930static int
9931readtoken(void)
9932{
9933 int t;
9934#ifdef DEBUG
9935 int alreadyseen = tokpushback;
9936#endif
9937
9938#ifdef CONFIG_ASH_ALIAS
9939top:
9940#endif
9941
9942 t = xxreadtoken();
9943
9944 /*
9945 * eat newlines
9946 */
9947 if (checkkwd & CHKNL) {
9948 while (t == TNL) {
9949 parseheredoc();
9950 t = xxreadtoken();
9951 }
9952 }
9953
9954 if (t != TWORD || quoteflag) {
9955 goto out;
9956 }
9957
9958 /*
9959 * check for keywords
9960 */
9961 if (checkkwd & CHKKWD) {
9962 const char *const *pp;
9963
9964 if ((pp = findkwd(wordtext))) {
9965 lasttoken = t = pp - tokname_array;
9966 TRACE(("keyword %s recognized\n", tokname(t)));
9967 goto out;
9968 }
9969 }
9970
9971 if (checkkwd & CHKALIAS) {
9972#ifdef CONFIG_ASH_ALIAS
9973 struct alias *ap;
9974 if ((ap = lookupalias(wordtext, 1)) != NULL) {
9975 if (*ap->val) {
9976 pushstring(ap->val, ap);
9977 }
9978 goto top;
9979 }
9980#endif
9981 }
9982out:
9983 checkkwd = 0;
9984#ifdef DEBUG
9985 if (!alreadyseen)
9986 TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
9987 else
9988 TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
9989#endif
9990 return (t);
9991}
9992
9993
9994/*
9995 * Read the next input token.
9996 * If the token is a word, we set backquotelist to the list of cmds in
9997 * backquotes. We set quoteflag to true if any part of the word was
9998 * quoted.
9999 * If the token is TREDIR, then we set redirnode to a structure containing
10000 * the redirection.
10001 * In all cases, the variable startlinno is set to the number of the line
10002 * on which the token starts.
10003 *
10004 * [Change comment: here documents and internal procedures]
10005 * [Readtoken shouldn't have any arguments. Perhaps we should make the
10006 * word parsing code into a separate routine. In this case, readtoken
10007 * doesn't need to have any internal procedures, but parseword does.
10008 * We could also make parseoperator in essence the main routine, and
10009 * have parseword (readtoken1?) handle both words and redirection.]
10010 */
10011
10012#define NEW_xxreadtoken
10013#ifdef NEW_xxreadtoken
10014
10015/* singles must be first! */
10016static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
10017
10018static const char xxreadtoken_tokens[] = {
10019 TNL, TLP, TRP, /* only single occurrence allowed */
10020 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
10021 TEOF, /* corresponds to trailing nul */
10022 TAND, TOR, TENDCASE, /* if double occurrence */
10023};
10024
10025#define xxreadtoken_doubles \
10026 (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
10027#define xxreadtoken_singles \
10028 (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
10029
10030static int xxreadtoken()
10031{
10032 int c;
10033
10034 if (tokpushback) {
10035 tokpushback = 0;
10036 return lasttoken;
10037 }
10038 if (needprompt) {
10039 setprompt(2);
10040 needprompt = 0;
10041 }
10042 startlinno = plinno;
10043 for (;;) { /* until token or start of word found */
10044 c = pgetc_macro();
10045
10046 if ((c != ' ') && (c != '\t')
10047#ifdef CONFIG_ASH_ALIAS
10048 && (c != PEOA)
10049#endif
10050 ) {
10051 if (c == '#') {
10052 while ((c = pgetc()) != '\n' && c != PEOF);
10053 pungetc();
10054 } else if (c == '\\') {
10055 if (pgetc() != '\n') {
10056 pungetc();
10057 goto READTOKEN1;
10058 }
10059 startlinno = ++plinno;
10060 if (doprompt)
10061 setprompt(2);
10062 } else {
10063 const char *p
10064 = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
10065
10066 if (c != PEOF) {
10067 if (c == '\n') {
10068 plinno++;
10069 needprompt = doprompt;
10070 }
10071
10072 p = strchr(xxreadtoken_chars, c);
10073 if (p == NULL) {
10074 READTOKEN1:
10075 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
10076 }
10077
10078 if (p - xxreadtoken_chars >= xxreadtoken_singles) {
10079 if (pgetc() == *p) { /* double occurrence? */
10080 p += xxreadtoken_doubles + 1;
10081 } else {
10082 pungetc();
10083 }
10084 }
10085 }
10086
10087 return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
10088 }
10089 }
10090 }
10091}
10092
10093
10094#else
10095#define RETURN(token) return lasttoken = token
10096
10097static int
10098xxreadtoken(void)
10099{
10100 int c;
10101
10102 if (tokpushback) {
10103 tokpushback = 0;
10104 return lasttoken;
10105 }
10106 if (needprompt) {
10107 setprompt(2);
10108 needprompt = 0;
10109 }
10110 startlinno = plinno;
10111 for (;;) { /* until token or start of word found */
10112 c = pgetc_macro();
10113 switch (c) {
10114 case ' ': case '\t':
10115#ifdef CONFIG_ASH_ALIAS
10116 case PEOA:
10117#endif
10118 continue;
10119 case '#':
10120 while ((c = pgetc()) != '\n' && c != PEOF);
10121 pungetc();
10122 continue;
10123 case '\\':
10124 if (pgetc() == '\n') {
10125 startlinno = ++plinno;
10126 if (doprompt)
10127 setprompt(2);
10128 continue;
10129 }
10130 pungetc();
10131 goto breakloop;
10132 case '\n':
10133 plinno++;
10134 needprompt = doprompt;
10135 RETURN(TNL);
10136 case PEOF:
10137 RETURN(TEOF);
10138 case '&':
10139 if (pgetc() == '&')
10140 RETURN(TAND);
10141 pungetc();
10142 RETURN(TBACKGND);
10143 case '|':
10144 if (pgetc() == '|')
10145 RETURN(TOR);
10146 pungetc();
10147 RETURN(TPIPE);
10148 case ';':
10149 if (pgetc() == ';')
10150 RETURN(TENDCASE);
10151 pungetc();
10152 RETURN(TSEMI);
10153 case '(':
10154 RETURN(TLP);
10155 case ')':
10156 RETURN(TRP);
10157 default:
10158 goto breakloop;
10159 }
10160 }
10161breakloop:
10162 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10163#undef RETURN
10164}
10165#endif /* NEW_xxreadtoken */
10166
10167
10168/*
10169 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10170 * is not NULL, read a here document. In the latter case, eofmark is the
10171 * word which marks the end of the document and striptabs is true if
10172 * leading tabs should be stripped from the document. The argument firstc
10173 * is the first character of the input token or document.
10174 *
10175 * Because C does not have internal subroutines, I have simulated them
10176 * using goto's to implement the subroutine linkage. The following macros
10177 * will run code that appears at the end of readtoken1.
10178 */
10179
10180#define CHECKEND() {goto checkend; checkend_return:;}
10181#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10182#define PARSESUB() {goto parsesub; parsesub_return:;}
10183#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10184#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10185#define PARSEARITH() {goto parsearith; parsearith_return:;}
10186
10187static int
10188readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
10189{
10190 int c = firstc;
10191 char *out;
10192 int len;
10193 char line[EOFMARKLEN + 1];
10194 struct nodelist *bqlist;
10195 int quotef;
10196 int dblquote;
10197 int varnest; /* levels of variables expansion */
10198 int arinest; /* levels of arithmetic expansion */
10199 int parenlevel; /* levels of parens in arithmetic */
10200 int dqvarnest; /* levels of variables expansion within double quotes */
10201 int oldstyle;
10202 int prevsyntax; /* syntax before arithmetic */
10203#if __GNUC__
10204 /* Avoid longjmp clobbering */
10205 (void) &out;
10206 (void) &quotef;
10207 (void) &dblquote;
10208 (void) &varnest;
10209 (void) &arinest;
10210 (void) &parenlevel;
10211 (void) &dqvarnest;
10212 (void) &oldstyle;
10213 (void) &prevsyntax;
10214 (void) &syntax;
10215#endif
10216
10217 startlinno = plinno;
10218 dblquote = 0;
10219 if (syntax == DQSYNTAX)
10220 dblquote = 1;
10221 quotef = 0;
10222 bqlist = NULL;
10223 varnest = 0;
10224 arinest = 0;
10225 parenlevel = 0;
10226 dqvarnest = 0;
10227
10228 STARTSTACKSTR(out);
10229 loop: { /* for each line, until end of word */
10230 CHECKEND(); /* set c to PEOF if at end of here document */
10231 for (;;) { /* until end of line or end of word */
10232 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
10233 switch(SIT(c, syntax)) {
10234 case CNL: /* '\n' */
10235 if (syntax == BASESYNTAX)
10236 goto endword; /* exit outer loop */
10237 USTPUTC(c, out);
10238 plinno++;
10239 if (doprompt)
10240 setprompt(2);
10241 c = pgetc();
10242 goto loop; /* continue outer loop */
10243 case CWORD:
10244 USTPUTC(c, out);
10245 break;
10246 case CCTL:
10247 if (eofmark == NULL || dblquote)
10248 USTPUTC(CTLESC, out);
10249 USTPUTC(c, out);
10250 break;
10251 case CBACK: /* backslash */
10252 c = pgetc2();
10253 if (c == PEOF) {
10254 USTPUTC(CTLESC, out);
10255 USTPUTC('\\', out);
10256 pungetc();
10257 } else if (c == '\n') {
10258 if (doprompt)
10259 setprompt(2);
10260 } else {
10261 if (
10262 dblquote &&
10263 c != '\\' && c != '`' &&
10264 c != '$' && (
10265 c != '"' ||
10266 eofmark != NULL
10267 )
10268 ) {
10269 USTPUTC(CTLESC, out);
10270 USTPUTC('\\', out);
10271 }
10272 if (SIT(c, SQSYNTAX) == CCTL)
10273 USTPUTC(CTLESC, out);
10274 USTPUTC(c, out);
10275 quotef++;
10276 }
10277 break;
10278 case CSQUOTE:
10279 syntax = SQSYNTAX;
10280quotemark:
10281 if (eofmark == NULL) {
10282 USTPUTC(CTLQUOTEMARK, out);
10283 }
10284 break;
10285 case CDQUOTE:
10286 syntax = DQSYNTAX;
10287 dblquote = 1;
10288 goto quotemark;
10289 case CENDQUOTE:
10290 if (eofmark != NULL && arinest == 0 &&
10291 varnest == 0) {
10292 USTPUTC(c, out);
10293 } else {
10294 if (dqvarnest == 0) {
10295 syntax = BASESYNTAX;
10296 dblquote = 0;
10297 }
10298 quotef++;
10299 goto quotemark;
10300 }
10301 break;
10302 case CVAR: /* '$' */
10303 PARSESUB(); /* parse substitution */
10304 break;
10305 case CENDVAR: /* '}' */
10306 if (varnest > 0) {
10307 varnest--;
10308 if (dqvarnest > 0) {
10309 dqvarnest--;
10310 }
10311 USTPUTC(CTLENDVAR, out);
10312 } else {
10313 USTPUTC(c, out);
10314 }
10315 break;
10316#ifdef CONFIG_ASH_MATH_SUPPORT
10317 case CLP: /* '(' in arithmetic */
10318 parenlevel++;
10319 USTPUTC(c, out);
10320 break;
10321 case CRP: /* ')' in arithmetic */
10322 if (parenlevel > 0) {
10323 USTPUTC(c, out);
10324 --parenlevel;
10325 } else {
10326 if (pgetc() == ')') {
10327 if (--arinest == 0) {
10328 USTPUTC(CTLENDARI, out);
10329 syntax = prevsyntax;
10330 if (syntax == DQSYNTAX)
10331 dblquote = 1;
10332 else
10333 dblquote = 0;
10334 } else
10335 USTPUTC(')', out);
10336 } else {
10337 /*
10338 * unbalanced parens
10339 * (don't 2nd guess - no error)
10340 */
10341 pungetc();
10342 USTPUTC(')', out);
10343 }
10344 }
10345 break;
10346#endif
10347 case CBQUOTE: /* '`' */
10348 PARSEBACKQOLD();
10349 break;
10350 case CENDFILE:
10351 goto endword; /* exit outer loop */
10352 case CIGN:
10353 break;
10354 default:
10355 if (varnest == 0)
10356 goto endword; /* exit outer loop */
10357#ifdef CONFIG_ASH_ALIAS
10358 if (c != PEOA)
10359#endif
10360 USTPUTC(c, out);
10361
10362 }
10363 c = pgetc_macro();
10364 }
10365 }
10366endword:
10367#ifdef CONFIG_ASH_MATH_SUPPORT
10368 if (syntax == ARISYNTAX)
10369 synerror("Missing '))'");
10370#endif
10371 if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
10372 synerror("Unterminated quoted string");
10373 if (varnest != 0) {
10374 startlinno = plinno;
10375 /* { */
10376 synerror("Missing '}'");
10377 }
10378 USTPUTC('\0', out);
10379 len = out - (char *)stackblock();
10380 out = stackblock();
10381 if (eofmark == NULL) {
10382 if ((c == '>' || c == '<')
10383 && quotef == 0
10384 && len <= 2
10385 && (*out == '\0' || is_digit(*out))) {
10386 PARSEREDIR();
10387 return lasttoken = TREDIR;
10388 } else {
10389 pungetc();
10390 }
10391 }
10392 quoteflag = quotef;
10393 backquotelist = bqlist;
10394 grabstackblock(len);
10395 wordtext = out;
10396 return lasttoken = TWORD;
10397/* end of readtoken routine */
10398
10399
10400
10401/*
10402 * Check to see whether we are at the end of the here document. When this
10403 * is called, c is set to the first character of the next input line. If
10404 * we are at the end of the here document, this routine sets the c to PEOF.
10405 */
10406
10407checkend: {
10408 if (eofmark) {
10409#ifdef CONFIG_ASH_ALIAS
10410 if (c == PEOA) {
10411 c = pgetc2();
10412 }
10413#endif
10414 if (striptabs) {
10415 while (c == '\t') {
10416 c = pgetc2();
10417 }
10418 }
10419 if (c == *eofmark) {
10420 if (pfgets(line, sizeof line) != NULL) {
10421 char *p, *q;
10422
10423 p = line;
10424 for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
10425 if (*p == '\n' && *q == '\0') {
10426 c = PEOF;
10427 plinno++;
10428 needprompt = doprompt;
10429 } else {
10430 pushstring(line, NULL);
10431 }
10432 }
10433 }
10434 }
10435 goto checkend_return;
10436}
10437
10438
10439/*
10440 * Parse a redirection operator. The variable "out" points to a string
10441 * specifying the fd to be redirected. The variable "c" contains the
10442 * first character of the redirection operator.
10443 */
10444
10445parseredir: {
10446 char fd = *out;
10447 union node *np;
10448
10449 np = (union node *)stalloc(sizeof (struct nfile));
10450 if (c == '>') {
10451 np->nfile.fd = 1;
10452 c = pgetc();
10453 if (c == '>')
10454 np->type = NAPPEND;
10455 else if (c == '|')
10456 np->type = NCLOBBER;
10457 else if (c == '&')
10458 np->type = NTOFD;
10459 else {
10460 np->type = NTO;
10461 pungetc();
10462 }
10463 } else { /* c == '<' */
10464 np->nfile.fd = 0;
10465 switch (c = pgetc()) {
10466 case '<':
10467 if (sizeof (struct nfile) != sizeof (struct nhere)) {
10468 np = (union node *)stalloc(sizeof (struct nhere));
10469 np->nfile.fd = 0;
10470 }
10471 np->type = NHERE;
10472 heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
10473 heredoc->here = np;
10474 if ((c = pgetc()) == '-') {
10475 heredoc->striptabs = 1;
10476 } else {
10477 heredoc->striptabs = 0;
10478 pungetc();
10479 }
10480 break;
10481
10482 case '&':
10483 np->type = NFROMFD;
10484 break;
10485
10486 case '>':
10487 np->type = NFROMTO;
10488 break;
10489
10490 default:
10491 np->type = NFROM;
10492 pungetc();
10493 break;
10494 }
10495 }
10496 if (fd != '\0')
10497 np->nfile.fd = digit_val(fd);
10498 redirnode = np;
10499 goto parseredir_return;
10500}
10501
10502
10503/*
10504 * Parse a substitution. At this point, we have read the dollar sign
10505 * and nothing else.
10506 */
10507
10508parsesub: {
10509 int subtype;
10510 int typeloc;
10511 int flags;
10512 char *p;
10513 static const char types[] = "}-+?=";
10514
10515 c = pgetc();
10516 if (
10517 c <= PEOA_OR_PEOF ||
10518 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10519 ) {
10520 USTPUTC('$', out);
10521 pungetc();
10522 } else if (c == '(') { /* $(command) or $((arith)) */
10523 if (pgetc() == '(') {
10524#ifdef CONFIG_ASH_MATH_SUPPORT
10525 PARSEARITH();
10526#else
10527 synerror("We unsupport $((arith))");
10528#endif
10529 } else {
10530 pungetc();
10531 PARSEBACKQNEW();
10532 }
10533 } else {
10534 USTPUTC(CTLVAR, out);
10535 typeloc = out - (char *)stackblock();
10536 USTPUTC(VSNORMAL, out);
10537 subtype = VSNORMAL;
10538 if (c == '{') {
10539 c = pgetc();
10540 if (c == '#') {
10541 if ((c = pgetc()) == '}')
10542 c = '#';
10543 else
10544 subtype = VSLENGTH;
10545 }
10546 else
10547 subtype = 0;
10548 }
10549 if (c > PEOA_OR_PEOF && is_name(c)) {
10550 do {
10551 STPUTC(c, out);
10552 c = pgetc();
10553 } while (c > PEOA_OR_PEOF && is_in_name(c));
10554 } else if (is_digit(c)) {
10555 do {
10556 STPUTC(c, out);
10557 c = pgetc();
10558 } while (is_digit(c));
10559 }
10560 else if (is_special(c)) {
10561 USTPUTC(c, out);
10562 c = pgetc();
10563 }
10564 else
10565badsub: synerror("Bad substitution");
10566
10567 STPUTC('=', out);
10568 flags = 0;
10569 if (subtype == 0) {
10570 switch (c) {
10571 case ':':
10572 flags = VSNUL;
10573 c = pgetc();
10574 /*FALLTHROUGH*/
10575 default:
10576 p = strchr(types, c);
10577 if (p == NULL)
10578 goto badsub;
10579 subtype = p - types + VSNORMAL;
10580 break;
10581 case '%':
10582 case '#':
10583 {
10584 int cc = c;
10585 subtype = c == '#' ? VSTRIMLEFT :
10586 VSTRIMRIGHT;
10587 c = pgetc();
10588 if (c == cc)
10589 subtype++;
10590 else
10591 pungetc();
10592 break;
10593 }
10594 }
10595 } else {
10596 pungetc();
10597 }
10598 if (dblquote || arinest)
10599 flags |= VSQUOTE;
10600 *((char *)stackblock() + typeloc) = subtype | flags;
10601 if (subtype != VSNORMAL) {
10602 varnest++;
10603 if (dblquote || arinest) {
10604 dqvarnest++;
10605 }
10606 }
10607 }
10608 goto parsesub_return;
10609}
10610
10611
10612/*
10613 * Called to parse command substitutions. Newstyle is set if the command
10614 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10615 * list of commands (passed by reference), and savelen is the number of
10616 * characters on the top of the stack which must be preserved.
10617 */
10618
10619parsebackq: {
10620 struct nodelist **nlpp;
10621 int savepbq;
10622 union node *n;
10623 char *volatile str;
10624 struct jmploc jmploc;
10625 struct jmploc *volatile savehandler;
10626 size_t savelen;
10627 int saveprompt;
10628#ifdef __GNUC__
10629 (void) &saveprompt;
10630#endif
10631
10632 savepbq = parsebackquote;
10633 if (setjmp(jmploc.loc)) {
10634 if (str)
10635 ckfree(str);
10636 parsebackquote = 0;
10637 handler = savehandler;
10638 longjmp(handler->loc, 1);
10639 }
10640 INTOFF;
10641 str = NULL;
10642 savelen = out - (char *)stackblock();
10643 if (savelen > 0) {
10644 str = ckmalloc(savelen);
10645 memcpy(str, stackblock(), savelen);
10646 }
10647 savehandler = handler;
10648 handler = &jmploc;
10649 INTON;
10650 if (oldstyle) {
10651 /* We must read until the closing backquote, giving special
10652 treatment to some slashes, and then push the string and
10653 reread it as input, interpreting it normally. */
10654 char *pout;
10655 int pc;
10656 size_t psavelen;
10657 char *pstr;
10658
10659
10660 STARTSTACKSTR(pout);
10661 for (;;) {
10662 if (needprompt) {
10663 setprompt(2);
10664 needprompt = 0;
10665 }
10666 switch (pc = pgetc()) {
10667 case '`':
10668 goto done;
10669
10670 case '\\':
10671 if ((pc = pgetc()) == '\n') {
10672 plinno++;
10673 if (doprompt)
10674 setprompt(2);
10675 /*
10676 * If eating a newline, avoid putting
10677 * the newline into the new character
10678 * stream (via the STPUTC after the
10679 * switch).
10680 */
10681 continue;
10682 }
10683 if (pc != '\\' && pc != '`' && pc != '$'
10684 && (!dblquote || pc != '"'))
10685 STPUTC('\\', pout);
10686 if (pc > PEOA_OR_PEOF) {
10687 break;
10688 }
10689 /* fall through */
10690
10691 case PEOF:
10692#ifdef CONFIG_ASH_ALIAS
10693 case PEOA:
10694#endif
10695 startlinno = plinno;
10696 synerror("EOF in backquote substitution");
10697
10698 case '\n':
10699 plinno++;
10700 needprompt = doprompt;
10701 break;
10702
10703 default:
10704 break;
10705 }
10706 STPUTC(pc, pout);
10707 }
10708done:
10709 STPUTC('\0', pout);
10710 psavelen = pout - (char *)stackblock();
10711 if (psavelen > 0) {
10712 pstr = grabstackstr(pout);
10713 setinputstring(pstr);
10714 }
10715 }
10716 nlpp = &bqlist;
10717 while (*nlpp)
10718 nlpp = &(*nlpp)->next;
10719 *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
10720 (*nlpp)->next = NULL;
10721 parsebackquote = oldstyle;
10722
10723 if (oldstyle) {
10724 saveprompt = doprompt;
10725 doprompt = 0;
10726 }
10727
10728 n = list(2);
10729
10730 if (oldstyle)
10731 doprompt = saveprompt;
10732 else {
10733 if (readtoken() != TRP)
10734 synexpect(TRP);
10735 }
10736
10737 (*nlpp)->n = n;
10738 if (oldstyle) {
10739 /*
10740 * Start reading from old file again, ignoring any pushed back
10741 * tokens left from the backquote parsing
10742 */
10743 popfile();
10744 tokpushback = 0;
10745 }
10746 while (stackblocksize() <= savelen)
10747 growstackblock();
10748 STARTSTACKSTR(out);
10749 if (str) {
10750 memcpy(out, str, savelen);
10751 STADJUST(savelen, out);
10752 INTOFF;
10753 ckfree(str);
10754 str = NULL;
10755 INTON;
10756 }
10757 parsebackquote = savepbq;
10758 handler = savehandler;
10759 if (arinest || dblquote)
10760 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10761 else
10762 USTPUTC(CTLBACKQ, out);
10763 if (oldstyle)
10764 goto parsebackq_oldreturn;
10765 else
10766 goto parsebackq_newreturn;
10767}
10768
10769#ifdef CONFIG_ASH_MATH_SUPPORT
10770/*
10771 * Parse an arithmetic expansion (indicate start of one and set state)
10772 */
10773parsearith: {
10774
10775 if (++arinest == 1) {
10776 prevsyntax = syntax;
10777 syntax = ARISYNTAX;
10778 USTPUTC(CTLARI, out);
10779 if (dblquote)
10780 USTPUTC('"',out);
10781 else
10782 USTPUTC(' ',out);
10783 } else {
10784 /*
10785 * we collapse embedded arithmetic expansion to
10786 * parenthesis, which should be equivalent
10787 */
10788 USTPUTC('(', out);
10789 }
10790 goto parsearith_return;
10791}
10792#endif
10793
10794} /* end of readtoken */
10795
10796
10797
10798/*
10799 * Returns true if the text contains nothing to expand (no dollar signs
10800 * or backquotes).
10801 */
10802
10803static int
10804noexpand(char *text)
10805{
10806 char *p;
10807 char c;
10808
10809 p = text;
10810 while ((c = *p++) != '\0') {
10811 if (c == CTLQUOTEMARK)
10812 continue;
10813 if (c == CTLESC)
10814 p++;
10815 else if (SIT(c, BASESYNTAX) == CCTL)
10816 return 0;
10817 }
10818 return 1;
10819}
10820
10821
10822/*
10823 * Return of a legal variable name (a letter or underscore followed by zero or
10824 * more letters, underscores, and digits).
10825 */
10826
10827static char *
10828endofname(const char *name)
10829{
10830 char *p;
10831
10832 p = (char *) name;
10833 if (! is_name(*p))
10834 return p;
10835 while (*++p) {
10836 if (! is_in_name(*p))
10837 break;
10838 }
10839 return p;
10840}
10841
10842
10843/*
10844 * Called when an unexpected token is read during the parse. The argument
10845 * is the token that is expected, or -1 if more than one type of token can
10846 * occur at this point.
10847 */
10848
10849static void synexpect(int token)
10850{
10851 char msg[64];
10852 int l;
10853
10854 l = sprintf(msg, "%s unexpected", tokname(lasttoken));
10855 if (token >= 0)
10856 sprintf(msg + l, " (expecting %s)", tokname(token));
10857 synerror(msg);
10858 /* NOTREACHED */
10859}
10860
10861static void
10862synerror(const char *msg)
10863{
10864 error("Syntax error: %s", msg);
10865 /* NOTREACHED */
10866}
10867
10868
10869/*
10870 * called by editline -- any expansions to the prompt
10871 * should be added here.
10872 */
10873
10874static void setprompt(int whichprompt)
10875{
10876 const char *prompt;
10877
10878 switch (whichprompt) {
10879 case 1:
10880 prompt = ps1val();
10881 break;
10882 case 2:
10883 prompt = ps2val();
10884 break;
10885 default: /* 0 */
10886 prompt = nullstr;
10887 }
10888 putprompt(prompt);
10889}
10890
10891
10892static const char *const *findkwd(const char *s)
10893{
10894 return bsearch(s, tokname_array + KWDOFFSET,
10895 (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
10896 sizeof(const char *), pstrcmp);
10897}
10898
10899/* $NetBSD: redir.c,v 1.27 2002/11/24 22:35:42 christos Exp $ */
10900
10901/*
10902 * Code for dealing with input/output redirection.
10903 */
10904
10905#define EMPTY -2 /* marks an unused slot in redirtab */
10906#ifndef PIPE_BUF
10907# define PIPESIZE 4096 /* amount of buffering in a pipe */
10908#else
10909# define PIPESIZE PIPE_BUF
10910#endif
10911
10912/*
10913 * Open a file in noclobber mode.
10914 * The code was copied from bash.
10915 */
10916static inline int
10917noclobberopen(const char *fname)
10918{
10919 int r, fd;
10920 struct stat finfo, finfo2;
10921
10922 /*
10923 * If the file exists and is a regular file, return an error
10924 * immediately.
10925 */
10926 r = stat(fname, &finfo);
10927 if (r == 0 && S_ISREG(finfo.st_mode)) {
10928 errno = EEXIST;
10929 return -1;
10930 }
10931
10932 /*
10933 * If the file was not present (r != 0), make sure we open it
10934 * exclusively so that if it is created before we open it, our open
10935 * will fail. Make sure that we do not truncate an existing file.
10936 * Note that we don't turn on O_EXCL unless the stat failed -- if the
10937 * file was not a regular file, we leave O_EXCL off.
10938 */
10939 if (r != 0)
10940 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
10941 fd = open(fname, O_WRONLY|O_CREAT, 0666);
10942
10943 /* If the open failed, return the file descriptor right away. */
10944 if (fd < 0)
10945 return fd;
10946
10947 /*
10948 * OK, the open succeeded, but the file may have been changed from a
10949 * non-regular file to a regular file between the stat and the open.
10950 * We are assuming that the O_EXCL open handles the case where FILENAME
10951 * did not exist and is symlinked to an existing file between the stat
10952 * and open.
10953 */
10954
10955 /*
10956 * If we can open it and fstat the file descriptor, and neither check
10957 * revealed that it was a regular file, and the file has not been
10958 * replaced, return the file descriptor.
10959 */
10960 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
10961 finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
10962 return fd;
10963
10964 /* The file has been replaced. badness. */
10965 close(fd);
10966 errno = EEXIST;
10967 return -1;
10968}
10969
10970/*
10971 * Handle here documents. Normally we fork off a process to write the
10972 * data to a pipe. If the document is short, we can stuff the data in
10973 * the pipe without forking.
10974 */
10975
10976static inline int
10977openhere(union node *redir)
10978{
10979 int pip[2];
10980 size_t len = 0;
10981
10982 if (pipe(pip) < 0)
10983 error("Pipe call failed");
10984 if (redir->type == NHERE) {
10985 len = strlen(redir->nhere.doc->narg.text);
10986 if (len <= PIPESIZE) {
10987 bb_full_write(pip[1], redir->nhere.doc->narg.text, len);
10988 goto out;
10989 }
10990 }
10991 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
10992 close(pip[0]);
10993 signal(SIGINT, SIG_IGN);
10994 signal(SIGQUIT, SIG_IGN);
10995 signal(SIGHUP, SIG_IGN);
10996#ifdef SIGTSTP
10997 signal(SIGTSTP, SIG_IGN);
10998#endif
10999 signal(SIGPIPE, SIG_DFL);
11000 if (redir->type == NHERE)
11001 bb_full_write(pip[1], redir->nhere.doc->narg.text, len);
11002 else
11003 expandhere(redir->nhere.doc, pip[1]);
11004 _exit(0);
11005 }
11006out:
11007 close(pip[1]);
11008 return pip[0];
11009}
11010
11011static int
11012openredirect(union node *redir)
11013{
11014 char *fname;
11015 int f;
11016
11017 switch (redir->nfile.type) {
11018 case NFROM:
11019 fname = redir->nfile.expfname;
11020 if ((f = open(fname, O_RDONLY)) < 0)
11021 goto eopen;
11022 break;
11023 case NFROMTO:
11024 fname = redir->nfile.expfname;
11025 if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
11026 goto ecreate;
11027 break;
11028 case NTO:
11029 /* Take care of noclobber mode. */
11030 if (Cflag) {
11031 fname = redir->nfile.expfname;
11032 if ((f = noclobberopen(fname)) < 0)
11033 goto ecreate;
11034 break;
11035 }
11036 /* FALLTHROUGH */
11037 case NCLOBBER:
11038 fname = redir->nfile.expfname;
11039 if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
11040 goto ecreate;
11041 break;
11042 case NAPPEND:
11043 fname = redir->nfile.expfname;
11044 if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
11045 goto ecreate;
11046 break;
11047 default:
11048#ifdef DEBUG
11049 abort();
11050#endif
11051 /* Fall through to eliminate warning. */
11052 case NTOFD:
11053 case NFROMFD:
11054 f = -1;
11055 break;
11056 case NHERE:
11057 case NXHERE:
11058 f = openhere(redir);
11059 break;
11060 }
11061
11062 return f;
11063ecreate:
11064 error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
11065eopen:
11066 error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
11067}
11068
11069static inline void
11070dupredirect(union node *redir, int f)
11071{
11072 int fd = redir->nfile.fd;
11073
11074 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
11075 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
11076 copyfd(redir->ndup.dupfd, fd);
11077 }
11078 return;
11079 }
11080
11081 if (f != fd) {
11082 copyfd(f, fd);
11083 close(f);
11084 }
11085 return;
11086}
11087
11088/*
11089 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
11090 * old file descriptors are stashed away so that the redirection can be
11091 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
11092 * standard output, and the standard error if it becomes a duplicate of
11093 * stdout, is saved in memory.
11094 */
11095
11096static void
11097redirect(union node *redir, int flags)
11098{
11099 union node *n;
11100 struct redirtab *sv;
11101 int i;
11102 int fd;
11103 int newfd;
11104 int *p;
11105 nullredirs++;
11106 if (!redir) {
11107 return;
11108 }
11109 sv = NULL;
11110 INTOFF;
11111 if (flags & REDIR_PUSH) {
11112 struct redirtab *q;
11113 q = ckmalloc(sizeof (struct redirtab));
11114 q->next = redirlist;
11115 redirlist = q;
11116 q->nullredirs = nullredirs - 1;
11117 for (i = 0 ; i < 10 ; i++)
11118 q->renamed[i] = EMPTY;
11119 nullredirs = 0;
11120 sv = q;
11121 }
11122 n = redir;
11123 do {
11124 fd = n->nfile.fd;
11125 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
11126 n->ndup.dupfd == fd)
11127 continue; /* redirect from/to same file descriptor */
11128
11129 newfd = openredirect(n);
11130 if (fd == newfd)
11131 continue;
11132 if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
11133 i = fcntl(fd, F_DUPFD, 10);
11134
11135 if (i == -1) {
11136 i = errno;
11137 if (i != EBADF) {
11138 close(newfd);
11139 errno = i;
11140 error("%d: %m", fd);
11141 /* NOTREACHED */
11142 }
11143 } else {
11144 *p = i;
11145 close(fd);
11146 }
11147 } else {
11148 close(fd);
11149 }
11150 dupredirect(n, newfd);
11151 } while ((n = n->nfile.next));
11152 INTON;
11153 if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
11154 preverrout_fd = sv->renamed[2];
11155}
11156
11157
11158/*
11159 * Undo the effects of the last redirection.
11160 */
11161
11162void
11163popredir(int drop)
11164{
11165 struct redirtab *rp;
11166 int i;
11167
11168 if (--nullredirs >= 0)
11169 return;
11170 INTOFF;
11171 rp = redirlist;
11172 for (i = 0 ; i < 10 ; i++) {
11173 if (rp->renamed[i] != EMPTY) {
11174 if (!drop) {
11175 close(i);
11176 copyfd(rp->renamed[i], i);
11177 }
11178 close(rp->renamed[i]);
11179 }
11180 }
11181 redirlist = rp->next;
11182 nullredirs = rp->nullredirs;
11183 ckfree(rp);
11184 INTON;
11185}
11186
11187/*
11188 * Undo all redirections. Called on error or interrupt.
11189 */
11190
11191/*
11192 * Discard all saved file descriptors.
11193 */
11194
11195void
11196clearredir(int drop)
11197{
11198 for (;;) {
11199 nullredirs = 0;
11200 if (!redirlist)
11201 break;
11202 popredir(drop);
11203 }
11204}
11205
11206
11207/*
11208 * Copy a file descriptor to be >= to. Returns -1
11209 * if the source file descriptor is closed, EMPTY if there are no unused
11210 * file descriptors left.
11211 */
11212
11213int
11214copyfd(int from, int to)
11215{
11216 int newfd;
11217
11218 newfd = fcntl(from, F_DUPFD, to);
11219 if (newfd < 0) {
11220 if (errno == EMFILE)
11221 return EMPTY;
11222 else
11223 error("%d: %m", from);
11224 }
11225 return newfd;
11226}
11227
11228
11229int
11230redirectsafe(union node *redir, int flags)
11231{
11232 int err;
11233 volatile int saveint;
11234 struct jmploc *volatile savehandler = handler;
11235 struct jmploc jmploc;
11236
11237 SAVEINT(saveint);
11238 if (!(err = setjmp(jmploc.loc) * 2)) {
11239 handler = &jmploc;
11240 redirect(redir, flags);
11241 }
11242 handler = savehandler;
11243 if (err && exception != EXERROR)
11244 longjmp(handler->loc, 1);
11245 RESTOREINT(saveint);
11246 return err;
11247}
11248
11249/* $NetBSD: show.c,v 1.24 2003/01/22 20:36:04 dsl Exp $ */
11250
11251#ifdef DEBUG
11252static void shtree(union node *, int, char *, FILE*);
11253static void shcmd(union node *, FILE *);
11254static void sharg(union node *, FILE *);
11255static void indent(int, char *, FILE *);
11256static void trstring(char *);
11257
11258
11259void
11260showtree(union node *n)
11261{
11262 trputs("showtree called\n");
11263 shtree(n, 1, NULL, stdout);
11264}
11265
11266
11267static void
11268shtree(union node *n, int ind, char *pfx, FILE *fp)
11269{
11270 struct nodelist *lp;
11271 const char *s;
11272
11273 if (n == NULL)
11274 return;
11275
11276 indent(ind, pfx, fp);
11277 switch(n->type) {
11278 case NSEMI:
11279 s = "; ";
11280 goto binop;
11281 case NAND:
11282 s = " && ";
11283 goto binop;
11284 case NOR:
11285 s = " || ";
11286binop:
11287 shtree(n->nbinary.ch1, ind, NULL, fp);
11288 /* if (ind < 0) */
11289 fputs(s, fp);
11290 shtree(n->nbinary.ch2, ind, NULL, fp);
11291 break;
11292 case NCMD:
11293 shcmd(n, fp);
11294 if (ind >= 0)
11295 putc('\n', fp);
11296 break;
11297 case NPIPE:
11298 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
11299 shcmd(lp->n, fp);
11300 if (lp->next)
11301 fputs(" | ", fp);
11302 }
11303 if (n->npipe.backgnd)
11304 fputs(" &", fp);
11305 if (ind >= 0)
11306 putc('\n', fp);
11307 break;
11308 default:
11309 fprintf(fp, "<node type %d>", n->type);
11310 if (ind >= 0)
11311 putc('\n', fp);
11312 break;
11313 }
11314}
11315
11316
11317static void
11318shcmd(union node *cmd, FILE *fp)
11319{
11320 union node *np;
11321 int first;
11322 const char *s;
11323 int dftfd;
11324
11325 first = 1;
11326 for (np = cmd->ncmd.args ; np ; np = np->narg.next) {
11327 if (! first)
11328 putchar(' ');
11329 sharg(np, fp);
11330 first = 0;
11331 }
11332 for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) {
11333 if (! first)
11334 putchar(' ');
11335 switch (np->nfile.type) {
11336 case NTO: s = ">"; dftfd = 1; break;
11337 case NCLOBBER: s = ">|"; dftfd = 1; break;
11338 case NAPPEND: s = ">>"; dftfd = 1; break;
11339 case NTOFD: s = ">&"; dftfd = 1; break;
11340 case NFROM: s = "<"; dftfd = 0; break;
11341 case NFROMFD: s = "<&"; dftfd = 0; break;
11342 case NFROMTO: s = "<>"; dftfd = 0; break;
11343 default: s = "*error*"; dftfd = 0; break;
11344 }
11345 if (np->nfile.fd != dftfd)
11346 fprintf(fp, "%d", np->nfile.fd);
11347 fputs(s, fp);
11348 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
11349 fprintf(fp, "%d", np->ndup.dupfd);
11350 } else {
11351 sharg(np->nfile.fname, fp);
11352 }
11353 first = 0;
11354 }
11355}
11356
11357
11358
11359static void
11360sharg(union node *arg, FILE *fp)
11361{
11362 char *p;
11363 struct nodelist *bqlist;
11364 int subtype;
11365
11366 if (arg->type != NARG) {
11367 out1fmt("<node type %d>\n", arg->type);
11368 abort();
11369 }
11370 bqlist = arg->narg.backquote;
11371 for (p = arg->narg.text ; *p ; p++) {
11372 switch (*p) {
11373 case CTLESC:
11374 putc(*++p, fp);
11375 break;
11376 case CTLVAR:
11377 putc('$', fp);
11378 putc('{', fp);
11379 subtype = *++p;
11380 if (subtype == VSLENGTH)
11381 putc('#', fp);
11382
11383 while (*p != '=')
11384 putc(*p++, fp);
11385
11386 if (subtype & VSNUL)
11387 putc(':', fp);
11388
11389 switch (subtype & VSTYPE) {
11390 case VSNORMAL:
11391 putc('}', fp);
11392 break;
11393 case VSMINUS:
11394 putc('-', fp);
11395 break;
11396 case VSPLUS:
11397 putc('+', fp);
11398 break;
11399 case VSQUESTION:
11400 putc('?', fp);
11401 break;
11402 case VSASSIGN:
11403 putc('=', fp);
11404 break;
11405 case VSTRIMLEFT:
11406 putc('#', fp);
11407 break;
11408 case VSTRIMLEFTMAX:
11409 putc('#', fp);
11410 putc('#', fp);
11411 break;
11412 case VSTRIMRIGHT:
11413 putc('%', fp);
11414 break;
11415 case VSTRIMRIGHTMAX:
11416 putc('%', fp);
11417 putc('%', fp);
11418 break;
11419 case VSLENGTH:
11420 break;
11421 default:
11422 out1fmt("<subtype %d>", subtype);
11423 }
11424 break;
11425 case CTLENDVAR:
11426 putc('}', fp);
11427 break;
11428 case CTLBACKQ:
11429 case CTLBACKQ|CTLQUOTE:
11430 putc('$', fp);
11431 putc('(', fp);
11432 shtree(bqlist->n, -1, NULL, fp);
11433 putc(')', fp);
11434 break;
11435 default:
11436 putc(*p, fp);
11437 break;
11438 }
11439 }
11440}
11441
11442
11443static void
11444indent(int amount, char *pfx, FILE *fp)
11445{
11446 int i;
11447
11448 for (i = 0 ; i < amount ; i++) {
11449 if (pfx && i == amount - 1)
11450 fputs(pfx, fp);
11451 putc('\t', fp);
11452 }
11453}
11454
11455
11456
11457/*
11458 * Debugging stuff.
11459 */
11460
11461
11462FILE *tracefile;
11463
11464
11465void
11466trputc(int c)
11467{
11468 if (debug != 1)
11469 return;
11470 putc(c, tracefile);
11471}
11472
11473void
11474trace(const char *fmt, ...)
11475{
11476 va_list va;
11477
11478 if (debug != 1)
11479 return;
11480 va_start(va, fmt);
11481 (void) vfprintf(tracefile, fmt, va);
11482 va_end(va);
11483}
11484
11485void
11486tracev(const char *fmt, va_list va)
11487{
11488 if (debug != 1)
11489 return;
11490 (void) vfprintf(tracefile, fmt, va);
11491}
11492
11493
11494void
11495trputs(const char *s)
11496{
11497 if (debug != 1)
11498 return;
11499 fputs(s, tracefile);
11500}
11501
11502
11503static void
11504trstring(char *s)
11505{
11506 char *p;
11507 char c;
11508
11509 if (debug != 1)
11510 return;
11511 putc('"', tracefile);
11512 for (p = s ; *p ; p++) {
11513 switch (*p) {
11514 case '\n': c = 'n'; goto backslash;
11515 case '\t': c = 't'; goto backslash;
11516 case '\r': c = 'r'; goto backslash;
11517 case '"': c = '"'; goto backslash;
11518 case '\\': c = '\\'; goto backslash;
11519 case CTLESC: c = 'e'; goto backslash;
11520 case CTLVAR: c = 'v'; goto backslash;
11521 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
11522 case CTLBACKQ: c = 'q'; goto backslash;
11523 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
11524backslash: putc('\\', tracefile);
11525 putc(c, tracefile);
11526 break;
11527 default:
11528 if (*p >= ' ' && *p <= '~')
11529 putc(*p, tracefile);
11530 else {
11531 putc('\\', tracefile);
11532 putc(*p >> 6 & 03, tracefile);
11533 putc(*p >> 3 & 07, tracefile);
11534 putc(*p & 07, tracefile);
11535 }
11536 break;
11537 }
11538 }
11539 putc('"', tracefile);
11540}
11541
11542
11543void
11544trargs(char **ap)
11545{
11546 if (debug != 1)
11547 return;
11548 while (*ap) {
11549 trstring(*ap++);
11550 if (*ap)
11551 putc(' ', tracefile);
11552 else
11553 putc('\n', tracefile);
11554 }
11555}
11556
11557
11558void
11559opentrace(void)
11560{
11561 char s[100];
11562#ifdef O_APPEND
11563 int flags;
11564#endif
11565
11566 if (debug != 1) {
11567 if (tracefile)
11568 fflush(tracefile);
11569 /* leave open because libedit might be using it */
11570 return;
11571 }
11572 scopy("./trace", s);
11573 if (tracefile) {
11574 if (!freopen(s, "a", tracefile)) {
11575 fprintf(stderr, "Can't re-open %s\n", s);
11576 debug = 0;
11577 return;
11578 }
11579 } else {
11580 if ((tracefile = fopen(s, "a")) == NULL) {
11581 fprintf(stderr, "Can't open %s\n", s);
11582 debug = 0;
11583 return;
11584 }
11585 }
11586#ifdef O_APPEND
11587 if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0)
11588 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
11589#endif
11590 setlinebuf(tracefile);
11591 fputs("\nTracing started.\n", tracefile);
11592}
11593#endif /* DEBUG */
11594
11595
11596/* $NetBSD: trap.c,v 1.28 2002/11/24 22:35:43 christos Exp $ */
11597
11598/*
11599 * Sigmode records the current value of the signal handlers for the various
11600 * modes. A value of zero means that the current handler is not known.
11601 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
11602 */
11603
11604#define S_DFL 1 /* default signal handling (SIG_DFL) */
11605#define S_CATCH 2 /* signal is caught */
11606#define S_IGN 3 /* signal is ignored (SIG_IGN) */
11607#define S_HARD_IGN 4 /* signal is ignored permenantly */
11608#define S_RESET 5 /* temporary - to reset a hard ignored sig */
11609
11610
11611
11612/*
11613 * The trap builtin.
11614 */
11615
11616int
11617trapcmd(int argc, char **argv)
11618{
11619 char *action;
11620 char **ap;
11621 int signo;
11622
11623 nextopt(nullstr);
11624 ap = argptr;
11625 if (!*ap) {
11626 for (signo = 0 ; signo < NSIG ; signo++) {
11627 if (trap[signo] != NULL) {
11628 const char *sn;
11629
11630 sn = u_signal_names(0, &signo, 0);
11631 if (sn == NULL)
11632 sn = "???";
11633 out1fmt("trap -- %s %s\n",
11634 single_quote(trap[signo]), sn);
11635 }
11636 }
11637 return 0;
11638 }
11639 if (!ap[1])
11640 action = NULL;
11641 else
11642 action = *ap++;
11643 while (*ap) {
11644 if ((signo = decode_signal(*ap, 0)) < 0)
11645 error("%s: bad trap", *ap);
11646 INTOFF;
11647 if (action) {
11648 if (action[0] == '-' && action[1] == '\0')
11649 action = NULL;
11650 else
11651 action = savestr(action);
11652 }
11653 if (trap[signo])
11654 ckfree(trap[signo]);
11655 trap[signo] = action;
11656 if (signo != 0)
11657 setsignal(signo);
11658 INTON;
11659 ap++;
11660 }
11661 return 0;
11662}
11663
11664
11665/*
11666 * Clear traps on a fork.
11667 */
11668
11669void
11670clear_traps(void)
11671{
11672 char **tp;
11673
11674 for (tp = trap ; tp < &trap[NSIG] ; tp++) {
11675 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
11676 INTOFF;
11677 ckfree(*tp);
11678 *tp = NULL;
11679 if (tp != &trap[0])
11680 setsignal(tp - trap);
11681 INTON;
11682 }
11683 }
11684}
11685
11686
11687/*
11688 * Set the signal handler for the specified signal. The routine figures
11689 * out what it should be set to.
11690 */
11691
11692void
11693setsignal(int signo)
11694{
11695 int action;
11696 char *t, tsig;
11697 struct sigaction act;
11698
11699 if ((t = trap[signo]) == NULL)
11700 action = S_DFL;
11701 else if (*t != '\0')
11702 action = S_CATCH;
11703 else
11704 action = S_IGN;
11705 if (rootshell && action == S_DFL) {
11706 switch (signo) {
11707 case SIGINT:
11708 if (iflag || minusc || sflag == 0)
11709 action = S_CATCH;
11710 break;
11711 case SIGQUIT:
11712#ifdef DEBUG
11713 if (debug)
11714 break;
11715#endif
11716 /* FALLTHROUGH */
11717 case SIGTERM:
11718 if (iflag)
11719 action = S_IGN;
11720 break;
11721#if JOBS
11722 case SIGTSTP:
11723 case SIGTTOU:
11724 if (mflag)
11725 action = S_IGN;
11726 break;
11727#endif
11728 }
11729 }
11730
11731 t = &sigmode[signo - 1];
11732 tsig = *t;
11733 if (tsig == 0) {
11734 /*
11735 * current setting unknown
11736 */
11737 if (sigaction(signo, 0, &act) == -1) {
11738 /*
11739 * Pretend it worked; maybe we should give a warning
11740 * here, but other shells don't. We don't alter
11741 * sigmode, so that we retry every time.
11742 */
11743 return;
11744 }
11745 if (act.sa_handler == SIG_IGN) {
11746 if (mflag && (signo == SIGTSTP ||
11747 signo == SIGTTIN || signo == SIGTTOU)) {
11748 tsig = S_IGN; /* don't hard ignore these */
11749 } else
11750 tsig = S_HARD_IGN;
11751 } else {
11752 tsig = S_RESET; /* force to be set */
11753 }
11754 }
11755 if (tsig == S_HARD_IGN || tsig == action)
11756 return;
11757 switch (action) {
11758 case S_CATCH:
11759 act.sa_handler = onsig;
11760 break;
11761 case S_IGN:
11762 act.sa_handler = SIG_IGN;
11763 break;
11764 default:
11765 act.sa_handler = SIG_DFL;
11766 }
11767 *t = action;
11768 act.sa_flags = 0;
11769 sigfillset(&act.sa_mask);
11770 sigaction(signo, &act, 0);
11771}
11772
11773/*
11774 * Ignore a signal.
11775 */
11776
11777void
11778ignoresig(int signo)
11779{
11780 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
11781 signal(signo, SIG_IGN);
11782 }
11783 sigmode[signo - 1] = S_HARD_IGN;
11784}
11785
11786
11787/*
11788 * Signal handler.
11789 */
11790
11791void
11792onsig(int signo)
11793{
11794 gotsig[signo - 1] = 1;
11795 pendingsigs = signo;
11796
11797 if (exsig || (signo == SIGINT && !trap[SIGINT])) {
11798 if (!suppressint)
11799 onint();
11800 intpending = 1;
11801 }
11802}
11803
11804
11805/*
11806 * Called to execute a trap. Perhaps we should avoid entering new trap
11807 * handlers while we are executing a trap handler.
11808 */
11809
11810void
11811dotrap(void)
11812{
11813 char *p;
11814 char *q;
11815 int savestatus;
11816
11817 savestatus = exitstatus;
11818 q = gotsig;
11819 while (pendingsigs = 0, xbarrier(), (p = memchr(q, 1, NSIG - 1))) {
11820 *p = 0;
11821 p = trap[p - q + 1];
11822 if (!p)
11823 continue;
11824 evalstring(p);
11825 exitstatus = savestatus;
11826 }
11827}
11828
11829
11830/*
11831 * Controls whether the shell is interactive or not.
11832 */
11833
11834void
11835setinteractive(int on)
11836{
11837 static int is_interactive;
11838
11839 if (++on == is_interactive)
11840 return;
11841 is_interactive = on;
11842 setsignal(SIGINT);
11843 setsignal(SIGQUIT);
11844 setsignal(SIGTERM);
11845#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
11846 if(is_interactive > 1) {
11847 /* Looks like they want an interactive shell */
11848 static int do_banner;
11849
11850 if(!do_banner) {
11851 out1fmt(
11852 "\n\n" BB_BANNER " Built-in shell (ash)\n"
11853 "Enter 'help' for a list of built-in commands.\n\n");
11854 do_banner++;
11855 }
11856 }
11857#endif
11858}
11859
11860
11861#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
11862/*** List the available builtins ***/
11863
11864static int helpcmd(int argc, char **argv)
11865{
11866 int col, i;
11867
11868 out1fmt("\nBuilt-in commands:\n-------------------\n");
11869 for (col = 0, i = 0; i < NUMBUILTINS; i++) {
11870 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
11871 builtincmd[i].name + 1);
11872 if (col > 60) {
11873 out1fmt("\n");
11874 col = 0;
11875 }
11876 }
11877#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
11878 {
11879 extern const struct BB_applet applets[];
11880 extern const size_t NUM_APPLETS;
11881
11882 for (i = 0; i < NUM_APPLETS; i++) {
11883
11884 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
11885 if (col > 60) {
11886 out1fmt("\n");
11887 col = 0;
11888 }
11889 }
11890 }
11891#endif
11892 out1fmt("\n\n");
11893 return EXIT_SUCCESS;
11894}
11895#endif /* CONFIG_FEATURE_SH_EXTRA_QUIET */
11896
11897/*
11898 * Called to exit the shell.
11899 */
11900
11901void
11902exitshell(void)
11903{
11904 struct jmploc loc;
11905 char *p;
11906 int status;
11907 int jmp;
11908
11909 jmp = setjmp(loc.loc);
11910 status = exitstatus;
11911 TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
11912 if (jmp)
11913 goto out;
11914 handler = &loc;
11915 if ((p = trap[0]) != NULL && *p != '\0') {
11916 trap[0] = NULL;
11917 evalstring(p);
11918 }
11919 flushall();
11920 setjobctl(0);
11921#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
11922 if (iflag && rootshell) {
11923 const char *hp = lookupvar("HISTFILE");
11924
11925 if(hp != NULL )
11926 save_history ( hp );
11927 }
11928#endif
11929out:
11930 _exit(status);
11931 /* NOTREACHED */
11932}
11933
11934static int decode_signal(const char *string, int minsig)
11935{
11936 int signo;
11937 const char *name = u_signal_names(string, &signo, minsig);
11938
11939 return name ? signo : -1;
11940}
11941
11942/* $NetBSD: var.c,v 1.32 2003/01/22 20:36:04 dsl Exp $ */
11943
11944static struct var *vartab[VTABSIZE];
11945
11946static int vpcmp(const void *, const void *);
11947static struct var **findvar(struct var **, const char *);
11948
11949/*
11950 * Initialize the variable symbol tables and import the environment
11951 */
11952
11953
11954#ifdef CONFIG_ASH_GETOPTS
11955/*
11956 * Safe version of setvar, returns 1 on success 0 on failure.
11957 */
11958
11959int
11960setvarsafe(const char *name, const char *val, int flags)
11961{
11962 int err;
11963 volatile int saveint;
11964 struct jmploc *volatile savehandler = handler;
11965 struct jmploc jmploc;
11966
11967 SAVEINT(saveint);
11968 if (setjmp(jmploc.loc))
11969 err = 1;
11970 else {
11971 handler = &jmploc;
11972 setvar(name, val, flags);
11973 err = 0;
11974 }
11975 handler = savehandler;
11976 RESTOREINT(saveint);
11977 return err;
11978}
11979#endif
11980
11981/*
11982 * Set the value of a variable. The flags argument is ored with the
11983 * flags of the variable. If val is NULL, the variable is unset.
11984 */
11985
11986static void
11987setvar(const char *name, const char *val, int flags)
11988{
11989 char *p, *q;
11990 size_t namelen;
11991 char *nameeq;
11992 size_t vallen;
11993
11994 q = endofname(name);
11995 p = strchrnul(q, '=');
11996 namelen = p - name;
11997 if (!namelen || p != q)
11998 error("%.*s: bad variable name", namelen, name);
11999 vallen = 0;
12000 if (val == NULL) {
12001 flags |= VUNSET;
12002 } else {
12003 vallen = strlen(val);
12004 }
12005 INTOFF;
12006 p = mempcpy(nameeq = ckmalloc(namelen + vallen + 2), name, namelen);
12007 *p++ = '\0';
12008 if (vallen) {
12009 p[-1] = '=';
12010 p = mempcpy(p, val, vallen);
12011 }
12012 *p = '\0';
12013 setvareq(nameeq, flags | VNOSAVE);
12014 INTON;
12015}
12016
12017
12018/*
12019 * Same as setvar except that the variable and value are passed in
12020 * the first argument as name=value. Since the first argument will
12021 * be actually stored in the table, it should not be a string that
12022 * will go away.
12023 * Called with interrupts off.
12024 */
12025
12026void
12027setvareq(char *s, int flags)
12028{
12029 struct var *vp, **vpp;
12030
12031 vpp = hashvar(s);
12032 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
12033 vp = *findvar(vpp, s);
12034 if (vp) {
12035 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
12036 const char *n;
12037
12038 if (flags & VNOSAVE)
12039 free(s);
12040 n = vp->text;
12041 error("%.*s: is read only", strchrnul(n, '=') - n, n);
12042 }
12043
12044 if (flags & VNOSET)
12045 return;
12046
12047 if (vp->func && (flags & VNOFUNC) == 0)
12048 (*vp->func)(strchrnul(s, '=') + 1);
12049
12050 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
12051 ckfree(vp->text);
12052
12053 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
12054 } else {
12055 if (flags & VNOSET)
12056 return;
12057 /* not found */
12058 vp = ckmalloc(sizeof (*vp));
12059 vp->next = *vpp;
12060 vp->func = NULL;
12061 *vpp = vp;
12062 }
12063 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
12064 s = savestr(s);
12065 vp->text = s;
12066 vp->flags = flags;
12067}
12068
12069
12070/*
12071 * Process a linked list of variable assignments.
12072 */
12073
12074static void
12075listsetvar(struct strlist *list_set_var, int flags)
12076{
12077 struct strlist *lp = list_set_var;
12078
12079 if (!lp)
12080 return;
12081 INTOFF;
12082 do {
12083 setvareq(lp->text, flags);
12084 } while ((lp = lp->next));
12085 INTON;
12086}
12087
12088
12089/*
12090 * Find the value of a variable. Returns NULL if not set.
12091 */
12092
12093static char *
12094lookupvar(const char *name)
12095{
12096 struct var *v;
12097
12098 if ((v = *findvar(hashvar(name), name))) {
12099#ifdef DYNAMIC_VAR
12100 /*
12101 * Dynamic variables are implemented roughly the same way they are
12102 * in bash. Namely, they're "special" so long as they aren't unset.
12103 * As soon as they're unset, they're no longer dynamic, and dynamic
12104 * lookup will no longer happen at that point. -- PFM.
12105 */
12106 if((v->flags & VDYNAMIC))
12107 (*v->func)(NULL);
12108#endif
12109 if(!(v->flags & VUNSET))
12110 return strchrnul(v->text, '=') + 1;
12111 }
12112
12113 return NULL;
12114}
12115
12116
12117/*
12118 * Search the environment of a builtin command.
12119 */
12120
12121static char *
12122bltinlookup(const char *name)
12123{
12124 struct strlist *sp;
12125
12126 for (sp = cmdenviron ; sp ; sp = sp->next) {
12127 if (varequal(sp->text, name))
12128 return strchrnul(sp->text, '=') + 1;
12129 }
12130 return lookupvar(name);
12131}
12132
12133
12134/*
12135 * Generate a list of variables satisfying the given conditions.
12136 */
12137
12138static char **
12139listvars(int on, int off, char ***end)
12140{
12141 struct var **vpp;
12142 struct var *vp;
12143 char **ep;
12144 int mask;
12145
12146 STARTSTACKSTR(ep);
12147 vpp = vartab;
12148 mask = on | off;
12149 do {
12150 for (vp = *vpp ; vp ; vp = vp->next)
12151 if ((vp->flags & mask) == on) {
12152 if (ep == stackstrend())
12153 ep = growstackstr();
12154 *ep++ = (char *) vp->text;
12155 }
12156 } while (++vpp < vartab + VTABSIZE);
12157 if (ep == stackstrend())
12158 ep = growstackstr();
12159 if (end)
12160 *end = ep;
12161 *ep++ = NULL;
12162 return grabstackstr(ep);
12163}
12164
12165
12166/*
12167 * POSIX requires that 'set' (but not export or readonly) output the
12168 * variables in lexicographic order - by the locale's collating order (sigh).
12169 * Maybe we could keep them in an ordered balanced binary tree
12170 * instead of hashed lists.
12171 * For now just roll 'em through qsort for printing...
12172 */
12173
12174static int
12175showvars(const char *sep_prefix, int on, int off)
12176{
12177 const char *sep;
12178 char **ep, **epend;
12179
12180 ep = listvars(on, off, &epend);
12181 qsort(ep, epend - ep, sizeof(char *), vpcmp);
12182
12183 sep = *sep_prefix ? spcstr : sep_prefix;
12184
12185 for (; ep < epend; ep++) {
12186 const char *p;
12187 const char *q;
12188
12189 p = strchrnul(*ep, '=');
12190 q = nullstr;
12191 if (*p)
12192 q = single_quote(++p);
12193
12194 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
12195 }
12196
12197 return 0;
12198}
12199
12200
12201
12202/*
12203 * The export and readonly commands.
12204 */
12205
12206static int
12207exportcmd(int argc, char **argv)
12208{
12209 struct var *vp;
12210 char *name;
12211 const char *p;
12212 char **aptr;
12213 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
12214 int notp;
12215
12216 notp = nextopt("p") - 'p';
12217 if (notp && ((name = *(aptr = argptr)))) {
12218 do {
12219 if ((p = strchr(name, '=')) != NULL) {
12220 p++;
12221 } else {
12222 if ((vp = *findvar(hashvar(name), name))) {
12223 vp->flags |= flag;
12224 continue;
12225 }
12226 }
12227 setvar(name, p, flag);
12228 } while ((name = *++aptr) != NULL);
12229 } else {
12230 showvars(argv[0], flag, 0);
12231 }
12232 return 0;
12233}
12234
12235
12236/*
12237 * Make a variable a local variable. When a variable is made local, it's
12238 * value and flags are saved in a localvar structure. The saved values
12239 * will be restored when the shell function returns. We handle the name
12240 * "-" as a special case.
12241 */
12242
12243static inline void
12244mklocal(char *name)
12245{
12246 struct localvar *lvp;
12247 struct var **vpp;
12248 struct var *vp;
12249
12250 INTOFF;
12251 lvp = ckmalloc(sizeof (struct localvar));
12252 if (name[0] == '-' && name[1] == '\0') {
12253 char *p;
12254 p = ckmalloc(sizeof(optlist));
12255 lvp->text = memcpy(p, optlist, sizeof(optlist));
12256 vp = NULL;
12257 } else {
12258 char *eq;
12259
12260 vpp = hashvar(name);
12261 vp = *findvar(vpp, name);
12262 eq = strchr(name, '=');
12263 if (vp == NULL) {
12264 if (eq)
12265 setvareq(name, VSTRFIXED);
12266 else
12267 setvar(name, NULL, VSTRFIXED);
12268 vp = *vpp; /* the new variable */
12269 lvp->flags = VUNSET;
12270 } else {
12271 lvp->text = vp->text;
12272 lvp->flags = vp->flags;
12273 vp->flags |= VSTRFIXED|VTEXTFIXED;
12274 if (eq)
12275 setvareq(name, 0);
12276 }
12277 }
12278 lvp->vp = vp;
12279 lvp->next = localvars;
12280 localvars = lvp;
12281 INTON;
12282}
12283
12284/*
12285 * The "local" command.
12286 */
12287
12288static int
12289localcmd(int argc, char **argv)
12290{
12291 char *name;
12292
12293 argv = argptr;
12294 while ((name = *argv++) != NULL) {
12295 mklocal(name);
12296 }
12297 return 0;
12298}
12299
12300
12301/*
12302 * Called after a function returns.
12303 * Interrupts must be off.
12304 */
12305
12306static void
12307poplocalvars(void)
12308{
12309 struct localvar *lvp;
12310 struct var *vp;
12311
12312 while ((lvp = localvars) != NULL) {
12313 localvars = lvp->next;
12314 vp = lvp->vp;
12315 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
12316 if (vp == NULL) { /* $- saved */
12317 memcpy(optlist, lvp->text, sizeof(optlist));
12318 ckfree(lvp->text);
12319 optschanged();
12320 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
12321 unsetvar(vp->text);
12322 } else {
12323 if (vp->func)
12324 (*vp->func)(strchrnul(lvp->text, '=') + 1);
12325 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
12326 ckfree(vp->text);
12327 vp->flags = lvp->flags;
12328 vp->text = lvp->text;
12329 }
12330 ckfree(lvp);
12331 }
12332}
12333
12334
12335/*
12336 * The unset builtin command. We unset the function before we unset the
12337 * variable to allow a function to be unset when there is a readonly variable
12338 * with the same name.
12339 */
12340
12341int
12342unsetcmd(int argc, char **argv)
12343{
12344 char **ap;
12345 int i;
12346 int flag = 0;
12347 int ret = 0;
12348
12349 while ((i = nextopt("vf")) != '\0') {
12350 flag = i;
12351 }
12352
12353 for (ap = argptr; *ap ; ap++) {
12354 if (flag != 'f') {
12355 i = unsetvar(*ap);
12356 ret |= i;
12357 if (!(i & 2))
12358 continue;
12359 }
12360 if (flag != 'v')
12361 unsetfunc(*ap);
12362 }
12363 return ret & 1;
12364}
12365
12366
12367/*
12368 * Unset the specified variable.
12369 */
12370
12371int
12372unsetvar(const char *s)
12373{
12374 struct var **vpp;
12375 struct var *vp;
12376 int retval;
12377
12378 vpp = findvar(hashvar(s), s);
12379 vp = *vpp;
12380 retval = 2;
12381 if (vp) {
12382 int flags = vp->flags;
12383
12384 retval = 1;
12385 if (flags & VREADONLY)
12386 goto out;
12387#ifdef DYNAMIC_VAR
12388 vp->flags &= ~VDYNAMIC;
12389#endif
12390 if (flags & VUNSET)
12391 goto ok;
12392 if ((flags & VSTRFIXED) == 0) {
12393 INTOFF;
12394 if ((flags & (VTEXTFIXED|VSTACK)) == 0)
12395 ckfree(vp->text);
12396 *vpp = vp->next;
12397 ckfree(vp);
12398 INTON;
12399 } else {
12400 setvar(s, 0, 0);
12401 vp->flags &= ~VEXPORT;
12402 }
12403ok:
12404 retval = 0;
12405 }
12406
12407out:
12408 return retval;
12409}
12410
12411
12412
12413/*
12414 * Find the appropriate entry in the hash table from the name.
12415 */
12416
12417static struct var **
12418hashvar(const char *p)
12419{
12420 unsigned int hashval;
12421
12422 hashval = ((unsigned char) *p) << 4;
12423 while (*p && *p != '=')
12424 hashval += (unsigned char) *p++;
12425 return &vartab[hashval % VTABSIZE];
12426}
12427
12428
12429
12430/*
12431 * Compares two strings up to the first = or '\0'. The first
12432 * string must be terminated by '='; the second may be terminated by
12433 * either '=' or '\0'.
12434 */
12435
12436int
12437varcmp(const char *p, const char *q)
12438{
12439 int c, d;
12440
12441 while ((c = *p) == (d = *q)) {
12442 if (!c || c == '=')
12443 goto out;
12444 p++;
12445 q++;
12446 }
12447 if (c == '=')
12448 c = 0;
12449 if (d == '=')
12450 d = 0;
12451out:
12452 return c - d;
12453}
12454
12455static int
12456vpcmp(const void *a, const void *b)
12457{
12458 return varcmp(*(const char **)a, *(const char **)b);
12459}
12460
12461static struct var **
12462findvar(struct var **vpp, const char *name)
12463{
12464 for (; *vpp; vpp = &(*vpp)->next) {
12465 if (varequal((*vpp)->text, name)) {
12466 break;
12467 }
12468 }
12469 return vpp;
12470}
12471/* $NetBSD: setmode.c,v 1.29 2003/01/15 23:58:03 kleink Exp $ */
12472
12473#include <sys/times.h>
12474
12475static const unsigned char timescmd_str[] = {
12476 ' ', offsetof(struct tms, tms_utime),
12477 '\n', offsetof(struct tms, tms_stime),
12478 ' ', offsetof(struct tms, tms_cutime),
12479 '\n', offsetof(struct tms, tms_cstime),
12480 0
12481};
12482
12483static int timescmd(int ac, char **av)
12484{
12485 long int clk_tck, s, t;
12486 const unsigned char *p;
12487 struct tms buf;
12488
12489 clk_tck = sysconf(_SC_CLK_TCK);
12490 times(&buf);
12491
12492 p = timescmd_str;
12493 do {
12494 t = *(clock_t *)(((char *) &buf) + p[1]);
12495 s = t / clk_tck;
12496 out1fmt("%ldm%ld.%.3lds%c",
12497 s/60, s%60,
12498 ((t - s * clk_tck) * 1000) / clk_tck,
12499 p[0]);
12500 } while (*(p += 2));
12501
12502 return 0;
12503}
12504
12505#ifdef CONFIG_ASH_MATH_SUPPORT
12506static arith_t
12507dash_arith(const char *s)
12508{
12509 arith_t result;
12510 int errcode = 0;
12511
12512 INTOFF;
12513 result = arith(s, &errcode);
12514 if (errcode < 0) {
12515 if (errcode == -3)
12516 error("exponent less than 0");
12517 else if (errcode == -2)
12518 error("divide by zero");
12519 else if (errcode == -5)
12520 error("expression recursion loop detected");
12521 else
12522 synerror(s);
12523 }
12524 INTON;
12525
12526 return (result);
12527}
12528
12529
12530/*
12531 * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
12532 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
12533 *
12534 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12535 */
12536
12537static int
12538letcmd(int argc, char **argv)
12539{
12540 char **ap;
12541 arith_t i;
12542
12543 ap = argv + 1;
12544 if(!*ap)
12545 error("expression expected");
12546 for (ap = argv + 1; *ap; ap++) {
12547 i = dash_arith(*ap);
12548 }
12549
12550 return (!i);
12551}
12552#endif /* CONFIG_ASH_MATH_SUPPORT */
12553
12554/* $NetBSD: miscbltin.c,v 1.31 2002/11/24 22:35:41 christos Exp $ */
12555
12556/*
12557 * Miscellaneous builtins.
12558 */
12559
12560#undef rflag
12561
12562#ifdef __GLIBC__
12563#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
12564typedef enum __rlimit_resource rlim_t;
12565#endif
12566#endif
12567
12568
12569/*
12570 * The read builtin. The -e option causes backslashes to escape the
12571 * following character.
12572 *
12573 * This uses unbuffered input, which may be avoidable in some cases.
12574 */
12575
12576static int
12577readcmd(int argc, char **argv)
12578{
12579 char **ap;
12580 int backslash;
12581 char c;
12582 int rflag;
12583 char *prompt;
12584 const char *ifs;
12585 char *p;
12586 int startword;
12587 int status;
12588 int i;
12589
12590 rflag = 0;
12591 prompt = NULL;
12592 while ((i = nextopt("p:r")) != '\0') {
12593 if (i == 'p')
12594 prompt = optionarg;
12595 else
12596 rflag = 1;
12597 }
12598 if (prompt && isatty(0)) {
12599 out2str(prompt);
12600 }
12601 if (*(ap = argptr) == NULL)
12602 error("arg count");
12603 if ((ifs = bltinlookup("IFS")) == NULL)
12604 ifs = defifs;
12605 status = 0;
12606 startword = 1;
12607 backslash = 0;
12608 STARTSTACKSTR(p);
12609 for (;;) {
12610 if (read(0, &c, 1) != 1) {
12611 status = 1;
12612 break;
12613 }
12614 if (c == '\0')
12615 continue;
12616 if (backslash) {
12617 backslash = 0;
12618 if (c != '\n')
12619 goto put;
12620 continue;
12621 }
12622 if (!rflag && c == '\\') {
12623 backslash++;
12624 continue;
12625 }
12626 if (c == '\n')
12627 break;
12628 if (startword && *ifs == ' ' && strchr(ifs, c)) {
12629 continue;
12630 }
12631 startword = 0;
12632 if (ap[1] != NULL && strchr(ifs, c) != NULL) {
12633 STACKSTRNUL(p);
12634 setvar(*ap, stackblock(), 0);
12635 ap++;
12636 startword = 1;
12637 STARTSTACKSTR(p);
12638 } else {
12639put:
12640 STPUTC(c, p);
12641 }
12642 }
12643 STACKSTRNUL(p);
12644 /* Remove trailing blanks */
12645 while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
12646 *p = '\0';
12647 setvar(*ap, stackblock(), 0);
12648 while (*++ap != NULL)
12649 setvar(*ap, nullstr, 0);
12650 return status;
12651}
12652
12653
12654static int umaskcmd(int argc, char **argv)
12655{
12656 static const char permuser[3] = "ugo";
12657 static const char permmode[3] = "rwx";
12658 static const short int permmask[] = {
12659 S_IRUSR, S_IWUSR, S_IXUSR,
12660 S_IRGRP, S_IWGRP, S_IXGRP,
12661 S_IROTH, S_IWOTH, S_IXOTH
12662 };
12663
12664 char *ap;
12665 mode_t mask;
12666 int i;
12667 int symbolic_mode = 0;
12668
12669 while (nextopt("S") != '\0') {
12670 symbolic_mode = 1;
12671 }
12672
12673 INTOFF;
12674 mask = umask(0);
12675 umask(mask);
12676 INTON;
12677
12678 if ((ap = *argptr) == NULL) {
12679 if (symbolic_mode) {
12680 char buf[18];
12681 char *p = buf;
12682
12683 for (i = 0; i < 3; i++) {
12684 int j;
12685
12686 *p++ = permuser[i];
12687 *p++ = '=';
12688 for (j = 0; j < 3; j++) {
12689 if ((mask & permmask[3 * i + j]) == 0) {
12690 *p++ = permmode[j];
12691 }
12692 }
12693 *p++ = ',';
12694 }
12695 *--p = 0;
12696 puts(buf);
12697 } else {
12698 out1fmt("%.4o\n", mask);
12699 }
12700 } else {
12701 if (is_digit((unsigned char) *ap)) {
12702 mask = 0;
12703 do {
12704 if (*ap >= '8' || *ap < '0')
12705 error(illnum, argv[1]);
12706 mask = (mask << 3) + (*ap - '0');
12707 } while (*++ap != '\0');
12708 umask(mask);
12709 } else {
12710 mask = ~mask & 0777;
12711 if (!bb_parse_mode(ap, &mask)) {
12712 error("Illegal mode: %s", ap);
12713 }
12714 umask(~mask & 0777);
12715 }
12716 }
12717 return 0;
12718}
12719
12720/*
12721 * ulimit builtin
12722 *
12723 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
12724 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
12725 * ash by J.T. Conklin.
12726 *
12727 * Public domain.
12728 */
12729
12730struct limits {
12731 const char *name;
12732 int cmd;
12733 int factor; /* multiply by to get rlim_{cur,max} values */
12734 char option;
12735};
12736
12737static const struct limits limits[] = {
12738#ifdef RLIMIT_CPU
12739 { "time(seconds)", RLIMIT_CPU, 1, 't' },
12740#endif
12741#ifdef RLIMIT_FSIZE
12742 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
12743#endif
12744#ifdef RLIMIT_DATA
12745 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
12746#endif
12747#ifdef RLIMIT_STACK
12748 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
12749#endif
12750#ifdef RLIMIT_CORE
12751 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
12752#endif
12753#ifdef RLIMIT_RSS
12754 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
12755#endif
12756#ifdef RLIMIT_MEMLOCK
12757 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
12758#endif
12759#ifdef RLIMIT_NPROC
12760 { "process", RLIMIT_NPROC, 1, 'p' },
12761#endif
12762#ifdef RLIMIT_NOFILE
12763 { "nofiles", RLIMIT_NOFILE, 1, 'n' },
12764#endif
12765#ifdef RLIMIT_AS
12766 { "vmemory(kbytes)", RLIMIT_AS, 1024, 'v' },
12767#endif
12768#ifdef RLIMIT_LOCKS
12769 { "locks", RLIMIT_LOCKS, 1, 'w' },
12770#endif
12771 { (char *) 0, 0, 0, '\0' }
12772};
12773
12774enum limtype { SOFT = 0x1, HARD = 0x2 };
12775
12776static void printlim(enum limtype how, const struct rlimit *limit,
12777 const struct limits *l)
12778{
12779 rlim_t val;
12780
12781 val = limit->rlim_max;
12782 if (how & SOFT)
12783 val = limit->rlim_cur;
12784
12785 if (val == RLIM_INFINITY)
12786 out1fmt("unlimited\n");
12787 else {
12788 val /= l->factor;
12789 out1fmt("%lld\n", (long long) val);
12790 }
12791}
12792
12793int
12794ulimitcmd(int argc, char **argv)
12795{
12796 int c;
12797 rlim_t val = 0;
12798 enum limtype how = SOFT | HARD;
12799 const struct limits *l;
12800 int set, all = 0;
12801 int optc, what;
12802 struct rlimit limit;
12803
12804 what = 'f';
12805 while ((optc = nextopt("HSa"
12806#ifdef RLIMIT_CPU
12807 "t"
12808#endif
12809#ifdef RLIMIT_FSIZE
12810 "f"
12811#endif
12812#ifdef RLIMIT_DATA
12813 "d"
12814#endif
12815#ifdef RLIMIT_STACK
12816 "s"
12817#endif
12818#ifdef RLIMIT_CORE
12819 "c"
12820#endif
12821#ifdef RLIMIT_RSS
12822 "m"
12823#endif
12824#ifdef RLIMIT_MEMLOCK
12825 "l"
12826#endif
12827#ifdef RLIMIT_NPROC
12828 "p"
12829#endif
12830#ifdef RLIMIT_NOFILE
12831 "n"
12832#endif
12833#ifdef RLIMIT_AS
12834 "v"
12835#endif
12836#ifdef RLIMIT_LOCKS
12837 "w"
12838#endif
12839 )) != '\0')
12840 switch (optc) {
12841 case 'H':
12842 how = HARD;
12843 break;
12844 case 'S':
12845 how = SOFT;
12846 break;
12847 case 'a':
12848 all = 1;
12849 break;
12850 default:
12851 what = optc;
12852 }
12853
12854 for (l = limits; l->option != what; l++)
12855 ;
12856
12857 set = *argptr ? 1 : 0;
12858 if (set) {
12859 char *p = *argptr;
12860
12861 if (all || argptr[1])
12862 error("too many arguments");
12863 if (strncmp(p, "unlimited\n", 9) == 0)
12864 val = RLIM_INFINITY;
12865 else {
12866 val = (rlim_t) 0;
12867
12868 while ((c = *p++) >= '0' && c <= '9')
12869 {
12870 val = (val * 10) + (long)(c - '0');
12871 if (val < (rlim_t) 0)
12872 break;
12873 }
12874 if (c)
12875 error("bad number");
12876 val *= l->factor;
12877 }
12878 }
12879 if (all) {
12880 for (l = limits; l->name; l++) {
12881 getrlimit(l->cmd, &limit);
12882 out1fmt("%-20s ", l->name);
12883 printlim(how, &limit, l);
12884 }
12885 return 0;
12886 }
12887
12888 getrlimit(l->cmd, &limit);
12889 if (set) {
12890 if (how & HARD)
12891 limit.rlim_max = val;
12892 if (how & SOFT)
12893 limit.rlim_cur = val;
12894 if (setrlimit(l->cmd, &limit) < 0)
12895 error("error setting limit (%m)");
12896 } else {
12897 printlim(how, &limit, l);
12898 }
12899 return 0;
12900}
12901
12902
12903#ifdef CONFIG_ASH_MATH_SUPPORT
12904
12905/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12906
12907 Permission is hereby granted, free of charge, to any person obtaining
12908 a copy of this software and associated documentation files (the
12909 "Software"), to deal in the Software without restriction, including
12910 without limitation the rights to use, copy, modify, merge, publish,
12911 distribute, sublicense, and/or sell copies of the Software, and to
12912 permit persons to whom the Software is furnished to do so, subject to
12913 the following conditions:
12914
12915 The above copyright notice and this permission notice shall be
12916 included in all copies or substantial portions of the Software.
12917
12918 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12919 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12920 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12921 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12922 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12923 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12924 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12925*/
12926
12927/* This is my infix parser/evaluator. It is optimized for size, intended
12928 * as a replacement for yacc-based parsers. However, it may well be faster
12929 * than a comparable parser written in yacc. The supported operators are
12930 * listed in #defines below. Parens, order of operations, and error handling
12931 * are supported. This code is thread safe. The exact expression format should
12932 * be that which POSIX specifies for shells. */
12933
12934/* The code uses a simple two-stack algorithm. See
12935 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
12936 * for a detailed explanation of the infix-to-postfix algorithm on which
12937 * this is based (this code differs in that it applies operators immediately
12938 * to the stack instead of adding them to a queue to end up with an
12939 * expression). */
12940
12941/* To use the routine, call it with an expression string and error return
12942 * pointer */
12943
12944/*
12945 * Aug 24, 2001 Manuel Novoa III
12946 *
12947 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
12948 *
12949 * 1) In arith_apply():
12950 * a) Cached values of *numptr and &(numptr[-1]).
12951 * b) Removed redundant test for zero denominator.
12952 *
12953 * 2) In arith():
12954 * a) Eliminated redundant code for processing operator tokens by moving
12955 * to a table-based implementation. Also folded handling of parens
12956 * into the table.
12957 * b) Combined all 3 loops which called arith_apply to reduce generated
12958 * code size at the cost of speed.
12959 *
12960 * 3) The following expressions were treated as valid by the original code:
12961 * 1() , 0! , 1 ( *3 ) .
12962 * These bugs have been fixed by internally enclosing the expression in
12963 * parens and then checking that all binary ops and right parens are
12964 * preceded by a valid expression (NUM_TOKEN).
12965 *
12966 * Note: It may be desirable to replace Aaron's test for whitespace with
12967 * ctype's isspace() if it is used by another busybox applet or if additional
12968 * whitespace chars should be considered. Look below the "#include"s for a
12969 * precompiler test.
12970 */
12971
12972/*
12973 * Aug 26, 2001 Manuel Novoa III
12974 *
12975 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
12976 *
12977 * Merge in Aaron's comments previously posted to the busybox list,
12978 * modified slightly to take account of my changes to the code.
12979 *
12980 */
12981
12982/*
12983 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
12984 *
12985 * - allow access to variable,
12986 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
12987 * - realize assign syntax (VAR=expr, +=, *= etc)
12988 * - realize exponentiation (** operator)
12989 * - realize comma separated - expr, expr
12990 * - realise ++expr --expr expr++ expr--
12991 * - realise expr ? expr : expr (but, second expr calculate always)
12992 * - allow hexadecimal and octal numbers
12993 * - was restored loses XOR operator
12994 * - remove one goto label, added three ;-)
12995 * - protect $((num num)) as true zero expr (Manuel`s error)
12996 * - always use special isspace(), see comment from bash ;-)
12997 */
12998
12999
13000#define arith_isspace(arithval) \
13001 (arithval == ' ' || arithval == '\n' || arithval == '\t')
13002
13003
13004typedef unsigned char operator;
13005
13006/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
13007 * precedence, and 3 high bits are an ID unique across operators of that
13008 * precedence. The ID portion is so that multiple operators can have the
13009 * same precedence, ensuring that the leftmost one is evaluated first.
13010 * Consider * and /. */
13011
13012#define tok_decl(prec,id) (((id)<<5)|(prec))
13013#define PREC(op) ((op) & 0x1F)
13014
13015#define TOK_LPAREN tok_decl(0,0)
13016
13017#define TOK_COMMA tok_decl(1,0)
13018
13019#define TOK_ASSIGN tok_decl(2,0)
13020#define TOK_AND_ASSIGN tok_decl(2,1)
13021#define TOK_OR_ASSIGN tok_decl(2,2)
13022#define TOK_XOR_ASSIGN tok_decl(2,3)
13023#define TOK_PLUS_ASSIGN tok_decl(2,4)
13024#define TOK_MINUS_ASSIGN tok_decl(2,5)
13025#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
13026#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
13027
13028#define TOK_MUL_ASSIGN tok_decl(3,0)
13029#define TOK_DIV_ASSIGN tok_decl(3,1)
13030#define TOK_REM_ASSIGN tok_decl(3,2)
13031
13032/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
13033#define convert_prec_is_assing(prec) do { if(prec == 3) prec = 2; } while(0)
13034
13035/* conditional is right associativity too */
13036#define TOK_CONDITIONAL tok_decl(4,0)
13037#define TOK_CONDITIONAL_SEP tok_decl(4,1)
13038
13039#define TOK_OR tok_decl(5,0)
13040
13041#define TOK_AND tok_decl(6,0)
13042
13043#define TOK_BOR tok_decl(7,0)
13044
13045#define TOK_BXOR tok_decl(8,0)
13046
13047#define TOK_BAND tok_decl(9,0)
13048
13049#define TOK_EQ tok_decl(10,0)
13050#define TOK_NE tok_decl(10,1)
13051
13052#define TOK_LT tok_decl(11,0)
13053#define TOK_GT tok_decl(11,1)
13054#define TOK_GE tok_decl(11,2)
13055#define TOK_LE tok_decl(11,3)
13056
13057#define TOK_LSHIFT tok_decl(12,0)
13058#define TOK_RSHIFT tok_decl(12,1)
13059
13060#define TOK_ADD tok_decl(13,0)
13061#define TOK_SUB tok_decl(13,1)
13062
13063#define TOK_MUL tok_decl(14,0)
13064#define TOK_DIV tok_decl(14,1)
13065#define TOK_REM tok_decl(14,2)
13066
13067/* exponent is right associativity */
13068#define TOK_EXPONENT tok_decl(15,1)
13069
13070/* For now unary operators. */
13071#define UNARYPREC 16
13072#define TOK_BNOT tok_decl(UNARYPREC,0)
13073#define TOK_NOT tok_decl(UNARYPREC,1)
13074
13075#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
13076#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
13077
13078#define PREC_PRE (UNARYPREC+2)
13079
13080#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
13081#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
13082
13083#define PREC_POST (UNARYPREC+3)
13084
13085#define TOK_POST_INC tok_decl(PREC_POST, 0)
13086#define TOK_POST_DEC tok_decl(PREC_POST, 1)
13087
13088#define SPEC_PREC (UNARYPREC+4)
13089
13090#define TOK_NUM tok_decl(SPEC_PREC, 0)
13091#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
13092
13093#define NUMPTR (*numstackptr)
13094
13095static inline int tok_have_assign(operator op)
13096{
13097 operator prec = PREC(op);
13098
13099 convert_prec_is_assing(prec);
13100 return (prec == PREC(TOK_ASSIGN) ||
13101 prec == PREC_PRE || prec == PREC_POST);
13102}
13103
13104static inline int is_right_associativity(operator prec)
13105{
13106 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) ||
13107 prec == PREC(TOK_CONDITIONAL));
13108}
13109
13110
13111typedef struct ARITCH_VAR_NUM {
13112 arith_t val;
13113 arith_t contidional_second_val;
13114 char contidional_second_val_initialized;
13115 char *var; /* if NULL then is regular number,
13116 else is variable name */
13117} v_n_t;
13118
13119
13120typedef struct CHK_VAR_RECURSIVE_LOOPED {
13121 const char *var;
13122 struct CHK_VAR_RECURSIVE_LOOPED *next;
13123} chk_var_recursive_looped_t;
13124
13125static chk_var_recursive_looped_t *prev_chk_var_recursive;
13126
13127
13128static int arith_lookup_val(v_n_t *t)
13129{
13130 if(t->var) {
13131 const char * p = lookupvar(t->var);
13132
13133 if(p) {
13134 int errcode;
13135
13136 /* recursive try as expression */
13137 chk_var_recursive_looped_t *cur;
13138 chk_var_recursive_looped_t cur_save;
13139
13140 for(cur = prev_chk_var_recursive; cur; cur = cur->next) {
13141 if(strcmp(cur->var, t->var) == 0) {
13142 /* expression recursion loop detected */
13143 return -5;
13144 }
13145 }
13146 /* save current lookuped var name */
13147 cur = prev_chk_var_recursive;
13148 cur_save.var = t->var;
13149 cur_save.next = cur;
13150 prev_chk_var_recursive = &cur_save;
13151
13152 t->val = arith (p, &errcode);
13153 /* restore previous ptr after recursiving */
13154 prev_chk_var_recursive = cur;
13155 return errcode;
13156 } else {
13157 /* allow undefined var as 0 */
13158 t->val = 0;
13159 }
13160 }
13161 return 0;
13162}
13163
13164/* "applying" a token means performing it on the top elements on the integer
13165 * stack. For a unary operator it will only change the top element, but a
13166 * binary operator will pop two arguments and push a result */
13167static inline int
13168arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
13169{
13170 v_n_t *numptr_m1;
13171 arith_t numptr_val, rez;
13172 int ret_arith_lookup_val;
13173
13174 if (NUMPTR == numstack) goto err; /* There is no operator that can work
13175 without arguments */
13176 numptr_m1 = NUMPTR - 1;
13177
13178 /* check operand is var with noninteger value */
13179 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13180 if(ret_arith_lookup_val)
13181 return ret_arith_lookup_val;
13182
13183 rez = numptr_m1->val;
13184 if (op == TOK_UMINUS)
13185 rez *= -1;
13186 else if (op == TOK_NOT)
13187 rez = !rez;
13188 else if (op == TOK_BNOT)
13189 rez = ~rez;
13190 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13191 rez++;
13192 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13193 rez--;
13194 else if (op != TOK_UPLUS) {
13195 /* Binary operators */
13196
13197 /* check and binary operators need two arguments */
13198 if (numptr_m1 == numstack) goto err;
13199
13200 /* ... and they pop one */
13201 --NUMPTR;
13202 numptr_val = rez;
13203 if (op == TOK_CONDITIONAL) {
13204 if(! numptr_m1->contidional_second_val_initialized) {
13205 /* protect $((expr1 ? expr2)) without ": expr" */
13206 goto err;
13207 }
13208 rez = numptr_m1->contidional_second_val;
13209 } else if(numptr_m1->contidional_second_val_initialized) {
13210 /* protect $((expr1 : expr2)) without "expr ? " */
13211 goto err;
13212 }
13213 numptr_m1 = NUMPTR - 1;
13214 if(op != TOK_ASSIGN) {
13215 /* check operand is var with noninteger value for not '=' */
13216 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13217 if(ret_arith_lookup_val)
13218 return ret_arith_lookup_val;
13219 }
13220 if (op == TOK_CONDITIONAL) {
13221 numptr_m1->contidional_second_val = rez;
13222 }
13223 rez = numptr_m1->val;
13224 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
13225 rez |= numptr_val;
13226 else if (op == TOK_OR)
13227 rez = numptr_val || rez;
13228 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
13229 rez &= numptr_val;
13230 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
13231 rez ^= numptr_val;
13232 else if (op == TOK_AND)
13233 rez = rez && numptr_val;
13234 else if (op == TOK_EQ)
13235 rez = (rez == numptr_val);
13236 else if (op == TOK_NE)
13237 rez = (rez != numptr_val);
13238 else if (op == TOK_GE)
13239 rez = (rez >= numptr_val);
13240 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
13241 rez >>= numptr_val;
13242 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
13243 rez <<= numptr_val;
13244 else if (op == TOK_GT)
13245 rez = (rez > numptr_val);
13246 else if (op == TOK_LT)
13247 rez = (rez < numptr_val);
13248 else if (op == TOK_LE)
13249 rez = (rez <= numptr_val);
13250 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
13251 rez *= numptr_val;
13252 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
13253 rez += numptr_val;
13254 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
13255 rez -= numptr_val;
13256 else if (op == TOK_ASSIGN || op == TOK_COMMA)
13257 rez = numptr_val;
13258 else if (op == TOK_CONDITIONAL_SEP) {
13259 if (numptr_m1 == numstack) {
13260 /* protect $((expr : expr)) without "expr ? " */
13261 goto err;
13262 }
13263 numptr_m1->contidional_second_val_initialized = op;
13264 numptr_m1->contidional_second_val = numptr_val;
13265 }
13266 else if (op == TOK_CONDITIONAL) {
13267 rez = rez ?
13268 numptr_val : numptr_m1->contidional_second_val;
13269 }
13270 else if(op == TOK_EXPONENT) {
13271 if(numptr_val < 0)
13272 return -3; /* exponent less than 0 */
13273 else {
13274 arith_t c = 1;
13275
13276 if(numptr_val)
13277 while(numptr_val--)
13278 c *= rez;
13279 rez = c;
13280 }
13281 }
13282 else if(numptr_val==0) /* zero divisor check */
13283 return -2;
13284 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
13285 rez /= numptr_val;
13286 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
13287 rez %= numptr_val;
13288 }
13289 if(tok_have_assign(op)) {
13290 char buf[32];
13291
13292 if(numptr_m1->var == NULL) {
13293 /* Hmm, 1=2 ? */
13294 goto err;
13295 }
13296 /* save to shell variable */
13297 snprintf(buf, sizeof(buf), "%lld", (long long) rez);
13298 setvar(numptr_m1->var, buf, 0);
13299 /* after saving, make previous value for v++ or v-- */
13300 if(op == TOK_POST_INC)
13301 rez--;
13302 else if(op == TOK_POST_DEC)
13303 rez++;
13304 }
13305 numptr_m1->val = rez;
13306 /* protect geting var value, is number now */
13307 numptr_m1->var = NULL;
13308 return 0;
13309err: return(-1);
13310}
13311
13312/* longest must first */
13313static const char op_tokens[] = {
13314 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13315 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13316 '<','<', 0, TOK_LSHIFT,
13317 '>','>', 0, TOK_RSHIFT,
13318 '|','|', 0, TOK_OR,
13319 '&','&', 0, TOK_AND,
13320 '!','=', 0, TOK_NE,
13321 '<','=', 0, TOK_LE,
13322 '>','=', 0, TOK_GE,
13323 '=','=', 0, TOK_EQ,
13324 '|','=', 0, TOK_OR_ASSIGN,
13325 '&','=', 0, TOK_AND_ASSIGN,
13326 '*','=', 0, TOK_MUL_ASSIGN,
13327 '/','=', 0, TOK_DIV_ASSIGN,
13328 '%','=', 0, TOK_REM_ASSIGN,
13329 '+','=', 0, TOK_PLUS_ASSIGN,
13330 '-','=', 0, TOK_MINUS_ASSIGN,
13331 '-','-', 0, TOK_POST_DEC,
13332 '^','=', 0, TOK_XOR_ASSIGN,
13333 '+','+', 0, TOK_POST_INC,
13334 '*','*', 0, TOK_EXPONENT,
13335 '!', 0, TOK_NOT,
13336 '<', 0, TOK_LT,
13337 '>', 0, TOK_GT,
13338 '=', 0, TOK_ASSIGN,
13339 '|', 0, TOK_BOR,
13340 '&', 0, TOK_BAND,
13341 '*', 0, TOK_MUL,
13342 '/', 0, TOK_DIV,
13343 '%', 0, TOK_REM,
13344 '+', 0, TOK_ADD,
13345 '-', 0, TOK_SUB,
13346 '^', 0, TOK_BXOR,
13347 /* uniq */
13348 '~', 0, TOK_BNOT,
13349 ',', 0, TOK_COMMA,
13350 '?', 0, TOK_CONDITIONAL,
13351 ':', 0, TOK_CONDITIONAL_SEP,
13352 ')', 0, TOK_RPAREN,
13353 '(', 0, TOK_LPAREN,
13354 0
13355};
13356/* ptr to ")" */
13357#define endexpression &op_tokens[sizeof(op_tokens)-7]
13358
13359
13360static arith_t arith (const char *expr, int *perrcode)
13361{
13362 register char arithval; /* Current character under analysis */
13363 operator lasttok, op;
13364 operator prec;
13365
13366 const char *p = endexpression;
13367 int errcode;
13368
13369 size_t datasizes = strlen(expr) + 2;
13370
13371 /* Stack of integers */
13372 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13373 * in any given correct or incorrect expression is left as an exercise to
13374 * the reader. */
13375 v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
13376 *numstackptr = numstack;
13377 /* Stack of operator tokens */
13378 operator *stack = alloca((datasizes) * sizeof(operator)),
13379 *stackptr = stack;
13380
13381 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13382 *perrcode = errcode = 0;
13383
13384 while(1) {
13385 if ((arithval = *expr) == 0) {
13386 if (p == endexpression) {
13387 /* Null expression. */
13388 return 0;
13389 }
13390
13391 /* This is only reached after all tokens have been extracted from the
13392 * input stream. If there are still tokens on the operator stack, they
13393 * are to be applied in order. At the end, there should be a final
13394 * result on the integer stack */
13395
13396 if (expr != endexpression + 1) {
13397 /* If we haven't done so already, */
13398 /* append a closing right paren */
13399 expr = endexpression;
13400 /* and let the loop process it. */
13401 continue;
13402 }
13403 /* At this point, we're done with the expression. */
13404 if (numstackptr != numstack+1) {
13405 /* ... but if there isn't, it's bad */
13406 err:
13407 return (*perrcode = -1);
13408 }
13409 if(numstack->var) {
13410 /* expression is $((var)) only, lookup now */
13411 errcode = arith_lookup_val(numstack);
13412 }
13413 ret:
13414 *perrcode = errcode;
13415 return numstack->val;
13416 } else {
13417 /* Continue processing the expression. */
13418 if (arith_isspace(arithval)) {
13419 /* Skip whitespace */
13420 goto prologue;
13421 }
13422 if((p = endofname(expr)) != expr) {
13423 size_t var_name_size = (p-expr) + 1; /* trailing zero */
13424
13425 numstackptr->var = alloca(var_name_size);
13426 safe_strncpy(numstackptr->var, expr, var_name_size);
13427 expr = p;
13428 num:
13429 numstackptr->contidional_second_val_initialized = 0;
13430 numstackptr++;
13431 lasttok = TOK_NUM;
13432 continue;
13433 } else if (is_digit(arithval)) {
13434 numstackptr->var = NULL;
13435 numstackptr->val = strtoll(expr, (char **) &expr, 0);
13436 goto num;
13437 }
13438 for(p = op_tokens; ; p++) {
13439 const char *o;
13440
13441 if(*p == 0) {
13442 /* strange operator not found */
13443 goto err;
13444 }
13445 for(o = expr; *p && *o == *p; p++)
13446 o++;
13447 if(! *p) {
13448 /* found */
13449 expr = o - 1;
13450 break;
13451 }
13452 /* skip tail uncompared token */
13453 while(*p)
13454 p++;
13455 /* skip zero delim */
13456 p++;
13457 }
13458 op = p[1];
13459
13460 /* post grammar: a++ reduce to num */
13461 if(lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13462 lasttok = TOK_NUM;
13463
13464 /* Plus and minus are binary (not unary) _only_ if the last
13465 * token was as number, or a right paren (which pretends to be
13466 * a number, since it evaluates to one). Think about it.
13467 * It makes sense. */
13468 if (lasttok != TOK_NUM) {
13469 switch(op) {
13470 case TOK_ADD:
13471 op = TOK_UPLUS;
13472 break;
13473 case TOK_SUB:
13474 op = TOK_UMINUS;
13475 break;
13476 case TOK_POST_INC:
13477 op = TOK_PRE_INC;
13478 break;
13479 case TOK_POST_DEC:
13480 op = TOK_PRE_DEC;
13481 break;
13482 }
13483 }
13484 /* We don't want a unary operator to cause recursive descent on the
13485 * stack, because there can be many in a row and it could cause an
13486 * operator to be evaluated before its argument is pushed onto the
13487 * integer stack. */
13488 /* But for binary operators, "apply" everything on the operator
13489 * stack until we find an operator with a lesser priority than the
13490 * one we have just extracted. */
13491 /* Left paren is given the lowest priority so it will never be
13492 * "applied" in this way.
13493 * if associativity is right and priority eq, applied also skip
13494 */
13495 prec = PREC(op);
13496 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13497 /* not left paren or unary */
13498 if (lasttok != TOK_NUM) {
13499 /* binary op must be preceded by a num */
13500 goto err;
13501 }
13502 while (stackptr != stack) {
13503 if (op == TOK_RPAREN) {
13504 /* The algorithm employed here is simple: while we don't
13505 * hit an open paren nor the bottom of the stack, pop
13506 * tokens and apply them */
13507 if (stackptr[-1] == TOK_LPAREN) {
13508 --stackptr;
13509 /* Any operator directly after a */
13510 lasttok = TOK_NUM;
13511 /* close paren should consider itself binary */
13512 goto prologue;
13513 }
13514 } else {
13515 operator prev_prec = PREC(stackptr[-1]);
13516
13517 convert_prec_is_assing(prec);
13518 convert_prec_is_assing(prev_prec);
13519 if (prev_prec < prec)
13520 break;
13521 /* check right assoc */
13522 if(prev_prec == prec && is_right_associativity(prec))
13523 break;
13524 }
13525 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13526 if(errcode) goto ret;
13527 }
13528 if (op == TOK_RPAREN) {
13529 goto err;
13530 }
13531 }
13532
13533 /* Push this operator to the stack and remember it. */
13534 *stackptr++ = lasttok = op;
13535
13536 prologue:
13537 ++expr;
13538 }
13539 }
13540}
13541#endif /* CONFIG_ASH_MATH_SUPPORT */
13542
13543
13544#ifdef DEBUG
13545const char *bb_applet_name = "debug stuff usage";
13546int main(int argc, char **argv)
13547{
13548 return ash_main(argc, argv);
13549}
13550#endif
13551
13552/*-
13553 * Copyright (c) 1989, 1991, 1993, 1994
13554 * The Regents of the University of California. All rights reserved.
13555 *
13556 * This code is derived from software contributed to Berkeley by
13557 * Kenneth Almquist.
13558 *
13559 * Redistribution and use in source and binary forms, with or without
13560 * modification, are permitted provided that the following conditions
13561 * are met:
13562 * 1. Redistributions of source code must retain the above copyright
13563 * notice, this list of conditions and the following disclaimer.
13564 * 2. Redistributions in binary form must reproduce the above copyright
13565 * notice, this list of conditions and the following disclaimer in the
13566 * documentation and/or other materials provided with the distribution.
13567 *
13568 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
13569 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
13570 *
13571 * 4. Neither the name of the University nor the names of its contributors
13572 * may be used to endorse or promote products derived from this software
13573 * without specific prior written permission.
13574 *
13575 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13576 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13577 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13578 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13579 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13580 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13581 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13582 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13583 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13584 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13585 * SUCH DAMAGE.
13586 */
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Termios command line History and Editing.
4 *
5 * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license.
6 * Written by: Vladimir Oleynik <dzo@simtreas.ru>
7 *
8 * Used ideas:
9 * Adam Rogoyski <rogoyski@cs.utexas.edu>
10 * Dave Cinege <dcinege@psychosis.com>
11 * Jakub Jelinek (c) 1995
12 * Erik Andersen <andersen@codepoet.org> (Majorly adjusted for busybox)
13 *
14 * This code is 'as is' with no warranty.
15 *
16 *
17 */
18
19/*
20 Usage and Known bugs:
21 Terminal key codes are not extensive, and more will probably
22 need to be added. This version was created on Debian GNU/Linux 2.x.
23 Delete, Backspace, Home, End, and the arrow keys were tested
24 to work in an Xterm and console. Ctrl-A also works as Home.
25 Ctrl-E also works as End.
26
27 Small bugs (simple effect):
28 - not true viewing if terminal size (x*y symbols) less
29 size (prompt + editor`s line + 2 symbols)
30 - not true viewing if length prompt less terminal width
31 */
32
33
34#include <stdio.h>
35#include <errno.h>
36#include <unistd.h>
37#include <stdlib.h>
38#include <string.h>
39#include <sys/ioctl.h>
40#include <ctype.h>
41#include <signal.h>
42#include <limits.h>
43
44#include "busybox.h"
45
46#include "../shell/cmdedit.h"
47
48
49#ifdef CONFIG_LOCALE_SUPPORT
50#define Isprint(c) isprint((c))
51#else
52#define Isprint(c) ( (c) >= ' ' && (c) != ((unsigned char)'\233') )
53#endif
54
55#ifdef TEST
56
57/* pretect redefined for test */
58#undef CONFIG_FEATURE_COMMAND_EDITING
59#undef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
60#undef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
61#undef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
62#undef CONFIG_FEATURE_CLEAN_UP
63
64#define CONFIG_FEATURE_COMMAND_EDITING
65#define CONFIG_FEATURE_COMMAND_TAB_COMPLETION
66#define CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
67#define CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
68#define CONFIG_FEATURE_CLEAN_UP
69
70#endif /* TEST */
71
72#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
73#include <dirent.h>
74#include <sys/stat.h>
75#endif
76
77#ifdef CONFIG_FEATURE_COMMAND_EDITING
78
79#if defined(CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION) || defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
80#define CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
81#endif
82
83#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
84#include "pwd_.h"
85#endif /* advanced FEATURES */
86
87
88/* Maximum length of the linked list for the command line history */
89#ifndef CONFIG_FEATURE_COMMAND_HISTORY
90#define MAX_HISTORY 15
91#else
92#define MAX_HISTORY CONFIG_FEATURE_COMMAND_HISTORY
93#endif
94
95#if MAX_HISTORY < 1
96#warning cmdedit: You set MAX_HISTORY < 1. The history algorithm switched off.
97#else
98static char *history[MAX_HISTORY+1]; /* history + current */
99/* saved history lines */
100static int n_history;
101/* current pointer to history line */
102static int cur_history;
103#endif
104
105#include <termios.h>
106#define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
107#define getTermSettings(fd,argp) tcgetattr(fd, argp);
108
109/* Current termio and the previous termio before starting sh */
110static struct termios initial_settings, new_settings;
111
112
113static
114volatile int cmdedit_termw = 80; /* actual terminal width */
115static
116volatile int handlers_sets = 0; /* Set next bites: */
117
118enum {
119 SET_ATEXIT = 1, /* when atexit() has been called
120 and get euid,uid,gid to fast compare */
121 SET_WCHG_HANDLERS = 2, /* winchg signal handler */
122 SET_RESET_TERM = 4, /* if the terminal needs to be reset upon exit */
123};
124
125
126static int cmdedit_x; /* real x terminal position */
127static int cmdedit_y; /* pseudoreal y terminal position */
128static int cmdedit_prmt_len; /* lenght prompt without colores string */
129
130static int cursor; /* required global for signal handler */
131static int len; /* --- "" - - "" - -"- --""-- --""--- */
132static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */
133static
134#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
135 const
136#endif
137char *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */
138
139#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
140static char *user_buf = "";
141static char *home_pwd_buf = "";
142static int my_euid;
143#endif
144
145#ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
146static char *hostname_buf;
147static int num_ok_lines = 1;
148#endif
149
150
151#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
152
153#ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
154static int my_euid;
155#endif
156
157static int my_uid;
158static int my_gid;
159
160#endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
161
162static void cmdedit_setwidth(int w, int redraw_flg);
163
164static void win_changed(int nsig)
165{
166 static sighandler_t previous_SIGWINCH_handler; /* for reset */
167
168 /* emulate || signal call */
169 if (nsig == -SIGWINCH || nsig == SIGWINCH) {
170 int width = 0;
171 get_terminal_width_height(0, &width, NULL);
172 cmdedit_setwidth(width, nsig == SIGWINCH);
173 }
174 /* Unix not all standart in recall signal */
175
176 if (nsig == -SIGWINCH) /* save previous handler */
177 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
178 else if (nsig == SIGWINCH) /* signaled called handler */
179 signal(SIGWINCH, win_changed); /* set for next call */
180 else /* nsig == 0 */
181 /* set previous handler */
182 signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */
183}
184
185static void cmdedit_reset_term(void)
186{
187 if ((handlers_sets & SET_RESET_TERM) != 0) {
188/* sparc and other have broken termios support: use old termio handling. */
189 setTermSettings(STDIN_FILENO, (void *) &initial_settings);
190 handlers_sets &= ~SET_RESET_TERM;
191 }
192 if ((handlers_sets & SET_WCHG_HANDLERS) != 0) {
193 /* reset SIGWINCH handler to previous (default) */
194 win_changed(0);
195 handlers_sets &= ~SET_WCHG_HANDLERS;
196 }
197 fflush(stdout);
198}
199
200
201/* special for recount position for scroll and remove terminal margin effect */
202static void cmdedit_set_out_char(int next_char)
203{
204
205 int c = (int)((unsigned char) command_ps[cursor]);
206
207 if (c == 0)
208 c = ' '; /* destroy end char? */
209#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
210 if (!Isprint(c)) { /* Inverse put non-printable characters */
211 if (c >= 128)
212 c -= 128;
213 if (c < ' ')
214 c += '@';
215 if (c == 127)
216 c = '?';
217 printf("\033[7m%c\033[0m", c);
218 } else
219#endif
220 putchar(c);
221 if (++cmdedit_x >= cmdedit_termw) {
222 /* terminal is scrolled down */
223 cmdedit_y++;
224 cmdedit_x = 0;
225
226 if (!next_char)
227 next_char = ' ';
228 /* destroy "(auto)margin" */
229 putchar(next_char);
230 putchar('\b');
231 }
232 cursor++;
233}
234
235/* Move to end line. Bonus: rewrite line from cursor */
236static void input_end(void)
237{
238 while (cursor < len)
239 cmdedit_set_out_char(0);
240}
241
242/* Go to the next line */
243static void goto_new_line(void)
244{
245 input_end();
246 if (cmdedit_x)
247 putchar('\n');
248}
249
250
251static inline void out1str(const char *s)
252{
253 if ( s )
254 fputs(s, stdout);
255}
256
257static inline void beep(void)
258{
259 putchar('\007');
260}
261
262/* Move back one character */
263/* special for slow terminal */
264static void input_backward(int num)
265{
266 if (num > cursor)
267 num = cursor;
268 cursor -= num; /* new cursor (in command, not terminal) */
269
270 if (cmdedit_x >= num) { /* no to up line */
271 cmdedit_x -= num;
272 if (num < 4)
273 while (num-- > 0)
274 putchar('\b');
275
276 else
277 printf("\033[%dD", num);
278 } else {
279 int count_y;
280
281 if (cmdedit_x) {
282 putchar('\r'); /* back to first terminal pos. */
283 num -= cmdedit_x; /* set previous backward */
284 }
285 count_y = 1 + num / cmdedit_termw;
286 printf("\033[%dA", count_y);
287 cmdedit_y -= count_y;
288 /* require forward after uping */
289 cmdedit_x = cmdedit_termw * count_y - num;
290 printf("\033[%dC", cmdedit_x); /* set term cursor */
291 }
292}
293
294static void put_prompt(void)
295{
296 out1str(cmdedit_prompt);
297 cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
298 cursor = 0;
299 cmdedit_y = 0; /* new quasireal y */
300}
301
302#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
303static void parse_prompt(const char *prmt_ptr)
304{
305 cmdedit_prompt = prmt_ptr;
306 cmdedit_prmt_len = strlen(prmt_ptr);
307 put_prompt();
308}
309#else
310static void parse_prompt(const char *prmt_ptr)
311{
312 int prmt_len = 0;
313 int sub_len = 0;
314 char flg_not_length = '[';
315 char *prmt_mem_ptr = xcalloc(1, 1);
316 char *pwd_buf = xgetcwd(0);
317 char buf2[PATH_MAX + 1];
318 char buf[2];
319 char c;
320 char *pbuf;
321
322 if (!pwd_buf) {
323 pwd_buf=(char *)bb_msg_unknown;
324 }
325
326 while (*prmt_ptr) {
327 pbuf = buf;
328 pbuf[1] = 0;
329 c = *prmt_ptr++;
330 if (c == '\\') {
331 const char *cp = prmt_ptr;
332 int l;
333
334 c = bb_process_escape_sequence(&prmt_ptr);
335 if(prmt_ptr==cp) {
336 if (*cp == 0)
337 break;
338 c = *prmt_ptr++;
339 switch (c) {
340#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
341 case 'u':
342 pbuf = user_buf;
343 break;
344#endif
345 case 'h':
346 pbuf = hostname_buf;
347 if (pbuf == 0) {
348 pbuf = xcalloc(256, 1);
349 if (gethostname(pbuf, 255) < 0) {
350 strcpy(pbuf, "?");
351 } else {
352 char *s = strchr(pbuf, '.');
353
354 if (s)
355 *s = 0;
356 }
357 hostname_buf = pbuf;
358 }
359 break;
360 case '$':
361 c = my_euid == 0 ? '#' : '$';
362 break;
363#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
364 case 'w':
365 pbuf = pwd_buf;
366 l = strlen(home_pwd_buf);
367 if (home_pwd_buf[0] != 0 &&
368 strncmp(home_pwd_buf, pbuf, l) == 0 &&
369 (pbuf[l]=='/' || pbuf[l]=='\0') &&
370 strlen(pwd_buf+l)<PATH_MAX) {
371 pbuf = buf2;
372 *pbuf = '~';
373 strcpy(pbuf+1, pwd_buf+l);
374 }
375 break;
376#endif
377 case 'W':
378 pbuf = pwd_buf;
379 cp = strrchr(pbuf,'/');
380 if ( (cp != NULL) && (cp != pbuf) )
381 pbuf += (cp-pbuf)+1;
382 break;
383 case '!':
384 snprintf(pbuf = buf2, sizeof(buf2), "%d", num_ok_lines);
385 break;
386 case 'e': case 'E': /* \e \E = \033 */
387 c = '\033';
388 break;
389 case 'x': case 'X':
390 for (l = 0; l < 3;) {
391 int h;
392 buf2[l++] = *prmt_ptr;
393 buf2[l] = 0;
394 h = strtol(buf2, &pbuf, 16);
395 if (h > UCHAR_MAX || (pbuf - buf2) < l) {
396 l--;
397 break;
398 }
399 prmt_ptr++;
400 }
401 buf2[l] = 0;
402 c = (char)strtol(buf2, 0, 16);
403 if(c==0)
404 c = '?';
405 pbuf = buf;
406 break;
407 case '[': case ']':
408 if (c == flg_not_length) {
409 flg_not_length = flg_not_length == '[' ? ']' : '[';
410 continue;
411 }
412 break;
413 }
414 }
415 }
416 if(pbuf == buf)
417 *pbuf = c;
418 prmt_len += strlen(pbuf);
419 prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
420 if (flg_not_length == ']')
421 sub_len++;
422 }
423 if(pwd_buf!=(char *)bb_msg_unknown)
424 free(pwd_buf);
425 cmdedit_prompt = prmt_mem_ptr;
426 cmdedit_prmt_len = prmt_len - sub_len;
427 put_prompt();
428}
429#endif
430
431
432/* draw prompt, editor line, and clear tail */
433static void redraw(int y, int back_cursor)
434{
435 if (y > 0) /* up to start y */
436 printf("\033[%dA", y);
437 putchar('\r');
438 put_prompt();
439 input_end(); /* rewrite */
440 printf("\033[J"); /* destroy tail after cursor */
441 input_backward(back_cursor);
442}
443
444/* Delete the char in front of the cursor */
445static void input_delete(void)
446{
447 int j = cursor;
448
449 if (j == len)
450 return;
451
452 strcpy(command_ps + j, command_ps + j + 1);
453 len--;
454 input_end(); /* rewtite new line */
455 cmdedit_set_out_char(0); /* destroy end char */
456 input_backward(cursor - j); /* back to old pos cursor */
457}
458
459/* Delete the char in back of the cursor */
460static void input_backspace(void)
461{
462 if (cursor > 0) {
463 input_backward(1);
464 input_delete();
465 }
466}
467
468
469/* Move forward one character */
470static void input_forward(void)
471{
472 if (cursor < len)
473 cmdedit_set_out_char(command_ps[cursor + 1]);
474}
475
476
477static void cmdedit_setwidth(int w, int redraw_flg)
478{
479 cmdedit_termw = cmdedit_prmt_len + 2;
480 if (w <= cmdedit_termw) {
481 cmdedit_termw = cmdedit_termw % w;
482 }
483 if (w > cmdedit_termw) {
484 cmdedit_termw = w;
485
486 if (redraw_flg) {
487 /* new y for current cursor */
488 int new_y = (cursor + cmdedit_prmt_len) / w;
489
490 /* redraw */
491 redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
492 fflush(stdout);
493 }
494 }
495}
496
497static void cmdedit_init(void)
498{
499 cmdedit_prmt_len = 0;
500 if ((handlers_sets & SET_WCHG_HANDLERS) == 0) {
501 /* emulate usage handler to set handler and call yours work */
502 win_changed(-SIGWINCH);
503 handlers_sets |= SET_WCHG_HANDLERS;
504 }
505
506 if ((handlers_sets & SET_ATEXIT) == 0) {
507#ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
508 struct passwd *entry;
509
510 my_euid = geteuid();
511 entry = getpwuid(my_euid);
512 if (entry) {
513 user_buf = bb_xstrdup(entry->pw_name);
514 home_pwd_buf = bb_xstrdup(entry->pw_dir);
515 }
516#endif
517
518#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
519
520#ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
521 my_euid = geteuid();
522#endif
523 my_uid = getuid();
524 my_gid = getgid();
525#endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
526 handlers_sets |= SET_ATEXIT;
527 atexit(cmdedit_reset_term); /* be sure to do this only once */
528 }
529}
530
531#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
532
533static int is_execute(const struct stat *st)
534{
535 if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) ||
536 (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) ||
537 (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) ||
538 (st->st_mode & S_IXOTH)) return TRUE;
539 return FALSE;
540}
541
542#ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
543
544static char **username_tab_completion(char *ud, int *num_matches)
545{
546 struct passwd *entry;
547 int userlen;
548 char *temp;
549
550
551 ud++; /* ~user/... to user/... */
552 userlen = strlen(ud);
553
554 if (num_matches == 0) { /* "~/..." or "~user/..." */
555 char *sav_ud = ud - 1;
556 char *home = 0;
557
558 if (*ud == '/') { /* "~/..." */
559 home = home_pwd_buf;
560 } else {
561 /* "~user/..." */
562 temp = strchr(ud, '/');
563 *temp = 0; /* ~user\0 */
564 entry = getpwnam(ud);
565 *temp = '/'; /* restore ~user/... */
566 ud = temp;
567 if (entry)
568 home = entry->pw_dir;
569 }
570 if (home) {
571 if ((userlen + strlen(home) + 1) < BUFSIZ) {
572 char temp2[BUFSIZ]; /* argument size */
573
574 /* /home/user/... */
575 sprintf(temp2, "%s%s", home, ud);
576 strcpy(sav_ud, temp2);
577 }
578 }
579 return 0; /* void, result save to argument :-) */
580 } else {
581 /* "~[^/]*" */
582 char **matches = (char **) NULL;
583 int nm = 0;
584
585 setpwent();
586
587 while ((entry = getpwent()) != NULL) {
588 /* Null usernames should result in all users as possible completions. */
589 if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) {
590
591 bb_xasprintf(&temp, "~%s/", entry->pw_name);
592 matches = xrealloc(matches, (nm + 1) * sizeof(char *));
593
594 matches[nm++] = temp;
595 }
596 }
597
598 endpwent();
599 (*num_matches) = nm;
600 return (matches);
601 }
602}
603#endif /* CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION */
604
605enum {
606 FIND_EXE_ONLY = 0,
607 FIND_DIR_ONLY = 1,
608 FIND_FILE_ONLY = 2,
609};
610
611#ifdef CONFIG_ASH
612const char *cmdedit_path_lookup;
613#else
614#define cmdedit_path_lookup getenv("PATH")
615#endif
616
617static int path_parse(char ***p, int flags)
618{
619 int npth;
620 const char *tmp;
621 const char *pth;
622
623 /* if not setenv PATH variable, to search cur dir "." */
624 if (flags != FIND_EXE_ONLY || (pth = cmdedit_path_lookup) == 0 ||
625 /* PATH=<empty> or PATH=:<empty> */
626 *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) {
627 return 1;
628 }
629
630 tmp = pth;
631 npth = 0;
632
633 for (;;) {
634 npth++; /* count words is + 1 count ':' */
635 tmp = strchr(tmp, ':');
636 if (tmp) {
637 if (*++tmp == 0)
638 break; /* :<empty> */
639 } else
640 break;
641 }
642
643 *p = xmalloc(npth * sizeof(char *));
644
645 tmp = pth;
646 (*p)[0] = bb_xstrdup(tmp);
647 npth = 1; /* count words is + 1 count ':' */
648
649 for (;;) {
650 tmp = strchr(tmp, ':');
651 if (tmp) {
652 (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */
653 if (*++tmp == 0)
654 break; /* :<empty> */
655 } else
656 break;
657 (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */
658 }
659
660 return npth;
661}
662
663static char *add_quote_for_spec_chars(char *found)
664{
665 int l = 0;
666 char *s = xmalloc((strlen(found) + 1) * 2);
667
668 while (*found) {
669 if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
670 s[l++] = '\\';
671 s[l++] = *found++;
672 }
673 s[l] = 0;
674 return s;
675}
676
677static char **exe_n_cwd_tab_completion(char *command, int *num_matches,
678 int type)
679{
680
681 char **matches = 0;
682 DIR *dir;
683 struct dirent *next;
684 char dirbuf[BUFSIZ];
685 int nm = *num_matches;
686 struct stat st;
687 char *path1[1];
688 char **paths = path1;
689 int npaths;
690 int i;
691 char *found;
692 char *pfind = strrchr(command, '/');
693
694 path1[0] = ".";
695
696 if (pfind == NULL) {
697 /* no dir, if flags==EXE_ONLY - get paths, else "." */
698 npaths = path_parse(&paths, type);
699 pfind = command;
700 } else {
701 /* with dir */
702 /* save for change */
703 strcpy(dirbuf, command);
704 /* set dir only */
705 dirbuf[(pfind - command) + 1] = 0;
706#ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
707 if (dirbuf[0] == '~') /* ~/... or ~user/... */
708 username_tab_completion(dirbuf, 0);
709#endif
710 /* "strip" dirname in command */
711 pfind++;
712
713 paths[0] = dirbuf;
714 npaths = 1; /* only 1 dir */
715 }
716
717 for (i = 0; i < npaths; i++) {
718
719 dir = opendir(paths[i]);
720 if (!dir) /* Don't print an error */
721 continue;
722
723 while ((next = readdir(dir)) != NULL) {
724 char *str_found = next->d_name;
725
726 /* matched ? */
727 if (strncmp(str_found, pfind, strlen(pfind)))
728 continue;
729 /* not see .name without .match */
730 if (*str_found == '.' && *pfind == 0) {
731 if (*paths[i] == '/' && paths[i][1] == 0
732 && str_found[1] == 0) str_found = ""; /* only "/" */
733 else
734 continue;
735 }
736 found = concat_path_file(paths[i], str_found);
737 /* hmm, remover in progress? */
738 if (stat(found, &st) < 0)
739 goto cont;
740 /* find with dirs ? */
741 if (paths[i] != dirbuf)
742 strcpy(found, next->d_name); /* only name */
743 if (S_ISDIR(st.st_mode)) {
744 /* name is directory */
745 str_found = found;
746 found = concat_path_file(found, "");
747 free(str_found);
748 str_found = add_quote_for_spec_chars(found);
749 } else {
750 /* not put found file if search only dirs for cd */
751 if (type == FIND_DIR_ONLY)
752 goto cont;
753 str_found = add_quote_for_spec_chars(found);
754 if (type == FIND_FILE_ONLY ||
755 (type == FIND_EXE_ONLY && is_execute(&st)))
756 strcat(str_found, " ");
757 }
758 /* Add it to the list */
759 matches = xrealloc(matches, (nm + 1) * sizeof(char *));
760
761 matches[nm++] = str_found;
762cont:
763 free(found);
764 }
765 closedir(dir);
766 }
767 if (paths != path1) {
768 free(paths[0]); /* allocated memory only in first member */
769 free(paths);
770 }
771 *num_matches = nm;
772 return (matches);
773}
774
775static int match_compare(const void *a, const void *b)
776{
777 return strcmp(*(char **) a, *(char **) b);
778}
779
780
781
782#define QUOT (UCHAR_MAX+1)
783
784#define collapse_pos(is, in) { \
785 memcpy(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \
786 memcpy(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); }
787
788static int find_match(char *matchBuf, int *len_with_quotes)
789{
790 int i, j;
791 int command_mode;
792 int c, c2;
793 int int_buf[BUFSIZ + 1];
794 int pos_buf[BUFSIZ + 1];
795
796 /* set to integer dimension characters and own positions */
797 for (i = 0;; i++) {
798 int_buf[i] = (int) ((unsigned char) matchBuf[i]);
799 if (int_buf[i] == 0) {
800 pos_buf[i] = -1; /* indicator end line */
801 break;
802 } else
803 pos_buf[i] = i;
804 }
805
806 /* mask \+symbol and convert '\t' to ' ' */
807 for (i = j = 0; matchBuf[i]; i++, j++)
808 if (matchBuf[i] == '\\') {
809 collapse_pos(j, j + 1);
810 int_buf[j] |= QUOT;
811 i++;
812#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
813 if (matchBuf[i] == '\t') /* algorithm equivalent */
814 int_buf[j] = ' ' | QUOT;
815#endif
816 }
817#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
818 else if (matchBuf[i] == '\t')
819 int_buf[j] = ' ';
820#endif
821
822 /* mask "symbols" or 'symbols' */
823 c2 = 0;
824 for (i = 0; int_buf[i]; i++) {
825 c = int_buf[i];
826 if (c == '\'' || c == '"') {
827 if (c2 == 0)
828 c2 = c;
829 else {
830 if (c == c2)
831 c2 = 0;
832 else
833 int_buf[i] |= QUOT;
834 }
835 } else if (c2 != 0 && c != '$')
836 int_buf[i] |= QUOT;
837 }
838
839 /* skip commands with arguments if line have commands delimiters */
840 /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
841 for (i = 0; int_buf[i]; i++) {
842 c = int_buf[i];
843 c2 = int_buf[i + 1];
844 j = i ? int_buf[i - 1] : -1;
845 command_mode = 0;
846 if (c == ';' || c == '&' || c == '|') {
847 command_mode = 1 + (c == c2);
848 if (c == '&') {
849 if (j == '>' || j == '<')
850 command_mode = 0;
851 } else if (c == '|' && j == '>')
852 command_mode = 0;
853 }
854 if (command_mode) {
855 collapse_pos(0, i + command_mode);
856 i = -1; /* hack incremet */
857 }
858 }
859 /* collapse `command...` */
860 for (i = 0; int_buf[i]; i++)
861 if (int_buf[i] == '`') {
862 for (j = i + 1; int_buf[j]; j++)
863 if (int_buf[j] == '`') {
864 collapse_pos(i, j + 1);
865 j = 0;
866 break;
867 }
868 if (j) {
869 /* not found close ` - command mode, collapse all previous */
870 collapse_pos(0, i + 1);
871 break;
872 } else
873 i--; /* hack incremet */
874 }
875
876 /* collapse (command...(command...)...) or {command...{command...}...} */
877 c = 0; /* "recursive" level */
878 c2 = 0;
879 for (i = 0; int_buf[i]; i++)
880 if (int_buf[i] == '(' || int_buf[i] == '{') {
881 if (int_buf[i] == '(')
882 c++;
883 else
884 c2++;
885 collapse_pos(0, i + 1);
886 i = -1; /* hack incremet */
887 }
888 for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
889 if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
890 if (int_buf[i] == ')')
891 c--;
892 else
893 c2--;
894 collapse_pos(0, i + 1);
895 i = -1; /* hack incremet */
896 }
897
898 /* skip first not quote space */
899 for (i = 0; int_buf[i]; i++)
900 if (int_buf[i] != ' ')
901 break;
902 if (i)
903 collapse_pos(0, i);
904
905 /* set find mode for completion */
906 command_mode = FIND_EXE_ONLY;
907 for (i = 0; int_buf[i]; i++)
908 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
909 if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
910 && matchBuf[pos_buf[0]]=='c'
911 && matchBuf[pos_buf[1]]=='d' )
912 command_mode = FIND_DIR_ONLY;
913 else {
914 command_mode = FIND_FILE_ONLY;
915 break;
916 }
917 }
918 /* "strlen" */
919 for (i = 0; int_buf[i]; i++);
920 /* find last word */
921 for (--i; i >= 0; i--) {
922 c = int_buf[i];
923 if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
924 collapse_pos(0, i + 1);
925 break;
926 }
927 }
928 /* skip first not quoted '\'' or '"' */
929 for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++);
930 /* collapse quote or unquote // or /~ */
931 while ((int_buf[i] & ~QUOT) == '/' &&
932 ((int_buf[i + 1] & ~QUOT) == '/'
933 || (int_buf[i + 1] & ~QUOT) == '~')) {
934 i++;
935 }
936
937 /* set only match and destroy quotes */
938 j = 0;
939 for (c = 0; pos_buf[i] >= 0; i++) {
940 matchBuf[c++] = matchBuf[pos_buf[i]];
941 j = pos_buf[i] + 1;
942 }
943 matchBuf[c] = 0;
944 /* old lenght matchBuf with quotes symbols */
945 *len_with_quotes = j ? j - pos_buf[0] : 0;
946
947 return command_mode;
948}
949
950/*
951 display by column original ideas from ls applet,
952 very optimize by my :)
953*/
954static void showfiles(char **matches, int nfiles)
955{
956 int ncols, row;
957 int column_width = 0;
958 int nrows = nfiles;
959
960 /* find the longest file name- use that as the column width */
961 for (row = 0; row < nrows; row++) {
962 int l = strlen(matches[row]);
963
964 if (column_width < l)
965 column_width = l;
966 }
967 column_width += 2; /* min space for columns */
968 ncols = cmdedit_termw / column_width;
969
970 if (ncols > 1) {
971 nrows /= ncols;
972 if(nfiles % ncols)
973 nrows++; /* round up fractionals */
974 column_width = -column_width; /* for printf("%-Ns", ...); */
975 } else {
976 ncols = 1;
977 }
978 for (row = 0; row < nrows; row++) {
979 int n = row;
980 int nc;
981
982 for(nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++)
983 printf("%*s", column_width, matches[n]);
984 printf("%s\n", matches[n]);
985 }
986}
987
988
989static void input_tab(int *lastWasTab)
990{
991 /* Do TAB completion */
992 static int num_matches;
993 static char **matches;
994
995 if (lastWasTab == 0) { /* free all memory */
996 if (matches) {
997 while (num_matches > 0)
998 free(matches[--num_matches]);
999 free(matches);
1000 matches = (char **) NULL;
1001 }
1002 return;
1003 }
1004 if (! *lastWasTab) {
1005
1006 char *tmp;
1007 int len_found;
1008 char matchBuf[BUFSIZ];
1009 int find_type;
1010 int recalc_pos;
1011
1012 *lastWasTab = TRUE; /* flop trigger */
1013
1014 /* Make a local copy of the string -- up
1015 * to the position of the cursor */
1016 tmp = strncpy(matchBuf, command_ps, cursor);
1017 tmp[cursor] = 0;
1018
1019 find_type = find_match(matchBuf, &recalc_pos);
1020
1021 /* Free up any memory already allocated */
1022 input_tab(0);
1023
1024#ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
1025 /* If the word starts with `~' and there is no slash in the word,
1026 * then try completing this word as a username. */
1027
1028 if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
1029 matches = username_tab_completion(matchBuf, &num_matches);
1030#endif
1031 /* Try to match any executable in our path and everything
1032 * in the current working directory that matches. */
1033 if (!matches)
1034 matches =
1035 exe_n_cwd_tab_completion(matchBuf,
1036 &num_matches, find_type);
1037 /* Remove duplicate found */
1038 if(matches) {
1039 int i, j;
1040 /* bubble */
1041 for(i=0; i<(num_matches-1); i++)
1042 for(j=i+1; j<num_matches; j++)
1043 if(matches[i]!=0 && matches[j]!=0 &&
1044 strcmp(matches[i], matches[j])==0) {
1045 free(matches[j]);
1046 matches[j]=0;
1047 }
1048 j=num_matches;
1049 num_matches = 0;
1050 for(i=0; i<j; i++)
1051 if(matches[i]) {
1052 if(!strcmp(matches[i], "./"))
1053 matches[i][1]=0;
1054 else if(!strcmp(matches[i], "../"))
1055 matches[i][2]=0;
1056 matches[num_matches++]=matches[i];
1057 }
1058 }
1059 /* Did we find exactly one match? */
1060 if (!matches || num_matches > 1) {
1061 char *tmp1;
1062
1063 beep();
1064 if (!matches)
1065 return; /* not found */
1066 /* sort */
1067 qsort(matches, num_matches, sizeof(char *), match_compare);
1068
1069 /* find minimal match */
1070 tmp = bb_xstrdup(matches[0]);
1071 for (tmp1 = tmp; *tmp1; tmp1++)
1072 for (len_found = 1; len_found < num_matches; len_found++)
1073 if (matches[len_found][(tmp1 - tmp)] != *tmp1) {
1074 *tmp1 = 0;
1075 break;
1076 }
1077 if (*tmp == 0) { /* have unique */
1078 free(tmp);
1079 return;
1080 }
1081 } else { /* one match */
1082 tmp = matches[0];
1083 /* for next completion current found */
1084 *lastWasTab = FALSE;
1085 }
1086
1087 len_found = strlen(tmp);
1088 /* have space to placed match? */
1089 if ((len_found - strlen(matchBuf) + len) < BUFSIZ) {
1090
1091 /* before word for match */
1092 command_ps[cursor - recalc_pos] = 0;
1093 /* save tail line */
1094 strcpy(matchBuf, command_ps + cursor);
1095 /* add match */
1096 strcat(command_ps, tmp);
1097 /* add tail */
1098 strcat(command_ps, matchBuf);
1099 /* back to begin word for match */
1100 input_backward(recalc_pos);
1101 /* new pos */
1102 recalc_pos = cursor + len_found;
1103 /* new len */
1104 len = strlen(command_ps);
1105 /* write out the matched command */
1106 redraw(cmdedit_y, len - recalc_pos);
1107 }
1108 if (tmp != matches[0])
1109 free(tmp);
1110 } else {
1111 /* Ok -- the last char was a TAB. Since they
1112 * just hit TAB again, print a list of all the
1113 * available choices... */
1114 if (matches && num_matches > 0) {
1115 int sav_cursor = cursor; /* change goto_new_line() */
1116
1117 /* Go to the next line */
1118 goto_new_line();
1119 showfiles(matches, num_matches);
1120 redraw(0, len - sav_cursor);
1121 }
1122 }
1123}
1124#endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
1125
1126#if MAX_HISTORY >= 1
1127static void get_previous_history(void)
1128{
1129 if(command_ps[0] != 0 || history[cur_history] == 0) {
1130 free(history[cur_history]);
1131 history[cur_history] = bb_xstrdup(command_ps);
1132 }
1133 cur_history--;
1134}
1135
1136static int get_next_history(void)
1137{
1138 int ch = cur_history;
1139
1140 if (ch < n_history) {
1141 get_previous_history(); /* save the current history line */
1142 return (cur_history = ch+1);
1143 } else {
1144 beep();
1145 return 0;
1146 }
1147}
1148
1149#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
1150extern void load_history ( const char *fromfile )
1151{
1152 FILE *fp;
1153 int hi;
1154
1155 /* cleanup old */
1156
1157 for(hi = n_history; hi > 0; ) {
1158 hi--;
1159 free ( history [hi] );
1160 }
1161
1162 if (( fp = fopen ( fromfile, "r" ))) {
1163
1164 for ( hi = 0; hi < MAX_HISTORY; ) {
1165 char * hl = bb_get_chomped_line_from_file(fp);
1166 int l;
1167
1168 if(!hl)
1169 break;
1170 l = strlen(hl);
1171 if(l >= BUFSIZ)
1172 hl[BUFSIZ-1] = 0;
1173 if(l == 0 || hl[0] == ' ') {
1174 free(hl);
1175 continue;
1176 }
1177 history [hi++] = hl;
1178 }
1179 fclose ( fp );
1180 }
1181 cur_history = n_history = hi;
1182}
1183
1184extern void save_history ( const char *tofile )
1185{
1186 FILE *fp = fopen ( tofile, "w" );
1187
1188 if ( fp ) {
1189 int i;
1190
1191 for ( i = 0; i < n_history; i++ ) {
1192 fprintf(fp, "%s\n", history [i]);
1193 }
1194 fclose ( fp );
1195 }
1196}
1197#endif
1198
1199#endif
1200
1201enum {
1202 ESC = 27,
1203 DEL = 127,
1204};
1205
1206
1207/*
1208 * This function is used to grab a character buffer
1209 * from the input file descriptor and allows you to
1210 * a string with full command editing (sort of like
1211 * a mini readline).
1212 *
1213 * The following standard commands are not implemented:
1214 * ESC-b -- Move back one word
1215 * ESC-f -- Move forward one word
1216 * ESC-d -- Delete back one word
1217 * ESC-h -- Delete forward one word
1218 * CTL-t -- Transpose two characters
1219 *
1220 * Furthermore, the "vi" command editing keys are not implemented.
1221 *
1222 */
1223
1224
1225int cmdedit_read_input(char *prompt, char command[BUFSIZ])
1226{
1227
1228 int break_out = 0;
1229 int lastWasTab = FALSE;
1230 unsigned char c = 0;
1231
1232 /* prepare before init handlers */
1233 cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
1234 len = 0;
1235 command_ps = command;
1236
1237 getTermSettings(0, (void *) &initial_settings);
1238 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
1239 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
1240 /* Turn off echoing and CTRL-C, so we can trap it */
1241 new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG);
1242 /* Hmm, in linux c_cc[] not parsed if set ~ICANON */
1243 new_settings.c_cc[VMIN] = 1;
1244 new_settings.c_cc[VTIME] = 0;
1245 /* Turn off CTRL-C, so we can trap it */
1246# ifndef _POSIX_VDISABLE
1247# define _POSIX_VDISABLE '\0'
1248# endif
1249 new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
1250 command[0] = 0;
1251
1252 setTermSettings(0, (void *) &new_settings);
1253 handlers_sets |= SET_RESET_TERM;
1254
1255 /* Now initialize things */
1256 cmdedit_init();
1257 /* Print out the command prompt */
1258 parse_prompt(prompt);
1259
1260 while (1) {
1261
1262 fflush(stdout); /* buffered out to fast */
1263
1264 if (safe_read(0, &c, 1) < 1)
1265 /* if we can't read input then exit */
1266 goto prepare_to_die;
1267
1268 switch (c) {
1269 case '\n':
1270 case '\r':
1271 /* Enter */
1272 goto_new_line();
1273 break_out = 1;
1274 break;
1275 case 1:
1276 /* Control-a -- Beginning of line */
1277 input_backward(cursor);
1278 break;
1279 case 2:
1280 /* Control-b -- Move back one character */
1281 input_backward(1);
1282 break;
1283 case 3:
1284 /* Control-c -- stop gathering input */
1285 goto_new_line();
1286#ifndef CONFIG_ASH
1287 command[0] = 0;
1288 len = 0;
1289 lastWasTab = FALSE;
1290 put_prompt();
1291#else
1292 len = 0;
1293 break_out = -1; /* to control traps */
1294#endif
1295 break;
1296 case 4:
1297 /* Control-d -- Delete one character, or exit
1298 * if the len=0 and no chars to delete */
1299 if (len == 0) {
1300 errno = 0;
1301prepare_to_die:
1302#if !defined(CONFIG_ASH)
1303 printf("exit");
1304 goto_new_line();
1305 /* cmdedit_reset_term() called in atexit */
1306 exit(EXIT_SUCCESS);
1307#else
1308 /* to control stopped jobs */
1309 len = break_out = -1;
1310 break;
1311#endif
1312 } else {
1313 input_delete();
1314 }
1315 break;
1316 case 5:
1317 /* Control-e -- End of line */
1318 input_end();
1319 break;
1320 case 6:
1321 /* Control-f -- Move forward one character */
1322 input_forward();
1323 break;
1324 case '\b':
1325 case DEL:
1326 /* Control-h and DEL */
1327 input_backspace();
1328 break;
1329 case '\t':
1330#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
1331 input_tab(&lastWasTab);
1332#endif
1333 break;
1334 case 11:
1335 /* Control-k -- clear to end of line */
1336 *(command + cursor) = 0;
1337 len = cursor;
1338 printf("\033[J");
1339 break;
1340 case 12:
1341 /* Control-l -- clear screen */
1342 printf("\033[H");
1343 redraw(0, len-cursor);
1344 break;
1345#if MAX_HISTORY >= 1
1346 case 14:
1347 /* Control-n -- Get next command in history */
1348 if (get_next_history())
1349 goto rewrite_line;
1350 break;
1351 case 16:
1352 /* Control-p -- Get previous command from history */
1353 if (cur_history > 0) {
1354 get_previous_history();
1355 goto rewrite_line;
1356 } else {
1357 beep();
1358 }
1359 break;
1360#endif
1361 case 21:
1362 /* Control-U -- Clear line before cursor */
1363 if (cursor) {
1364 strcpy(command, command + cursor);
1365 redraw(cmdedit_y, len -= cursor);
1366 }
1367 break;
1368 case 23:
1369 /* Control-W -- Remove the last word */
1370 while (cursor > 0 && isspace(command[cursor-1]))
1371 input_backspace();
1372 while (cursor > 0 &&!isspace(command[cursor-1]))
1373 input_backspace();
1374 break;
1375 case ESC:{
1376 /* escape sequence follows */
1377 if (safe_read(0, &c, 1) < 1)
1378 goto prepare_to_die;
1379 /* different vt100 emulations */
1380 if (c == '[' || c == 'O') {
1381 if (safe_read(0, &c, 1) < 1)
1382 goto prepare_to_die;
1383 }
1384 if (c >= '1' && c <= '9') {
1385 unsigned char dummy;
1386
1387 if (safe_read(0, &dummy, 1) < 1)
1388 goto prepare_to_die;
1389 if(dummy != '~')
1390 c = 0;
1391 }
1392 switch (c) {
1393#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
1394 case '\t': /* Alt-Tab */
1395
1396 input_tab(&lastWasTab);
1397 break;
1398#endif
1399#if MAX_HISTORY >= 1
1400 case 'A':
1401 /* Up Arrow -- Get previous command from history */
1402 if (cur_history > 0) {
1403 get_previous_history();
1404 goto rewrite_line;
1405 } else {
1406 beep();
1407 }
1408 break;
1409 case 'B':
1410 /* Down Arrow -- Get next command in history */
1411 if (!get_next_history())
1412 break;
1413 /* Rewrite the line with the selected history item */
1414rewrite_line:
1415 /* change command */
1416 len = strlen(strcpy(command, history[cur_history]));
1417 /* redraw and go to end line */
1418 redraw(cmdedit_y, 0);
1419 break;
1420#endif
1421 case 'C':
1422 /* Right Arrow -- Move forward one character */
1423 input_forward();
1424 break;
1425 case 'D':
1426 /* Left Arrow -- Move back one character */
1427 input_backward(1);
1428 break;
1429 case '3':
1430 /* Delete */
1431 input_delete();
1432 break;
1433 case '1':
1434 case 'H':
1435 /* <Home> */
1436 input_backward(cursor);
1437 break;
1438 case '4':
1439 case 'F':
1440 /* <End> */
1441 input_end();
1442 break;
1443 default:
1444 c = 0;
1445 beep();
1446 }
1447 break;
1448 }
1449
1450 default: /* If it's regular input, do the normal thing */
1451#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1452 /* Control-V -- Add non-printable symbol */
1453 if (c == 22) {
1454 if (safe_read(0, &c, 1) < 1)
1455 goto prepare_to_die;
1456 if (c == 0) {
1457 beep();
1458 break;
1459 }
1460 } else
1461#endif
1462 if (!Isprint(c)) /* Skip non-printable characters */
1463 break;
1464
1465 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
1466 break;
1467
1468 len++;
1469
1470 if (cursor == (len - 1)) { /* Append if at the end of the line */
1471 *(command + cursor) = c;
1472 *(command + cursor + 1) = 0;
1473 cmdedit_set_out_char(0);
1474 } else { /* Insert otherwise */
1475 int sc = cursor;
1476
1477 memmove(command + sc + 1, command + sc, len - sc);
1478 *(command + sc) = c;
1479 sc++;
1480 /* rewrite from cursor */
1481 input_end();
1482 /* to prev x pos + 1 */
1483 input_backward(cursor - sc);
1484 }
1485
1486 break;
1487 }
1488 if (break_out) /* Enter is the command terminator, no more input. */
1489 break;
1490
1491 if (c != '\t')
1492 lastWasTab = FALSE;
1493 }
1494
1495 setTermSettings(0, (void *) &initial_settings);
1496 handlers_sets &= ~SET_RESET_TERM;
1497
1498#if MAX_HISTORY >= 1
1499 /* Handle command history log */
1500 /* cleanup may be saved current command line */
1501 if (len> 0) { /* no put empty line */
1502 int i = n_history;
1503
1504 free(history[MAX_HISTORY]);
1505 history[MAX_HISTORY] = 0;
1506 /* After max history, remove the oldest command */
1507 if (i >= MAX_HISTORY) {
1508 free(history[0]);
1509 for(i = 0; i < (MAX_HISTORY-1); i++)
1510 history[i] = history[i+1];
1511 }
1512 history[i++] = bb_xstrdup(command);
1513 cur_history = i;
1514 n_history = i;
1515#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1516 num_ok_lines++;
1517#endif
1518 }
1519#else /* MAX_HISTORY < 1 */
1520#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1521 if (len > 0) { /* no put empty line */
1522 num_ok_lines++;
1523 }
1524#endif
1525#endif /* MAX_HISTORY >= 1 */
1526 if (break_out > 0) {
1527 command[len++] = '\n'; /* set '\n' */
1528 command[len] = 0;
1529 }
1530#if defined(CONFIG_FEATURE_CLEAN_UP) && defined(CONFIG_FEATURE_COMMAND_TAB_COMPLETION)
1531 input_tab(0); /* strong free */
1532#endif
1533#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1534 free(cmdedit_prompt);
1535#endif
1536 cmdedit_reset_term();
1537 return len;
1538}
1539
1540
1541
1542#endif /* CONFIG_FEATURE_COMMAND_EDITING */
1543
1544
1545#ifdef TEST
1546
1547const char *bb_applet_name = "debug stuff usage";
1548
1549#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1550#include <locale.h>
1551#endif
1552
1553int main(int argc, char **argv)
1554{
1555 char buff[BUFSIZ];
1556 char *prompt =
1557#if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
1558 "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:\
1559\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \
1560\\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
1561#else
1562 "% ";
1563#endif
1564
1565#ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
1566 setlocale(LC_ALL, "");
1567#endif
1568 while(1) {
1569 int l;
1570 l = cmdedit_read_input(prompt, buff);
1571 if(l > 0 && buff[l-1] == '\n') {
1572 buff[l-1] = 0;
1573 printf("*** cmdedit_read_input() returned line =%s=\n", buff);
1574 } else {
1575 break;
1576 }
1577 }
1578 printf("*** cmdedit_read_input() detect ^D\n");
1579 return 0;
1580}
1581
1582#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 @@
1#ifndef CMDEDIT_H
2#define CMDEDIT_H
3
4int cmdedit_read_input(char* promptStr, char* command);
5
6#ifdef CONFIG_ASH
7extern const char *cmdedit_path_lookup;
8#endif
9
10#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
11void load_history ( const char *fromfile );
12void save_history ( const char *tofile );
13#endif
14
15#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * sh.c -- a prototype Bourne shell grammar parser
4 * Intended to follow the original Thompson and Ritchie
5 * "small and simple is beautiful" philosophy, which
6 * incidentally is a good match to today's BusyBox.
7 *
8 * Copyright (C) 2000,2001 Larry Doolittle <larry@doolittle.boa.org>
9 *
10 * Credits:
11 * The parser routines proper are all original material, first
12 * written Dec 2000 and Jan 2001 by Larry Doolittle. The
13 * execution engine, the builtins, and much of the underlying
14 * support has been adapted from busybox-0.49pre's lash, which is
15 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
16 * written by Erik Andersen <andersen@codepoet.org>. That, in turn,
17 * is based in part on ladsh.c, by Michael K. Johnson and Erik W.
18 * Troan, which they placed in the public domain. I don't know
19 * how much of the Johnson/Troan code has survived the repeated
20 * rewrites.
21 *
22 * Other credits:
23 * simple_itoa() was lifted from boa-0.93.15
24 * b_addchr() derived from similar w_addchar function in glibc-2.2
25 * setup_redirect(), redirect_opt_num(), and big chunks of main()
26 * and many builtins derived from contributions by Erik Andersen
27 * miscellaneous bugfixes from Matt Kraai
28 *
29 * There are two big (and related) architecture differences between
30 * this parser and the lash parser. One is that this version is
31 * actually designed from the ground up to understand nearly all
32 * of the Bourne grammar. The second, consequential change is that
33 * the parser and input reader have been turned inside out. Now,
34 * the parser is in control, and asks for input as needed. The old
35 * way had the input reader in control, and it asked for parsing to
36 * take place as needed. The new way makes it much easier to properly
37 * handle the recursion implicit in the various substitutions, especially
38 * across continuation lines.
39 *
40 * Bash grammar not implemented: (how many of these were in original sh?)
41 * $@ (those sure look like weird quoting rules)
42 * $_
43 * ! negation operator for pipes
44 * &> and >& redirection of stdout+stderr
45 * Brace Expansion
46 * Tilde Expansion
47 * fancy forms of Parameter Expansion
48 * aliases
49 * Arithmetic Expansion
50 * <(list) and >(list) Process Substitution
51 * reserved words: case, esac, select, function
52 * Here Documents ( << word )
53 * Functions
54 * Major bugs:
55 * job handling woefully incomplete and buggy
56 * reserved word execution woefully incomplete and buggy
57 * to-do:
58 * port selected bugfixes from post-0.49 busybox lash - done?
59 * finish implementing reserved words: for, while, until, do, done
60 * change { and } from special chars to reserved words
61 * builtins: break, continue, eval, return, set, trap, ulimit
62 * test magic exec
63 * handle children going into background
64 * clean up recognition of null pipes
65 * check setting of global_argc and global_argv
66 * control-C handling, probably with longjmp
67 * follow IFS rules more precisely, including update semantics
68 * figure out what to do with backslash-newline
69 * explain why we use signal instead of sigaction
70 * propagate syntax errors, die on resource errors?
71 * continuation lines, both explicit and implicit - done?
72 * memory leak finding and plugging - done?
73 * more testing, especially quoting rules and redirection
74 * document how quoting rules not precisely followed for variable assignments
75 * maybe change map[] to use 2-bit entries
76 * (eventually) remove all the printf's
77 *
78 * This program is free software; you can redistribute it and/or modify
79 * it under the terms of the GNU General Public License as published by
80 * the Free Software Foundation; either version 2 of the License, or
81 * (at your option) any later version.
82 *
83 * This program is distributed in the hope that it will be useful,
84 * but WITHOUT ANY WARRANTY; without even the implied warranty of
85 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
86 * General Public License for more details.
87 *
88 * You should have received a copy of the GNU General Public License
89 * along with this program; if not, write to the Free Software
90 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
91 */
92#include <ctype.h> /* isalpha, isdigit */
93#include <unistd.h> /* getpid */
94#include <stdlib.h> /* getenv, atoi */
95#include <string.h> /* strchr */
96#include <stdio.h> /* popen etc. */
97#include <glob.h> /* glob, of course */
98#include <stdarg.h> /* va_list */
99#include <errno.h>
100#include <fcntl.h>
101#include <getopt.h> /* should be pretty obvious */
102
103#include <sys/stat.h> /* ulimit */
104#include <sys/types.h>
105#include <sys/wait.h>
106#include <signal.h>
107
108/* #include <dmalloc.h> */
109/* #define DEBUG_SHELL */
110
111#if 1
112#include "busybox.h"
113#include "cmdedit.h"
114#else
115#define bb_applet_name "hush"
116#include "standalone.h"
117#define hush_main main
118#undef CONFIG_FEATURE_SH_FANCY_PROMPT
119#define BB_BANNER
120#endif
121#define SPECIAL_VAR_SYMBOL 03
122#define FLAG_EXIT_FROM_LOOP 1
123#define FLAG_PARSE_SEMICOLON (1 << 1) /* symbol ';' is special for parser */
124#define FLAG_REPARSING (1 << 2) /* >=2nd pass */
125
126typedef enum {
127 REDIRECT_INPUT = 1,
128 REDIRECT_OVERWRITE = 2,
129 REDIRECT_APPEND = 3,
130 REDIRECT_HEREIS = 4,
131 REDIRECT_IO = 5
132} redir_type;
133
134/* The descrip member of this structure is only used to make debugging
135 * output pretty */
136struct {int mode; int default_fd; char *descrip;} redir_table[] = {
137 { 0, 0, "()" },
138 { O_RDONLY, 0, "<" },
139 { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" },
140 { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" },
141 { O_RDONLY, -1, "<<" },
142 { O_RDWR, 1, "<>" }
143};
144
145typedef enum {
146 PIPE_SEQ = 1,
147 PIPE_AND = 2,
148 PIPE_OR = 3,
149 PIPE_BG = 4,
150} pipe_style;
151
152/* might eventually control execution */
153typedef enum {
154 RES_NONE = 0,
155 RES_IF = 1,
156 RES_THEN = 2,
157 RES_ELIF = 3,
158 RES_ELSE = 4,
159 RES_FI = 5,
160 RES_FOR = 6,
161 RES_WHILE = 7,
162 RES_UNTIL = 8,
163 RES_DO = 9,
164 RES_DONE = 10,
165 RES_XXXX = 11,
166 RES_IN = 12,
167 RES_SNTX = 13
168} reserved_style;
169#define FLAG_END (1<<RES_NONE)
170#define FLAG_IF (1<<RES_IF)
171#define FLAG_THEN (1<<RES_THEN)
172#define FLAG_ELIF (1<<RES_ELIF)
173#define FLAG_ELSE (1<<RES_ELSE)
174#define FLAG_FI (1<<RES_FI)
175#define FLAG_FOR (1<<RES_FOR)
176#define FLAG_WHILE (1<<RES_WHILE)
177#define FLAG_UNTIL (1<<RES_UNTIL)
178#define FLAG_DO (1<<RES_DO)
179#define FLAG_DONE (1<<RES_DONE)
180#define FLAG_IN (1<<RES_IN)
181#define FLAG_START (1<<RES_XXXX)
182
183/* This holds pointers to the various results of parsing */
184struct p_context {
185 struct child_prog *child;
186 struct pipe *list_head;
187 struct pipe *pipe;
188 struct redir_struct *pending_redirect;
189 reserved_style w;
190 int old_flag; /* for figuring out valid reserved words */
191 struct p_context *stack;
192 int type; /* define type of parser : ";$" common or special symbol */
193 /* How about quoting status? */
194};
195
196struct redir_struct {
197 redir_type type; /* type of redirection */
198 int fd; /* file descriptor being redirected */
199 int dup; /* -1, or file descriptor being duplicated */
200 struct redir_struct *next; /* pointer to the next redirect in the list */
201 glob_t word; /* *word.gl_pathv is the filename */
202};
203
204struct child_prog {
205 pid_t pid; /* 0 if exited */
206 char **argv; /* program name and arguments */
207 struct pipe *group; /* if non-NULL, first in group or subshell */
208 int subshell; /* flag, non-zero if group must be forked */
209 struct redir_struct *redirects; /* I/O redirections */
210 glob_t glob_result; /* result of parameter globbing */
211 int is_stopped; /* is the program currently running? */
212 struct pipe *family; /* pointer back to the child's parent pipe */
213 int sp; /* number of SPECIAL_VAR_SYMBOL */
214 int type;
215};
216
217struct pipe {
218 int jobid; /* job number */
219 int num_progs; /* total number of programs in job */
220 int running_progs; /* number of programs running */
221 char *text; /* name of job */
222 char *cmdbuf; /* buffer various argv's point into */
223 pid_t pgrp; /* process group ID for the job */
224 struct child_prog *progs; /* array of commands in pipe */
225 struct pipe *next; /* to track background commands */
226 int stopped_progs; /* number of programs alive, but stopped */
227 int job_context; /* bitmask defining current context */
228 pipe_style followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */
229 reserved_style r_mode; /* supports if, for, while, until */
230};
231
232struct close_me {
233 int fd;
234 struct close_me *next;
235};
236
237struct variables {
238 char *name;
239 char *value;
240 int flg_export;
241 int flg_read_only;
242 struct variables *next;
243};
244
245/* globals, connect us to the outside world
246 * the first three support $?, $#, and $1 */
247char **global_argv;
248unsigned int global_argc;
249unsigned int last_return_code;
250extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */
251
252/* "globals" within this file */
253static char *ifs;
254static char map[256];
255static int fake_mode;
256static int interactive;
257static struct close_me *close_me_head;
258static const char *cwd;
259static struct pipe *job_list;
260static unsigned int last_bg_pid;
261static unsigned int last_jobid;
262static unsigned int shell_terminal;
263static char *PS1;
264static char *PS2;
265struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 };
266struct variables *top_vars = &shell_ver;
267
268
269#define B_CHUNK (100)
270#define B_NOSPAC 1
271
272typedef struct {
273 char *data;
274 int length;
275 int maxlen;
276 int quote;
277 int nonnull;
278} o_string;
279#define NULL_O_STRING {NULL,0,0,0,0}
280/* used for initialization:
281 o_string foo = NULL_O_STRING; */
282
283/* I can almost use ordinary FILE *. Is open_memstream() universally
284 * available? Where is it documented? */
285struct in_str {
286 const char *p;
287 char peek_buf[2];
288 int __promptme;
289 int promptmode;
290 FILE *file;
291 int (*get) (struct in_str *);
292 int (*peek) (struct in_str *);
293};
294#define b_getch(input) ((input)->get(input))
295#define b_peek(input) ((input)->peek(input))
296
297#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
298
299struct built_in_command {
300 char *cmd; /* name */
301 char *descr; /* description */
302 int (*function) (struct child_prog *); /* function ptr */
303};
304
305/* belongs in busybox.h */
306static inline int max(int a, int b) {
307 return (a>b)?a:b;
308}
309
310/* This should be in utility.c */
311#ifdef DEBUG_SHELL
312static void debug_printf(const char *format, ...)
313{
314 va_list args;
315 va_start(args, format);
316 vfprintf(stderr, format, args);
317 va_end(args);
318}
319#else
320static inline void debug_printf(const char *format, ...) { }
321#endif
322#define final_printf debug_printf
323
324static void __syntax(char *file, int line) {
325 bb_error_msg("syntax error %s:%d", file, line);
326}
327#define syntax() __syntax(__FILE__, __LINE__)
328
329/* Index of subroutines: */
330/* function prototypes for builtins */
331static int builtin_cd(struct child_prog *child);
332static int builtin_env(struct child_prog *child);
333static int builtin_eval(struct child_prog *child);
334static int builtin_exec(struct child_prog *child);
335static int builtin_exit(struct child_prog *child);
336static int builtin_export(struct child_prog *child);
337static int builtin_fg_bg(struct child_prog *child);
338static int builtin_help(struct child_prog *child);
339static int builtin_jobs(struct child_prog *child);
340static int builtin_pwd(struct child_prog *child);
341static int builtin_read(struct child_prog *child);
342static int builtin_set(struct child_prog *child);
343static int builtin_shift(struct child_prog *child);
344static int builtin_source(struct child_prog *child);
345static int builtin_umask(struct child_prog *child);
346static int builtin_unset(struct child_prog *child);
347static int builtin_not_written(struct child_prog *child);
348/* o_string manipulation: */
349static int b_check_space(o_string *o, int len);
350static int b_addchr(o_string *o, int ch);
351static void b_reset(o_string *o);
352static int b_addqchr(o_string *o, int ch, int quote);
353static int b_adduint(o_string *o, unsigned int i);
354/* in_str manipulations: */
355static int static_get(struct in_str *i);
356static int static_peek(struct in_str *i);
357static int file_get(struct in_str *i);
358static int file_peek(struct in_str *i);
359static void setup_file_in_str(struct in_str *i, FILE *f);
360static void setup_string_in_str(struct in_str *i, const char *s);
361/* close_me manipulations: */
362static void mark_open(int fd);
363static void mark_closed(int fd);
364static void close_all(void);
365/* "run" the final data structures: */
366static char *indenter(int i);
367static int free_pipe_list(struct pipe *head, int indent);
368static int free_pipe(struct pipe *pi, int indent);
369/* really run the final data structures: */
370static int setup_redirects(struct child_prog *prog, int squirrel[]);
371static int run_list_real(struct pipe *pi);
372static void pseudo_exec(struct child_prog *child) __attribute__ ((noreturn));
373static int run_pipe_real(struct pipe *pi);
374/* extended glob support: */
375static int globhack(const char *src, int flags, glob_t *pglob);
376static int glob_needed(const char *s);
377static int xglob(o_string *dest, int flags, glob_t *pglob);
378/* variable assignment: */
379static int is_assignment(const char *s);
380/* data structure manipulation: */
381static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input);
382static void initialize_context(struct p_context *ctx);
383static int done_word(o_string *dest, struct p_context *ctx);
384static int done_command(struct p_context *ctx);
385static int done_pipe(struct p_context *ctx, pipe_style type);
386/* primary string parsing: */
387static int redirect_dup_num(struct in_str *input);
388static int redirect_opt_num(o_string *o);
389static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end);
390static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch);
391static char *lookup_param(char *src);
392static char *make_string(char **inp);
393static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input);
394static int parse_string(o_string *dest, struct p_context *ctx, const char *src);
395static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, int end_trigger);
396/* setup: */
397static int parse_stream_outer(struct in_str *inp, int flag);
398static int parse_string_outer(const char *s, int flag);
399static int parse_file_outer(FILE *f);
400/* job management: */
401static int checkjobs(struct pipe* fg_pipe);
402static void insert_bg_job(struct pipe *pi);
403static void remove_bg_job(struct pipe *pi);
404/* local variable support */
405static char **make_list_in(char **inp, char *name);
406static char *insert_var_value(char *inp);
407static char *get_local_var(const char *var);
408static void unset_local_var(const char *name);
409static int set_local_var(const char *s, int flg_export);
410
411/* Table of built-in functions. They can be forked or not, depending on
412 * context: within pipes, they fork. As simple commands, they do not.
413 * When used in non-forking context, they can change global variables
414 * in the parent shell process. If forked, of course they can not.
415 * For example, 'unset foo | whatever' will parse and run, but foo will
416 * still be set at the end. */
417static struct built_in_command bltins[] = {
418 {"bg", "Resume a job in the background", builtin_fg_bg},
419 {"break", "Exit for, while or until loop", builtin_not_written},
420 {"cd", "Change working directory", builtin_cd},
421 {"continue", "Continue for, while or until loop", builtin_not_written},
422 {"env", "Print all environment variables", builtin_env},
423 {"eval", "Construct and run shell command", builtin_eval},
424 {"exec", "Exec command, replacing this shell with the exec'd process",
425 builtin_exec},
426 {"exit", "Exit from shell()", builtin_exit},
427 {"export", "Set environment variable", builtin_export},
428 {"fg", "Bring job into the foreground", builtin_fg_bg},
429 {"jobs", "Lists the active jobs", builtin_jobs},
430 {"pwd", "Print current directory", builtin_pwd},
431 {"read", "Input environment variable", builtin_read},
432 {"return", "Return from a function", builtin_not_written},
433 {"set", "Set/unset shell local variables", builtin_set},
434 {"shift", "Shift positional parameters", builtin_shift},
435 {"trap", "Trap signals", builtin_not_written},
436 {"ulimit","Controls resource limits", builtin_not_written},
437 {"umask","Sets file creation mask", builtin_umask},
438 {"unset", "Unset environment variable", builtin_unset},
439 {".", "Source-in and run commands in a file", builtin_source},
440 {"help", "List shell built-in commands", builtin_help},
441 {NULL, NULL, NULL}
442};
443
444static const char *set_cwd(void)
445{
446 if(cwd==bb_msg_unknown)
447 cwd = NULL; /* xgetcwd(arg) called free(arg) */
448 cwd = xgetcwd((char *)cwd);
449 if (!cwd)
450 cwd = bb_msg_unknown;
451 return cwd;
452}
453
454/* built-in 'eval' handler */
455static int builtin_eval(struct child_prog *child)
456{
457 char *str = NULL;
458 int rcode = EXIT_SUCCESS;
459
460 if (child->argv[1]) {
461 str = make_string(child->argv + 1);
462 parse_string_outer(str, FLAG_EXIT_FROM_LOOP |
463 FLAG_PARSE_SEMICOLON);
464 free(str);
465 rcode = last_return_code;
466 }
467 return rcode;
468}
469
470/* built-in 'cd <path>' handler */
471static int builtin_cd(struct child_prog *child)
472{
473 char *newdir;
474 if (child->argv[1] == NULL)
475 newdir = getenv("HOME");
476 else
477 newdir = child->argv[1];
478 if (chdir(newdir)) {
479 printf("cd: %s: %s\n", newdir, strerror(errno));
480 return EXIT_FAILURE;
481 }
482 set_cwd();
483 return EXIT_SUCCESS;
484}
485
486/* built-in 'env' handler */
487static int builtin_env(struct child_prog *dummy)
488{
489 char **e = environ;
490 if (e == NULL) return EXIT_FAILURE;
491 for (; *e; e++) {
492 puts(*e);
493 }
494 return EXIT_SUCCESS;
495}
496
497/* built-in 'exec' handler */
498static int builtin_exec(struct child_prog *child)
499{
500 if (child->argv[1] == NULL)
501 return EXIT_SUCCESS; /* Really? */
502 child->argv++;
503 pseudo_exec(child);
504 /* never returns */
505}
506
507/* built-in 'exit' handler */
508static int builtin_exit(struct child_prog *child)
509{
510 if (child->argv[1] == NULL)
511 exit(last_return_code);
512 exit (atoi(child->argv[1]));
513}
514
515/* built-in 'export VAR=value' handler */
516static int builtin_export(struct child_prog *child)
517{
518 int res = 0;
519 char *name = child->argv[1];
520
521 if (name == NULL) {
522 return (builtin_env(child));
523 }
524
525 name = strdup(name);
526
527 if(name) {
528 char *value = strchr(name, '=');
529
530 if (!value) {
531 char *tmp;
532 /* They are exporting something without an =VALUE */
533
534 value = get_local_var(name);
535 if (value) {
536 size_t ln = strlen(name);
537
538 tmp = realloc(name, ln+strlen(value)+2);
539 if(tmp==NULL)
540 res = -1;
541 else {
542 sprintf(tmp+ln, "=%s", value);
543 name = tmp;
544 }
545 } else {
546 /* bash does not return an error when trying to export
547 * an undefined variable. Do likewise. */
548 res = 1;
549 }
550 }
551 }
552 if (res<0)
553 bb_perror_msg("export");
554 else if(res==0)
555 res = set_local_var(name, 1);
556 else
557 res = 0;
558 free(name);
559 return res;
560}
561
562/* built-in 'fg' and 'bg' handler */
563static int builtin_fg_bg(struct child_prog *child)
564{
565 int i, jobnum;
566 struct pipe *pi=NULL;
567
568 if (!interactive)
569 return EXIT_FAILURE;
570 /* If they gave us no args, assume they want the last backgrounded task */
571 if (!child->argv[1]) {
572 for (pi = job_list; pi; pi = pi->next) {
573 if (pi->jobid == last_jobid) {
574 break;
575 }
576 }
577 if (!pi) {
578 bb_error_msg("%s: no current job", child->argv[0]);
579 return EXIT_FAILURE;
580 }
581 } else {
582 if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) {
583 bb_error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]);
584 return EXIT_FAILURE;
585 }
586 for (pi = job_list; pi; pi = pi->next) {
587 if (pi->jobid == jobnum) {
588 break;
589 }
590 }
591 if (!pi) {
592 bb_error_msg("%s: %d: no such job", child->argv[0], jobnum);
593 return EXIT_FAILURE;
594 }
595 }
596
597 if (*child->argv[0] == 'f') {
598 /* Put the job into the foreground. */
599 tcsetpgrp(shell_terminal, pi->pgrp);
600 }
601
602 /* Restart the processes in the job */
603 for (i = 0; i < pi->num_progs; i++)
604 pi->progs[i].is_stopped = 0;
605
606 if ( (i=kill(- pi->pgrp, SIGCONT)) < 0) {
607 if (i == ESRCH) {
608 remove_bg_job(pi);
609 } else {
610 bb_perror_msg("kill (SIGCONT)");
611 }
612 }
613
614 pi->stopped_progs = 0;
615 return EXIT_SUCCESS;
616}
617
618/* built-in 'help' handler */
619static int builtin_help(struct child_prog *dummy)
620{
621 struct built_in_command *x;
622
623 printf("\nBuilt-in commands:\n");
624 printf("-------------------\n");
625 for (x = bltins; x->cmd; x++) {
626 if (x->descr==NULL)
627 continue;
628 printf("%s\t%s\n", x->cmd, x->descr);
629 }
630 printf("\n\n");
631 return EXIT_SUCCESS;
632}
633
634/* built-in 'jobs' handler */
635static int builtin_jobs(struct child_prog *child)
636{
637 struct pipe *job;
638 char *status_string;
639
640 for (job = job_list; job; job = job->next) {
641 if (job->running_progs == job->stopped_progs)
642 status_string = "Stopped";
643 else
644 status_string = "Running";
645
646 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text);
647 }
648 return EXIT_SUCCESS;
649}
650
651
652/* built-in 'pwd' handler */
653static int builtin_pwd(struct child_prog *dummy)
654{
655 puts(set_cwd());
656 return EXIT_SUCCESS;
657}
658
659/* built-in 'read VAR' handler */
660static int builtin_read(struct child_prog *child)
661{
662 int res;
663
664 if (child->argv[1]) {
665 char string[BUFSIZ];
666 char *var = 0;
667
668 string[0] = 0; /* In case stdin has only EOF */
669 /* read string */
670 fgets(string, sizeof(string), stdin);
671 chomp(string);
672 var = malloc(strlen(child->argv[1])+strlen(string)+2);
673 if(var) {
674 sprintf(var, "%s=%s", child->argv[1], string);
675 res = set_local_var(var, 0);
676 } else
677 res = -1;
678 if (res)
679 fprintf(stderr, "read: %m\n");
680 free(var); /* So not move up to avoid breaking errno */
681 return res;
682 } else {
683 do res=getchar(); while(res!='\n' && res!=EOF);
684 return 0;
685 }
686}
687
688/* built-in 'set VAR=value' handler */
689static int builtin_set(struct child_prog *child)
690{
691 char *temp = child->argv[1];
692 struct variables *e;
693
694 if (temp == NULL)
695 for(e = top_vars; e; e=e->next)
696 printf("%s=%s\n", e->name, e->value);
697 else
698 set_local_var(temp, 0);
699
700 return EXIT_SUCCESS;
701}
702
703
704/* Built-in 'shift' handler */
705static int builtin_shift(struct child_prog *child)
706{
707 int n=1;
708 if (child->argv[1]) {
709 n=atoi(child->argv[1]);
710 }
711 if (n>=0 && n<global_argc) {
712 /* XXX This probably breaks $0 */
713 global_argc -= n;
714 global_argv += n;
715 return EXIT_SUCCESS;
716 } else {
717 return EXIT_FAILURE;
718 }
719}
720
721/* Built-in '.' handler (read-in and execute commands from file) */
722static int builtin_source(struct child_prog *child)
723{
724 FILE *input;
725 int status;
726
727 if (child->argv[1] == NULL)
728 return EXIT_FAILURE;
729
730 /* XXX search through $PATH is missing */
731 input = fopen(child->argv[1], "r");
732 if (!input) {
733 bb_error_msg("Couldn't open file '%s'", child->argv[1]);
734 return EXIT_FAILURE;
735 }
736
737 /* Now run the file */
738 /* XXX argv and argc are broken; need to save old global_argv
739 * (pointer only is OK!) on this stack frame,
740 * set global_argv=child->argv+1, recurse, and restore. */
741 mark_open(fileno(input));
742 status = parse_file_outer(input);
743 mark_closed(fileno(input));
744 fclose(input);
745 return (status);
746}
747
748static int builtin_umask(struct child_prog *child)
749{
750 mode_t new_umask;
751 const char *arg = child->argv[1];
752 char *end;
753 if (arg) {
754 new_umask=strtoul(arg, &end, 8);
755 if (*end!='\0' || end == arg) {
756 return EXIT_FAILURE;
757 }
758 } else {
759 printf("%.3o\n", (unsigned int) (new_umask=umask(0)));
760 }
761 umask(new_umask);
762 return EXIT_SUCCESS;
763}
764
765/* built-in 'unset VAR' handler */
766static int builtin_unset(struct child_prog *child)
767{
768 /* bash returned already true */
769 unset_local_var(child->argv[1]);
770 return EXIT_SUCCESS;
771}
772
773static int builtin_not_written(struct child_prog *child)
774{
775 printf("builtin_%s not written\n",child->argv[0]);
776 return EXIT_FAILURE;
777}
778
779static int b_check_space(o_string *o, int len)
780{
781 /* It would be easy to drop a more restrictive policy
782 * in here, such as setting a maximum string length */
783 if (o->length + len > o->maxlen) {
784 char *old_data = o->data;
785 /* assert (data == NULL || o->maxlen != 0); */
786 o->maxlen += max(2*len, B_CHUNK);
787 o->data = realloc(o->data, 1 + o->maxlen);
788 if (o->data == NULL) {
789 free(old_data);
790 }
791 }
792 return o->data == NULL;
793}
794
795static int b_addchr(o_string *o, int ch)
796{
797 debug_printf("b_addchr: %c %d %p\n", ch, o->length, o);
798 if (b_check_space(o, 1)) return B_NOSPAC;
799 o->data[o->length] = ch;
800 o->length++;
801 o->data[o->length] = '\0';
802 return 0;
803}
804
805static void b_reset(o_string *o)
806{
807 o->length = 0;
808 o->nonnull = 0;
809 if (o->data != NULL) *o->data = '\0';
810}
811
812static void b_free(o_string *o)
813{
814 b_reset(o);
815 free(o->data);
816 o->data = NULL;
817 o->maxlen = 0;
818}
819
820/* My analysis of quoting semantics tells me that state information
821 * is associated with a destination, not a source.
822 */
823static int b_addqchr(o_string *o, int ch, int quote)
824{
825 if (quote && strchr("*?[\\",ch)) {
826 int rc;
827 rc = b_addchr(o, '\\');
828 if (rc) return rc;
829 }
830 return b_addchr(o, ch);
831}
832
833/* belongs in utility.c */
834char *simple_itoa(unsigned int i)
835{
836 /* 21 digits plus null terminator, good for 64-bit or smaller ints */
837 static char local[22];
838 char *p = &local[21];
839 *p-- = '\0';
840 do {
841 *p-- = '0' + i % 10;
842 i /= 10;
843 } while (i > 0);
844 return p + 1;
845}
846
847static int b_adduint(o_string *o, unsigned int i)
848{
849 int r;
850 char *p = simple_itoa(i);
851 /* no escape checking necessary */
852 do r=b_addchr(o, *p++); while (r==0 && *p);
853 return r;
854}
855
856static int static_get(struct in_str *i)
857{
858 int ch=*i->p++;
859 if (ch=='\0') return EOF;
860 return ch;
861}
862
863static int static_peek(struct in_str *i)
864{
865 return *i->p;
866}
867
868static inline void cmdedit_set_initial_prompt(void)
869{
870#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
871 PS1 = NULL;
872#else
873 PS1 = getenv("PS1");
874 if(PS1==0)
875 PS1 = "\\w \\$ ";
876#endif
877}
878
879static inline void setup_prompt_string(int promptmode, char **prompt_str)
880{
881 debug_printf("setup_prompt_string %d ",promptmode);
882#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
883 /* Set up the prompt */
884 if (promptmode == 1) {
885 free(PS1);
886 PS1=xmalloc(strlen(cwd)+4);
887 sprintf(PS1, "%s %s", cwd, ( geteuid() != 0 ) ? "$ ":"# ");
888 *prompt_str = PS1;
889 } else {
890 *prompt_str = PS2;
891 }
892#else
893 *prompt_str = (promptmode==1)? PS1 : PS2;
894#endif
895 debug_printf("result %s\n",*prompt_str);
896}
897
898static void get_user_input(struct in_str *i)
899{
900 char *prompt_str;
901 static char the_command[BUFSIZ];
902
903 setup_prompt_string(i->promptmode, &prompt_str);
904#ifdef CONFIG_FEATURE_COMMAND_EDITING
905 /*
906 ** enable command line editing only while a command line
907 ** is actually being read; otherwise, we'll end up bequeathing
908 ** atexit() handlers and other unwanted stuff to our
909 ** child processes (rob@sysgo.de)
910 */
911 cmdedit_read_input(prompt_str, the_command);
912#else
913 fputs(prompt_str, stdout);
914 fflush(stdout);
915 the_command[0]=fgetc(i->file);
916 the_command[1]='\0';
917#endif
918 fflush(stdout);
919 i->p = the_command;
920}
921
922/* This is the magic location that prints prompts
923 * and gets data back from the user */
924static int file_get(struct in_str *i)
925{
926 int ch;
927
928 ch = 0;
929 /* If there is data waiting, eat it up */
930 if (i->p && *i->p) {
931 ch=*i->p++;
932 } else {
933 /* need to double check i->file because we might be doing something
934 * more complicated by now, like sourcing or substituting. */
935 if (i->__promptme && interactive && i->file == stdin) {
936 while(! i->p || (interactive && strlen(i->p)==0) ) {
937 get_user_input(i);
938 }
939 i->promptmode=2;
940 i->__promptme = 0;
941 if (i->p && *i->p) {
942 ch=*i->p++;
943 }
944 } else {
945 ch = fgetc(i->file);
946 }
947
948 debug_printf("b_getch: got a %d\n", ch);
949 }
950 if (ch == '\n') i->__promptme=1;
951 return ch;
952}
953
954/* All the callers guarantee this routine will never be
955 * used right after a newline, so prompting is not needed.
956 */
957static int file_peek(struct in_str *i)
958{
959 if (i->p && *i->p) {
960 return *i->p;
961 } else {
962 i->peek_buf[0] = fgetc(i->file);
963 i->peek_buf[1] = '\0';
964 i->p = i->peek_buf;
965 debug_printf("b_peek: got a %d\n", *i->p);
966 return *i->p;
967 }
968}
969
970static void setup_file_in_str(struct in_str *i, FILE *f)
971{
972 i->peek = file_peek;
973 i->get = file_get;
974 i->__promptme=1;
975 i->promptmode=1;
976 i->file = f;
977 i->p = NULL;
978}
979
980static void setup_string_in_str(struct in_str *i, const char *s)
981{
982 i->peek = static_peek;
983 i->get = static_get;
984 i->__promptme=1;
985 i->promptmode=1;
986 i->p = s;
987}
988
989static void mark_open(int fd)
990{
991 struct close_me *new = xmalloc(sizeof(struct close_me));
992 new->fd = fd;
993 new->next = close_me_head;
994 close_me_head = new;
995}
996
997static void mark_closed(int fd)
998{
999 struct close_me *tmp;
1000 if (close_me_head == NULL || close_me_head->fd != fd)
1001 bb_error_msg_and_die("corrupt close_me");
1002 tmp = close_me_head;
1003 close_me_head = close_me_head->next;
1004 free(tmp);
1005}
1006
1007static void close_all(void)
1008{
1009 struct close_me *c;
1010 for (c=close_me_head; c; c=c->next) {
1011 close(c->fd);
1012 }
1013 close_me_head = NULL;
1014}
1015
1016/* squirrel != NULL means we squirrel away copies of stdin, stdout,
1017 * and stderr if they are redirected. */
1018static int setup_redirects(struct child_prog *prog, int squirrel[])
1019{
1020 int openfd, mode;
1021 struct redir_struct *redir;
1022
1023 for (redir=prog->redirects; redir; redir=redir->next) {
1024 if (redir->dup == -1 && redir->word.gl_pathv == NULL) {
1025 /* something went wrong in the parse. Pretend it didn't happen */
1026 continue;
1027 }
1028 if (redir->dup == -1) {
1029 mode=redir_table[redir->type].mode;
1030 openfd = open(redir->word.gl_pathv[0], mode, 0666);
1031 if (openfd < 0) {
1032 /* this could get lost if stderr has been redirected, but
1033 bash and ash both lose it as well (though zsh doesn't!) */
1034 bb_perror_msg("error opening %s", redir->word.gl_pathv[0]);
1035 return 1;
1036 }
1037 } else {
1038 openfd = redir->dup;
1039 }
1040
1041 if (openfd != redir->fd) {
1042 if (squirrel && redir->fd < 3) {
1043 squirrel[redir->fd] = dup(redir->fd);
1044 }
1045 if (openfd == -3) {
1046 close(openfd);
1047 } else {
1048 dup2(openfd, redir->fd);
1049 if (redir->dup == -1)
1050 close (openfd);
1051 }
1052 }
1053 }
1054 return 0;
1055}
1056
1057static void restore_redirects(int squirrel[])
1058{
1059 int i, fd;
1060 for (i=0; i<3; i++) {
1061 fd = squirrel[i];
1062 if (fd != -1) {
1063 /* No error checking. I sure wouldn't know what
1064 * to do with an error if I found one! */
1065 dup2(fd, i);
1066 close(fd);
1067 }
1068 }
1069}
1070
1071/* never returns */
1072/* XXX no exit() here. If you don't exec, use _exit instead.
1073 * The at_exit handlers apparently confuse the calling process,
1074 * in particular stdin handling. Not sure why? */
1075static void pseudo_exec(struct child_prog *child)
1076{
1077 int i, rcode;
1078 char *p;
1079 struct built_in_command *x;
1080 if (child->argv) {
1081 for (i=0; is_assignment(child->argv[i]); i++) {
1082 debug_printf("pid %d environment modification: %s\n",getpid(),child->argv[i]);
1083 p = insert_var_value(child->argv[i]);
1084 putenv(strdup(p));
1085 if (p != child->argv[i]) free(p);
1086 }
1087 child->argv+=i; /* XXX this hack isn't so horrible, since we are about
1088 to exit, and therefore don't need to keep data
1089 structures consistent for free() use. */
1090 /* If a variable is assigned in a forest, and nobody listens,
1091 * was it ever really set?
1092 */
1093 if (child->argv[0] == NULL) {
1094 _exit(EXIT_SUCCESS);
1095 }
1096
1097 /*
1098 * Check if the command matches any of the builtins.
1099 * Depending on context, this might be redundant. But it's
1100 * easier to waste a few CPU cycles than it is to figure out
1101 * if this is one of those cases.
1102 */
1103 for (x = bltins; x->cmd; x++) {
1104 if (strcmp(child->argv[0], x->cmd) == 0 ) {
1105 debug_printf("builtin exec %s\n", child->argv[0]);
1106 rcode = x->function(child);
1107 fflush(stdout);
1108 _exit(rcode);
1109 }
1110 }
1111
1112 /* Check if the command matches any busybox internal commands
1113 * ("applets") here.
1114 * FIXME: This feature is not 100% safe, since
1115 * BusyBox is not fully reentrant, so we have no guarantee the things
1116 * from the .bss are still zeroed, or that things from .data are still
1117 * at their defaults. We could exec ourself from /proc/self/exe, but I
1118 * really dislike relying on /proc for things. We could exec ourself
1119 * from global_argv[0], but if we are in a chroot, we may not be able
1120 * to find ourself... */
1121#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
1122 {
1123 int argc_l;
1124 char** argv_l=child->argv;
1125 char *name = child->argv[0];
1126
1127 /* Count argc for use in a second... */
1128 for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++);
1129 optind = 1;
1130 debug_printf("running applet %s\n", name);
1131 run_applet_by_name(name, argc_l, child->argv);
1132 }
1133#endif
1134 debug_printf("exec of %s\n",child->argv[0]);
1135 execvp(child->argv[0],child->argv);
1136 bb_perror_msg("couldn't exec: %s",child->argv[0]);
1137 _exit(1);
1138 } else if (child->group) {
1139 debug_printf("runtime nesting to group\n");
1140 interactive=0; /* crucial!!!! */
1141 rcode = run_list_real(child->group);
1142 /* OK to leak memory by not calling free_pipe_list,
1143 * since this process is about to exit */
1144 _exit(rcode);
1145 } else {
1146 /* Can happen. See what bash does with ">foo" by itself. */
1147 debug_printf("trying to pseudo_exec null command\n");
1148 _exit(EXIT_SUCCESS);
1149 }
1150}
1151
1152static void insert_bg_job(struct pipe *pi)
1153{
1154 struct pipe *thejob;
1155
1156 /* Linear search for the ID of the job to use */
1157 pi->jobid = 1;
1158 for (thejob = job_list; thejob; thejob = thejob->next)
1159 if (thejob->jobid >= pi->jobid)
1160 pi->jobid = thejob->jobid + 1;
1161
1162 /* add thejob to the list of running jobs */
1163 if (!job_list) {
1164 thejob = job_list = xmalloc(sizeof(*thejob));
1165 } else {
1166 for (thejob = job_list; thejob->next; thejob = thejob->next) /* nothing */;
1167 thejob->next = xmalloc(sizeof(*thejob));
1168 thejob = thejob->next;
1169 }
1170
1171 /* physically copy the struct job */
1172 memcpy(thejob, pi, sizeof(struct pipe));
1173 thejob->next = NULL;
1174 thejob->running_progs = thejob->num_progs;
1175 thejob->stopped_progs = 0;
1176 thejob->text = xmalloc(BUFSIZ); /* cmdedit buffer size */
1177
1178 //if (pi->progs[0] && pi->progs[0].argv && pi->progs[0].argv[0])
1179 {
1180 char *bar=thejob->text;
1181 char **foo=pi->progs[0].argv;
1182 while(foo && *foo) {
1183 bar += sprintf(bar, "%s ", *foo++);
1184 }
1185 }
1186
1187 /* we don't wait for background thejobs to return -- append it
1188 to the list of backgrounded thejobs and leave it alone */
1189 printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid);
1190 last_bg_pid = thejob->progs[0].pid;
1191 last_jobid = thejob->jobid;
1192}
1193
1194/* remove a backgrounded job */
1195static void remove_bg_job(struct pipe *pi)
1196{
1197 struct pipe *prev_pipe;
1198
1199 if (pi == job_list) {
1200 job_list = pi->next;
1201 } else {
1202 prev_pipe = job_list;
1203 while (prev_pipe->next != pi)
1204 prev_pipe = prev_pipe->next;
1205 prev_pipe->next = pi->next;
1206 }
1207 if (job_list)
1208 last_jobid = job_list->jobid;
1209 else
1210 last_jobid = 0;
1211
1212 pi->stopped_progs = 0;
1213 free_pipe(pi, 0);
1214 free(pi);
1215}
1216
1217/* Checks to see if any processes have exited -- if they
1218 have, figure out why and see if a job has completed */
1219static int checkjobs(struct pipe* fg_pipe)
1220{
1221 int attributes;
1222 int status;
1223 int prognum = 0;
1224 struct pipe *pi;
1225 pid_t childpid;
1226
1227 attributes = WUNTRACED;
1228 if (fg_pipe==NULL) {
1229 attributes |= WNOHANG;
1230 }
1231
1232 while ((childpid = waitpid(-1, &status, attributes)) > 0) {
1233 if (fg_pipe) {
1234 int i, rcode = 0;
1235 for (i=0; i < fg_pipe->num_progs; i++) {
1236 if (fg_pipe->progs[i].pid == childpid) {
1237 if (i==fg_pipe->num_progs-1)
1238 rcode=WEXITSTATUS(status);
1239 (fg_pipe->num_progs)--;
1240 return(rcode);
1241 }
1242 }
1243 }
1244
1245 for (pi = job_list; pi; pi = pi->next) {
1246 prognum = 0;
1247 while (prognum < pi->num_progs && pi->progs[prognum].pid != childpid) {
1248 prognum++;
1249 }
1250 if (prognum < pi->num_progs)
1251 break;
1252 }
1253
1254 if(pi==NULL) {
1255 debug_printf("checkjobs: pid %d was not in our list!\n", childpid);
1256 continue;
1257 }
1258
1259 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1260 /* child exited */
1261 pi->running_progs--;
1262 pi->progs[prognum].pid = 0;
1263
1264 if (!pi->running_progs) {
1265 printf(JOB_STATUS_FORMAT, pi->jobid, "Done", pi->text);
1266 remove_bg_job(pi);
1267 }
1268 } else {
1269 /* child stopped */
1270 pi->stopped_progs++;
1271 pi->progs[prognum].is_stopped = 1;
1272
1273#if 0
1274 /* Printing this stuff is a pain, since it tends to
1275 * overwrite the prompt an inconveinient moments. So
1276 * don't do that. */
1277 if (pi->stopped_progs == pi->num_progs) {
1278 printf("\n"JOB_STATUS_FORMAT, pi->jobid, "Stopped", pi->text);
1279 }
1280#endif
1281 }
1282 }
1283
1284 if (childpid == -1 && errno != ECHILD)
1285 bb_perror_msg("waitpid");
1286
1287 /* move the shell to the foreground */
1288 //if (interactive && tcsetpgrp(shell_terminal, getpgid(0)))
1289 // bb_perror_msg("tcsetpgrp-2");
1290 return -1;
1291}
1292
1293/* Figure out our controlling tty, checking in order stderr,
1294 * stdin, and stdout. If check_pgrp is set, also check that
1295 * we belong to the foreground process group associated with
1296 * that tty. The value of shell_terminal is needed in order to call
1297 * tcsetpgrp(shell_terminal, ...); */
1298void controlling_tty(int check_pgrp)
1299{
1300 pid_t curpgrp;
1301
1302 if ((curpgrp = tcgetpgrp(shell_terminal = 2)) < 0
1303 && (curpgrp = tcgetpgrp(shell_terminal = 0)) < 0
1304 && (curpgrp = tcgetpgrp(shell_terminal = 1)) < 0)
1305 goto shell_terminal_error;
1306
1307 if (check_pgrp && curpgrp != getpgid(0))
1308 goto shell_terminal_error;
1309
1310 return;
1311
1312shell_terminal_error:
1313 shell_terminal = -1;
1314 return;
1315}
1316
1317/* run_pipe_real() starts all the jobs, but doesn't wait for anything
1318 * to finish. See checkjobs().
1319 *
1320 * return code is normally -1, when the caller has to wait for children
1321 * to finish to determine the exit status of the pipe. If the pipe
1322 * is a simple builtin command, however, the action is done by the
1323 * time run_pipe_real returns, and the exit code is provided as the
1324 * return value.
1325 *
1326 * The input of the pipe is always stdin, the output is always
1327 * stdout. The outpipe[] mechanism in BusyBox-0.48 lash is bogus,
1328 * because it tries to avoid running the command substitution in
1329 * subshell, when that is in fact necessary. The subshell process
1330 * now has its stdout directed to the input of the appropriate pipe,
1331 * so this routine is noticeably simpler.
1332 */
1333static int run_pipe_real(struct pipe *pi)
1334{
1335 int i;
1336 int nextin, nextout;
1337 int pipefds[2]; /* pipefds[0] is for reading */
1338 struct child_prog *child;
1339 struct built_in_command *x;
1340 char *p;
1341
1342 nextin = 0;
1343 pi->pgrp = -1;
1344
1345 /* Check if this is a simple builtin (not part of a pipe).
1346 * Builtins within pipes have to fork anyway, and are handled in
1347 * pseudo_exec. "echo foo | read bar" doesn't work on bash, either.
1348 */
1349 if (pi->num_progs == 1) child = & (pi->progs[0]);
1350 if (pi->num_progs == 1 && child->group && child->subshell == 0) {
1351 int squirrel[] = {-1, -1, -1};
1352 int rcode;
1353 debug_printf("non-subshell grouping\n");
1354 setup_redirects(child, squirrel);
1355 /* XXX could we merge code with following builtin case,
1356 * by creating a pseudo builtin that calls run_list_real? */
1357 rcode = run_list_real(child->group);
1358 restore_redirects(squirrel);
1359 return rcode;
1360 } else if (pi->num_progs == 1 && pi->progs[0].argv != NULL) {
1361 for (i=0; is_assignment(child->argv[i]); i++) { /* nothing */ }
1362 if (i!=0 && child->argv[i]==NULL) {
1363 /* assignments, but no command: set the local environment */
1364 for (i=0; child->argv[i]!=NULL; i++) {
1365
1366 /* Ok, this case is tricky. We have to decide if this is a
1367 * local variable, or an already exported variable. If it is
1368 * already exported, we have to export the new value. If it is
1369 * not exported, we need only set this as a local variable.
1370 * This junk is all to decide whether or not to export this
1371 * variable. */
1372 int export_me=0;
1373 char *name, *value;
1374 name = bb_xstrdup(child->argv[i]);
1375 debug_printf("Local environment set: %s\n", name);
1376 value = strchr(name, '=');
1377 if (value)
1378 *value=0;
1379 if ( get_local_var(name)) {
1380 export_me=1;
1381 }
1382 free(name);
1383 p = insert_var_value(child->argv[i]);
1384 set_local_var(p, export_me);
1385 if (p != child->argv[i]) free(p);
1386 }
1387 return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */
1388 }
1389 for (i = 0; is_assignment(child->argv[i]); i++) {
1390 p = insert_var_value(child->argv[i]);
1391 putenv(strdup(p));
1392 if (p != child->argv[i]) {
1393 child->sp--;
1394 free(p);
1395 }
1396 }
1397 if (child->sp) {
1398 char * str = NULL;
1399
1400 str = make_string((child->argv + i));
1401 parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING);
1402 free(str);
1403 return last_return_code;
1404 }
1405 for (x = bltins; x->cmd; x++) {
1406 if (strcmp(child->argv[i], x->cmd) == 0 ) {
1407 int squirrel[] = {-1, -1, -1};
1408 int rcode;
1409 if (x->function == builtin_exec && child->argv[i+1]==NULL) {
1410 debug_printf("magic exec\n");
1411 setup_redirects(child,NULL);
1412 return EXIT_SUCCESS;
1413 }
1414 debug_printf("builtin inline %s\n", child->argv[0]);
1415 /* XXX setup_redirects acts on file descriptors, not FILEs.
1416 * This is perfect for work that comes after exec().
1417 * Is it really safe for inline use? Experimentally,
1418 * things seem to work with glibc. */
1419 setup_redirects(child, squirrel);
1420 child->argv+=i; /* XXX horrible hack */
1421 rcode = x->function(child);
1422 child->argv-=i; /* XXX restore hack so free() can work right */
1423 restore_redirects(squirrel);
1424 return rcode;
1425 }
1426 }
1427 }
1428
1429 for (i = 0; i < pi->num_progs; i++) {
1430 child = & (pi->progs[i]);
1431
1432 /* pipes are inserted between pairs of commands */
1433 if ((i + 1) < pi->num_progs) {
1434 if (pipe(pipefds)<0) bb_perror_msg_and_die("pipe");
1435 nextout = pipefds[1];
1436 } else {
1437 nextout=1;
1438 pipefds[0] = -1;
1439 }
1440
1441 /* XXX test for failed fork()? */
1442#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
1443 if (!(child->pid = fork()))
1444#else
1445 if (!(child->pid = vfork()))
1446#endif
1447 {
1448 /* Set the handling for job control signals back to the default. */
1449 signal(SIGINT, SIG_DFL);
1450 signal(SIGQUIT, SIG_DFL);
1451 signal(SIGTERM, SIG_DFL);
1452 signal(SIGTSTP, SIG_DFL);
1453 signal(SIGTTIN, SIG_DFL);
1454 signal(SIGTTOU, SIG_DFL);
1455 signal(SIGCHLD, SIG_DFL);
1456
1457 close_all();
1458
1459 if (nextin != 0) {
1460 dup2(nextin, 0);
1461 close(nextin);
1462 }
1463 if (nextout != 1) {
1464 dup2(nextout, 1);
1465 close(nextout);
1466 }
1467 if (pipefds[0]!=-1) {
1468 close(pipefds[0]); /* opposite end of our output pipe */
1469 }
1470
1471 /* Like bash, explicit redirects override pipes,
1472 * and the pipe fd is available for dup'ing. */
1473 setup_redirects(child,NULL);
1474
1475 if (interactive && pi->followup!=PIPE_BG) {
1476 /* If we (the child) win the race, put ourselves in the process
1477 * group whose leader is the first process in this pipe. */
1478 if (pi->pgrp < 0) {
1479 pi->pgrp = getpid();
1480 }
1481 if (setpgid(0, pi->pgrp) == 0) {
1482 tcsetpgrp(2, pi->pgrp);
1483 }
1484 }
1485
1486 pseudo_exec(child);
1487 }
1488
1489
1490 /* put our child in the process group whose leader is the
1491 first process in this pipe */
1492 if (pi->pgrp < 0) {
1493 pi->pgrp = child->pid;
1494 }
1495 /* Don't check for errors. The child may be dead already,
1496 * in which case setpgid returns error code EACCES. */
1497 setpgid(child->pid, pi->pgrp);
1498
1499 if (nextin != 0)
1500 close(nextin);
1501 if (nextout != 1)
1502 close(nextout);
1503
1504 /* If there isn't another process, nextin is garbage
1505 but it doesn't matter */
1506 nextin = pipefds[0];
1507 }
1508 return -1;
1509}
1510
1511static int run_list_real(struct pipe *pi)
1512{
1513 char *save_name = NULL;
1514 char **list = NULL;
1515 char **save_list = NULL;
1516 struct pipe *rpipe;
1517 int flag_rep = 0;
1518 int save_num_progs;
1519 int rcode=0, flag_skip=1;
1520 int flag_restore = 0;
1521 int if_code=0, next_if_code=0; /* need double-buffer to handle elif */
1522 reserved_style rmode, skip_more_in_this_rmode=RES_XXXX;
1523 /* check syntax for "for" */
1524 for (rpipe = pi; rpipe; rpipe = rpipe->next) {
1525 if ((rpipe->r_mode == RES_IN ||
1526 rpipe->r_mode == RES_FOR) &&
1527 (rpipe->next == NULL)) {
1528 syntax();
1529 return 1;
1530 }
1531 if ((rpipe->r_mode == RES_IN &&
1532 (rpipe->next->r_mode == RES_IN &&
1533 rpipe->next->progs->argv != NULL))||
1534 (rpipe->r_mode == RES_FOR &&
1535 rpipe->next->r_mode != RES_IN)) {
1536 syntax();
1537 return 1;
1538 }
1539 }
1540 for (; pi; pi = (flag_restore != 0) ? rpipe : pi->next) {
1541 if (pi->r_mode == RES_WHILE || pi->r_mode == RES_UNTIL ||
1542 pi->r_mode == RES_FOR) {
1543 flag_restore = 0;
1544 if (!rpipe) {
1545 flag_rep = 0;
1546 rpipe = pi;
1547 }
1548 }
1549 rmode = pi->r_mode;
1550 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);
1551 if (rmode == skip_more_in_this_rmode && flag_skip) {
1552 if (pi->followup == PIPE_SEQ) flag_skip=0;
1553 continue;
1554 }
1555 flag_skip = 1;
1556 skip_more_in_this_rmode = RES_XXXX;
1557 if (rmode == RES_THEN || rmode == RES_ELSE) if_code = next_if_code;
1558 if (rmode == RES_THEN && if_code) continue;
1559 if (rmode == RES_ELSE && !if_code) continue;
1560 if (rmode == RES_ELIF && !if_code) break;
1561 if (rmode == RES_FOR && pi->num_progs) {
1562 if (!list) {
1563 /* if no variable values after "in" we skip "for" */
1564 if (!pi->next->progs->argv) continue;
1565 /* create list of variable values */
1566 list = make_list_in(pi->next->progs->argv,
1567 pi->progs->argv[0]);
1568 save_list = list;
1569 save_name = pi->progs->argv[0];
1570 pi->progs->argv[0] = NULL;
1571 flag_rep = 1;
1572 }
1573 if (!(*list)) {
1574 free(pi->progs->argv[0]);
1575 free(save_list);
1576 list = NULL;
1577 flag_rep = 0;
1578 pi->progs->argv[0] = save_name;
1579 pi->progs->glob_result.gl_pathv[0] =
1580 pi->progs->argv[0];
1581 continue;
1582 } else {
1583 /* insert new value from list for variable */
1584 if (pi->progs->argv[0])
1585 free(pi->progs->argv[0]);
1586 pi->progs->argv[0] = *list++;
1587 pi->progs->glob_result.gl_pathv[0] =
1588 pi->progs->argv[0];
1589 }
1590 }
1591 if (rmode == RES_IN) continue;
1592 if (rmode == RES_DO) {
1593 if (!flag_rep) continue;
1594 }
1595 if ((rmode == RES_DONE)) {
1596 if (flag_rep) {
1597 flag_restore = 1;
1598 } else {
1599 rpipe = NULL;
1600 }
1601 }
1602 if (pi->num_progs == 0) continue;
1603 save_num_progs = pi->num_progs; /* save number of programs */
1604 rcode = run_pipe_real(pi);
1605 debug_printf("run_pipe_real returned %d\n",rcode);
1606 if (rcode!=-1) {
1607 /* We only ran a builtin: rcode was set by the return value
1608 * of run_pipe_real(), and we don't need to wait for anything. */
1609 } else if (pi->followup==PIPE_BG) {
1610 /* XXX check bash's behavior with nontrivial pipes */
1611 /* XXX compute jobid */
1612 /* XXX what does bash do with attempts to background builtins? */
1613 insert_bg_job(pi);
1614 rcode = EXIT_SUCCESS;
1615 } else {
1616 if (interactive) {
1617 /* move the new process group into the foreground */
1618 if (tcsetpgrp(shell_terminal, pi->pgrp) && errno != ENOTTY)
1619 bb_perror_msg("tcsetpgrp-3");
1620 rcode = checkjobs(pi);
1621 /* move the shell to the foreground */
1622 if (tcsetpgrp(shell_terminal, getpgid(0)) && errno != ENOTTY)
1623 bb_perror_msg("tcsetpgrp-4");
1624 } else {
1625 rcode = checkjobs(pi);
1626 }
1627 debug_printf("checkjobs returned %d\n",rcode);
1628 }
1629 last_return_code=rcode;
1630 pi->num_progs = save_num_progs; /* restore number of programs */
1631 if ( rmode == RES_IF || rmode == RES_ELIF )
1632 next_if_code=rcode; /* can be overwritten a number of times */
1633 if (rmode == RES_WHILE)
1634 flag_rep = !last_return_code;
1635 if (rmode == RES_UNTIL)
1636 flag_rep = last_return_code;
1637 if ( (rcode==EXIT_SUCCESS && pi->followup==PIPE_OR) ||
1638 (rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) )
1639 skip_more_in_this_rmode=rmode;
1640 checkjobs(NULL);
1641 }
1642 return rcode;
1643}
1644
1645/* broken, of course, but OK for testing */
1646static char *indenter(int i)
1647{
1648 static char blanks[]=" ";
1649 return &blanks[sizeof(blanks)-i-1];
1650}
1651
1652/* return code is the exit status of the pipe */
1653static int free_pipe(struct pipe *pi, int indent)
1654{
1655 char **p;
1656 struct child_prog *child;
1657 struct redir_struct *r, *rnext;
1658 int a, i, ret_code=0;
1659 char *ind = indenter(indent);
1660
1661 if (pi->stopped_progs > 0)
1662 return ret_code;
1663 final_printf("%s run pipe: (pid %d)\n",ind,getpid());
1664 for (i=0; i<pi->num_progs; i++) {
1665 child = &pi->progs[i];
1666 final_printf("%s command %d:\n",ind,i);
1667 if (child->argv) {
1668 for (a=0,p=child->argv; *p; a++,p++) {
1669 final_printf("%s argv[%d] = %s\n",ind,a,*p);
1670 }
1671 globfree(&child->glob_result);
1672 child->argv=NULL;
1673 } else if (child->group) {
1674 final_printf("%s begin group (subshell:%d)\n",ind, child->subshell);
1675 ret_code = free_pipe_list(child->group,indent+3);
1676 final_printf("%s end group\n",ind);
1677 } else {
1678 final_printf("%s (nil)\n",ind);
1679 }
1680 for (r=child->redirects; r; r=rnext) {
1681 final_printf("%s redirect %d%s", ind, r->fd, redir_table[r->type].descrip);
1682 if (r->dup == -1) {
1683 /* guard against the case >$FOO, where foo is unset or blank */
1684 if (r->word.gl_pathv) {
1685 final_printf(" %s\n", *r->word.gl_pathv);
1686 globfree(&r->word);
1687 }
1688 } else {
1689 final_printf("&%d\n", r->dup);
1690 }
1691 rnext=r->next;
1692 free(r);
1693 }
1694 child->redirects=NULL;
1695 }
1696 free(pi->progs); /* children are an array, they get freed all at once */
1697 pi->progs=NULL;
1698 return ret_code;
1699}
1700
1701static int free_pipe_list(struct pipe *head, int indent)
1702{
1703 int rcode=0; /* if list has no members */
1704 struct pipe *pi, *next;
1705 char *ind = indenter(indent);
1706 for (pi=head; pi; pi=next) {
1707 final_printf("%s pipe reserved mode %d\n", ind, pi->r_mode);
1708 rcode = free_pipe(pi, indent);
1709 final_printf("%s pipe followup code %d\n", ind, pi->followup);
1710 next=pi->next;
1711 pi->next=NULL;
1712 free(pi);
1713 }
1714 return rcode;
1715}
1716
1717/* Select which version we will use */
1718static int run_list(struct pipe *pi)
1719{
1720 int rcode=0;
1721 if (fake_mode==0) {
1722 rcode = run_list_real(pi);
1723 }
1724 /* free_pipe_list has the side effect of clearing memory
1725 * In the long run that function can be merged with run_list_real,
1726 * but doing that now would hobble the debugging effort. */
1727 free_pipe_list(pi,0);
1728 return rcode;
1729}
1730
1731/* The API for glob is arguably broken. This routine pushes a non-matching
1732 * string into the output structure, removing non-backslashed backslashes.
1733 * If someone can prove me wrong, by performing this function within the
1734 * original glob(3) api, feel free to rewrite this routine into oblivion.
1735 * Return code (0 vs. GLOB_NOSPACE) matches glob(3).
1736 * XXX broken if the last character is '\\', check that before calling.
1737 */
1738static int globhack(const char *src, int flags, glob_t *pglob)
1739{
1740 int cnt=0, pathc;
1741 const char *s;
1742 char *dest;
1743 for (cnt=1, s=src; s && *s; s++) {
1744 if (*s == '\\') s++;
1745 cnt++;
1746 }
1747 dest = malloc(cnt);
1748 if (!dest) return GLOB_NOSPACE;
1749 if (!(flags & GLOB_APPEND)) {
1750 pglob->gl_pathv=NULL;
1751 pglob->gl_pathc=0;
1752 pglob->gl_offs=0;
1753 pglob->gl_offs=0;
1754 }
1755 pathc = ++pglob->gl_pathc;
1756 pglob->gl_pathv = realloc(pglob->gl_pathv, (pathc+1)*sizeof(*pglob->gl_pathv));
1757 if (pglob->gl_pathv == NULL) return GLOB_NOSPACE;
1758 pglob->gl_pathv[pathc-1]=dest;
1759 pglob->gl_pathv[pathc]=NULL;
1760 for (s=src; s && *s; s++, dest++) {
1761 if (*s == '\\') s++;
1762 *dest = *s;
1763 }
1764 *dest='\0';
1765 return 0;
1766}
1767
1768/* XXX broken if the last character is '\\', check that before calling */
1769static int glob_needed(const char *s)
1770{
1771 for (; *s; s++) {
1772 if (*s == '\\') s++;
1773 if (strchr("*[?",*s)) return 1;
1774 }
1775 return 0;
1776}
1777
1778#if 0
1779static void globprint(glob_t *pglob)
1780{
1781 int i;
1782 debug_printf("glob_t at %p:\n", pglob);
1783 debug_printf(" gl_pathc=%d gl_pathv=%p gl_offs=%d gl_flags=%d\n",
1784 pglob->gl_pathc, pglob->gl_pathv, pglob->gl_offs, pglob->gl_flags);
1785 for (i=0; i<pglob->gl_pathc; i++)
1786 debug_printf("pglob->gl_pathv[%d] = %p = %s\n", i,
1787 pglob->gl_pathv[i], pglob->gl_pathv[i]);
1788}
1789#endif
1790
1791static int xglob(o_string *dest, int flags, glob_t *pglob)
1792{
1793 int gr;
1794
1795 /* short-circuit for null word */
1796 /* we can code this better when the debug_printf's are gone */
1797 if (dest->length == 0) {
1798 if (dest->nonnull) {
1799 /* bash man page calls this an "explicit" null */
1800 gr = globhack(dest->data, flags, pglob);
1801 debug_printf("globhack returned %d\n",gr);
1802 } else {
1803 return 0;
1804 }
1805 } else if (glob_needed(dest->data)) {
1806 gr = glob(dest->data, flags, NULL, pglob);
1807 debug_printf("glob returned %d\n",gr);
1808 if (gr == GLOB_NOMATCH) {
1809 /* quote removal, or more accurately, backslash removal */
1810 gr = globhack(dest->data, flags, pglob);
1811 debug_printf("globhack returned %d\n",gr);
1812 }
1813 } else {
1814 gr = globhack(dest->data, flags, pglob);
1815 debug_printf("globhack returned %d\n",gr);
1816 }
1817 if (gr == GLOB_NOSPACE)
1818 bb_error_msg_and_die("out of memory during glob");
1819 if (gr != 0) { /* GLOB_ABORTED ? */
1820 bb_error_msg("glob(3) error %d",gr);
1821 }
1822 /* globprint(glob_target); */
1823 return gr;
1824}
1825
1826/* This is used to get/check local shell variables */
1827static char *get_local_var(const char *s)
1828{
1829 struct variables *cur;
1830
1831 if (!s)
1832 return NULL;
1833 for (cur = top_vars; cur; cur=cur->next)
1834 if(strcmp(cur->name, s)==0)
1835 return cur->value;
1836 return NULL;
1837}
1838
1839/* This is used to set local shell variables
1840 flg_export==0 if only local (not exporting) variable
1841 flg_export==1 if "new" exporting environ
1842 flg_export>1 if current startup environ (not call putenv()) */
1843static int set_local_var(const char *s, int flg_export)
1844{
1845 char *name, *value;
1846 int result=0;
1847 struct variables *cur;
1848
1849 name=strdup(s);
1850
1851 /* Assume when we enter this function that we are already in
1852 * NAME=VALUE format. So the first order of business is to
1853 * split 's' on the '=' into 'name' and 'value' */
1854 value = strchr(name, '=');
1855 if (value==0 && ++value==0) {
1856 free(name);
1857 return -1;
1858 }
1859 *value++ = 0;
1860
1861 for(cur = top_vars; cur; cur = cur->next) {
1862 if(strcmp(cur->name, name)==0)
1863 break;
1864 }
1865
1866 if(cur) {
1867 if(strcmp(cur->value, value)==0) {
1868 if(flg_export>0 && cur->flg_export==0)
1869 cur->flg_export=flg_export;
1870 else
1871 result++;
1872 } else {
1873 if(cur->flg_read_only) {
1874 bb_error_msg("%s: readonly variable", name);
1875 result = -1;
1876 } else {
1877 if(flg_export>0 || cur->flg_export>1)
1878 cur->flg_export=1;
1879 free(cur->value);
1880
1881 cur->value = strdup(value);
1882 }
1883 }
1884 } else {
1885 cur = malloc(sizeof(struct variables));
1886 if(!cur) {
1887 result = -1;
1888 } else {
1889 cur->name = strdup(name);
1890 if(cur->name == 0) {
1891 free(cur);
1892 result = -1;
1893 } else {
1894 struct variables *bottom = top_vars;
1895 cur->value = strdup(value);
1896 cur->next = 0;
1897 cur->flg_export = flg_export;
1898 cur->flg_read_only = 0;
1899 while(bottom->next) bottom=bottom->next;
1900 bottom->next = cur;
1901 }
1902 }
1903 }
1904
1905 if(result==0 && cur->flg_export==1) {
1906 *(value-1) = '=';
1907 result = putenv(name);
1908 } else {
1909 free(name);
1910 if(result>0) /* equivalent to previous set */
1911 result = 0;
1912 }
1913 return result;
1914}
1915
1916static void unset_local_var(const char *name)
1917{
1918 struct variables *cur;
1919
1920 if (name) {
1921 for (cur = top_vars; cur; cur=cur->next) {
1922 if(strcmp(cur->name, name)==0)
1923 break;
1924 }
1925 if(cur!=0) {
1926 struct variables *next = top_vars;
1927 if(cur->flg_read_only) {
1928 bb_error_msg("%s: readonly variable", name);
1929 return;
1930 } else {
1931 if(cur->flg_export)
1932 unsetenv(cur->name);
1933 free(cur->name);
1934 free(cur->value);
1935 while (next->next != cur)
1936 next = next->next;
1937 next->next = cur->next;
1938 }
1939 free(cur);
1940 }
1941 }
1942}
1943
1944static int is_assignment(const char *s)
1945{
1946 if (s==NULL || !isalpha(*s)) return 0;
1947 ++s;
1948 while(isalnum(*s) || *s=='_') ++s;
1949 return *s=='=';
1950}
1951
1952/* the src parameter allows us to peek forward to a possible &n syntax
1953 * for file descriptor duplication, e.g., "2>&1".
1954 * Return code is 0 normally, 1 if a syntax error is detected in src.
1955 * Resource errors (in xmalloc) cause the process to exit */
1956static int setup_redirect(struct p_context *ctx, int fd, redir_type style,
1957 struct in_str *input)
1958{
1959 struct child_prog *child=ctx->child;
1960 struct redir_struct *redir = child->redirects;
1961 struct redir_struct *last_redir=NULL;
1962
1963 /* Create a new redir_struct and drop it onto the end of the linked list */
1964 while(redir) {
1965 last_redir=redir;
1966 redir=redir->next;
1967 }
1968 redir = xmalloc(sizeof(struct redir_struct));
1969 redir->next=NULL;
1970 redir->word.gl_pathv=NULL;
1971 if (last_redir) {
1972 last_redir->next=redir;
1973 } else {
1974 child->redirects=redir;
1975 }
1976
1977 redir->type=style;
1978 redir->fd= (fd==-1) ? redir_table[style].default_fd : fd ;
1979
1980 debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip);
1981
1982 /* Check for a '2>&1' type redirect */
1983 redir->dup = redirect_dup_num(input);
1984 if (redir->dup == -2) return 1; /* syntax error */
1985 if (redir->dup != -1) {
1986 /* Erik had a check here that the file descriptor in question
1987 * is legit; I postpone that to "run time"
1988 * A "-" representation of "close me" shows up as a -3 here */
1989 debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup);
1990 } else {
1991 /* We do _not_ try to open the file that src points to,
1992 * since we need to return and let src be expanded first.
1993 * Set ctx->pending_redirect, so we know what to do at the
1994 * end of the next parsed word.
1995 */
1996 ctx->pending_redirect = redir;
1997 }
1998 return 0;
1999}
2000
2001struct pipe *new_pipe(void) {
2002 struct pipe *pi;
2003 pi = xmalloc(sizeof(struct pipe));
2004 pi->num_progs = 0;
2005 pi->progs = NULL;
2006 pi->next = NULL;
2007 pi->followup = 0; /* invalid */
2008 return pi;
2009}
2010
2011static void initialize_context(struct p_context *ctx)
2012{
2013 ctx->pipe=NULL;
2014 ctx->pending_redirect=NULL;
2015 ctx->child=NULL;
2016 ctx->list_head=new_pipe();
2017 ctx->pipe=ctx->list_head;
2018 ctx->w=RES_NONE;
2019 ctx->stack=NULL;
2020 ctx->old_flag=0;
2021 done_command(ctx); /* creates the memory for working child */
2022}
2023
2024/* normal return is 0
2025 * if a reserved word is found, and processed, return 1
2026 * should handle if, then, elif, else, fi, for, while, until, do, done.
2027 * case, function, and select are obnoxious, save those for later.
2028 */
2029int reserved_word(o_string *dest, struct p_context *ctx)
2030{
2031 struct reserved_combo {
2032 char *literal;
2033 int code;
2034 long flag;
2035 };
2036 /* Mostly a list of accepted follow-up reserved words.
2037 * FLAG_END means we are done with the sequence, and are ready
2038 * to turn the compound list into a command.
2039 * FLAG_START means the word must start a new compound list.
2040 */
2041 static struct reserved_combo reserved_list[] = {
2042 { "if", RES_IF, FLAG_THEN | FLAG_START },
2043 { "then", RES_THEN, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
2044 { "elif", RES_ELIF, FLAG_THEN },
2045 { "else", RES_ELSE, FLAG_FI },
2046 { "fi", RES_FI, FLAG_END },
2047 { "for", RES_FOR, FLAG_IN | FLAG_START },
2048 { "while", RES_WHILE, FLAG_DO | FLAG_START },
2049 { "until", RES_UNTIL, FLAG_DO | FLAG_START },
2050 { "in", RES_IN, FLAG_DO },
2051 { "do", RES_DO, FLAG_DONE },
2052 { "done", RES_DONE, FLAG_END }
2053 };
2054 struct reserved_combo *r;
2055 for (r=reserved_list;
2056#define NRES sizeof(reserved_list)/sizeof(struct reserved_combo)
2057 r<reserved_list+NRES; r++) {
2058 if (strcmp(dest->data, r->literal) == 0) {
2059 debug_printf("found reserved word %s, code %d\n",r->literal,r->code);
2060 if (r->flag & FLAG_START) {
2061 struct p_context *new = xmalloc(sizeof(struct p_context));
2062 debug_printf("push stack\n");
2063 if (ctx->w == RES_IN || ctx->w == RES_FOR) {
2064 syntax();
2065 free(new);
2066 ctx->w = RES_SNTX;
2067 b_reset(dest);
2068 return 1;
2069 }
2070 *new = *ctx; /* physical copy */
2071 initialize_context(ctx);
2072 ctx->stack=new;
2073 } else if ( ctx->w == RES_NONE || ! (ctx->old_flag & (1<<r->code))) {
2074 syntax();
2075 ctx->w = RES_SNTX;
2076 b_reset(dest);
2077 return 1;
2078 }
2079 ctx->w=r->code;
2080 ctx->old_flag = r->flag;
2081 if (ctx->old_flag & FLAG_END) {
2082 struct p_context *old;
2083 debug_printf("pop stack\n");
2084 done_pipe(ctx,PIPE_SEQ);
2085 old = ctx->stack;
2086 old->child->group = ctx->list_head;
2087 old->child->subshell = 0;
2088 *ctx = *old; /* physical copy */
2089 free(old);
2090 }
2091 b_reset (dest);
2092 return 1;
2093 }
2094 }
2095 return 0;
2096}
2097
2098/* normal return is 0.
2099 * Syntax or xglob errors return 1. */
2100static int done_word(o_string *dest, struct p_context *ctx)
2101{
2102 struct child_prog *child=ctx->child;
2103 glob_t *glob_target;
2104 int gr, flags = 0;
2105
2106 debug_printf("done_word: %s %p\n", dest->data, child);
2107 if (dest->length == 0 && !dest->nonnull) {
2108 debug_printf(" true null, ignored\n");
2109 return 0;
2110 }
2111 if (ctx->pending_redirect) {
2112 glob_target = &ctx->pending_redirect->word;
2113 } else {
2114 if (child->group) {
2115 syntax();
2116 return 1; /* syntax error, groups and arglists don't mix */
2117 }
2118 if (!child->argv && (ctx->type & FLAG_PARSE_SEMICOLON)) {
2119 debug_printf("checking %s for reserved-ness\n",dest->data);
2120 if (reserved_word(dest,ctx)) return ctx->w==RES_SNTX;
2121 }
2122 glob_target = &child->glob_result;
2123 if (child->argv) flags |= GLOB_APPEND;
2124 }
2125 gr = xglob(dest, flags, glob_target);
2126 if (gr != 0) return 1;
2127
2128 b_reset(dest);
2129 if (ctx->pending_redirect) {
2130 ctx->pending_redirect=NULL;
2131 if (glob_target->gl_pathc != 1) {
2132 bb_error_msg("ambiguous redirect");
2133 return 1;
2134 }
2135 } else {
2136 child->argv = glob_target->gl_pathv;
2137 }
2138 if (ctx->w == RES_FOR) {
2139 done_word(dest,ctx);
2140 done_pipe(ctx,PIPE_SEQ);
2141 }
2142 return 0;
2143}
2144
2145/* The only possible error here is out of memory, in which case
2146 * xmalloc exits. */
2147static int done_command(struct p_context *ctx)
2148{
2149 /* The child is really already in the pipe structure, so
2150 * advance the pipe counter and make a new, null child.
2151 * Only real trickiness here is that the uncommitted
2152 * child structure, to which ctx->child points, is not
2153 * counted in pi->num_progs. */
2154 struct pipe *pi=ctx->pipe;
2155 struct child_prog *prog=ctx->child;
2156
2157 if (prog && prog->group == NULL
2158 && prog->argv == NULL
2159 && prog->redirects == NULL) {
2160 debug_printf("done_command: skipping null command\n");
2161 return 0;
2162 } else if (prog) {
2163 pi->num_progs++;
2164 debug_printf("done_command: num_progs incremented to %d\n",pi->num_progs);
2165 } else {
2166 debug_printf("done_command: initializing\n");
2167 }
2168 pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1));
2169
2170 prog = pi->progs + pi->num_progs;
2171 prog->redirects = NULL;
2172 prog->argv = NULL;
2173 prog->is_stopped = 0;
2174 prog->group = NULL;
2175 prog->glob_result.gl_pathv = NULL;
2176 prog->family = pi;
2177 prog->sp = 0;
2178 ctx->child = prog;
2179 prog->type = ctx->type;
2180
2181 /* but ctx->pipe and ctx->list_head remain unchanged */
2182 return 0;
2183}
2184
2185static int done_pipe(struct p_context *ctx, pipe_style type)
2186{
2187 struct pipe *new_p;
2188 done_command(ctx); /* implicit closure of previous command */
2189 debug_printf("done_pipe, type %d\n", type);
2190 ctx->pipe->followup = type;
2191 ctx->pipe->r_mode = ctx->w;
2192 new_p=new_pipe();
2193 ctx->pipe->next = new_p;
2194 ctx->pipe = new_p;
2195 ctx->child = NULL;
2196 done_command(ctx); /* set up new pipe to accept commands */
2197 return 0;
2198}
2199
2200/* peek ahead in the in_str to find out if we have a "&n" construct,
2201 * as in "2>&1", that represents duplicating a file descriptor.
2202 * returns either -2 (syntax error), -1 (no &), or the number found.
2203 */
2204static int redirect_dup_num(struct in_str *input)
2205{
2206 int ch, d=0, ok=0;
2207 ch = b_peek(input);
2208 if (ch != '&') return -1;
2209
2210 b_getch(input); /* get the & */
2211 ch=b_peek(input);
2212 if (ch == '-') {
2213 b_getch(input);
2214 return -3; /* "-" represents "close me" */
2215 }
2216 while (isdigit(ch)) {
2217 d = d*10+(ch-'0');
2218 ok=1;
2219 b_getch(input);
2220 ch = b_peek(input);
2221 }
2222 if (ok) return d;
2223
2224 bb_error_msg("ambiguous redirect");
2225 return -2;
2226}
2227
2228/* If a redirect is immediately preceded by a number, that number is
2229 * supposed to tell which file descriptor to redirect. This routine
2230 * looks for such preceding numbers. In an ideal world this routine
2231 * needs to handle all the following classes of redirects...
2232 * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo
2233 * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo
2234 * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo
2235 * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo
2236 * A -1 output from this program means no valid number was found, so the
2237 * caller should use the appropriate default for this redirection.
2238 */
2239static int redirect_opt_num(o_string *o)
2240{
2241 int num;
2242
2243 if (o->length==0) return -1;
2244 for(num=0; num<o->length; num++) {
2245 if (!isdigit(*(o->data+num))) {
2246 return -1;
2247 }
2248 }
2249 /* reuse num (and save an int) */
2250 num=atoi(o->data);
2251 b_reset(o);
2252 return num;
2253}
2254
2255FILE *generate_stream_from_list(struct pipe *head)
2256{
2257 FILE *pf;
2258#if 1
2259 int pid, channel[2];
2260 if (pipe(channel)<0) bb_perror_msg_and_die("pipe");
2261#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
2262 pid=fork();
2263#else
2264 pid=vfork();
2265#endif
2266 if (pid<0) {
2267 bb_perror_msg_and_die("fork");
2268 } else if (pid==0) {
2269 close(channel[0]);
2270 if (channel[1] != 1) {
2271 dup2(channel[1],1);
2272 close(channel[1]);
2273 }
2274#if 0
2275#define SURROGATE "surrogate response"
2276 write(1,SURROGATE,sizeof(SURROGATE));
2277 _exit(run_list(head));
2278#else
2279 _exit(run_list_real(head)); /* leaks memory */
2280#endif
2281 }
2282 debug_printf("forked child %d\n",pid);
2283 close(channel[1]);
2284 pf = fdopen(channel[0],"r");
2285 debug_printf("pipe on FILE *%p\n",pf);
2286#else
2287 free_pipe_list(head,0);
2288 pf=popen("echo surrogate response","r");
2289 debug_printf("started fake pipe on FILE *%p\n",pf);
2290#endif
2291 return pf;
2292}
2293
2294/* this version hacked for testing purposes */
2295/* return code is exit status of the process that is run. */
2296static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end)
2297{
2298 int retcode;
2299 o_string result=NULL_O_STRING;
2300 struct p_context inner;
2301 FILE *p;
2302 struct in_str pipe_str;
2303 initialize_context(&inner);
2304
2305 /* recursion to generate command */
2306 retcode = parse_stream(&result, &inner, input, subst_end);
2307 if (retcode != 0) return retcode; /* syntax error or EOF */
2308 done_word(&result, &inner);
2309 done_pipe(&inner, PIPE_SEQ);
2310 b_free(&result);
2311
2312 p=generate_stream_from_list(inner.list_head);
2313 if (p==NULL) return 1;
2314 mark_open(fileno(p));
2315 setup_file_in_str(&pipe_str, p);
2316
2317 /* now send results of command back into original context */
2318 retcode = parse_stream(dest, ctx, &pipe_str, '\0');
2319 /* XXX In case of a syntax error, should we try to kill the child?
2320 * That would be tough to do right, so just read until EOF. */
2321 if (retcode == 1) {
2322 while (b_getch(&pipe_str)!=EOF) { /* discard */ };
2323 }
2324
2325 debug_printf("done reading from pipe, pclose()ing\n");
2326 /* This is the step that wait()s for the child. Should be pretty
2327 * safe, since we just read an EOF from its stdout. We could try
2328 * to better, by using wait(), and keeping track of background jobs
2329 * at the same time. That would be a lot of work, and contrary
2330 * to the KISS philosophy of this program. */
2331 mark_closed(fileno(p));
2332 retcode=pclose(p);
2333 free_pipe_list(inner.list_head,0);
2334 debug_printf("pclosed, retcode=%d\n",retcode);
2335 /* XXX this process fails to trim a single trailing newline */
2336 return retcode;
2337}
2338
2339static int parse_group(o_string *dest, struct p_context *ctx,
2340 struct in_str *input, int ch)
2341{
2342 int rcode, endch=0;
2343 struct p_context sub;
2344 struct child_prog *child = ctx->child;
2345 if (child->argv) {
2346 syntax();
2347 return 1; /* syntax error, groups and arglists don't mix */
2348 }
2349 initialize_context(&sub);
2350 switch(ch) {
2351 case '(': endch=')'; child->subshell=1; break;
2352 case '{': endch='}'; break;
2353 default: syntax(); /* really logic error */
2354 }
2355 rcode=parse_stream(dest,&sub,input,endch);
2356 done_word(dest,&sub); /* finish off the final word in the subcontext */
2357 done_pipe(&sub, PIPE_SEQ); /* and the final command there, too */
2358 child->group = sub.list_head;
2359 return rcode;
2360 /* child remains "open", available for possible redirects */
2361}
2362
2363/* basically useful version until someone wants to get fancier,
2364 * see the bash man page under "Parameter Expansion" */
2365static char *lookup_param(char *src)
2366{
2367 char *p=NULL;
2368 if (src) {
2369 p = getenv(src);
2370 if (!p)
2371 p = get_local_var(src);
2372 }
2373 return p;
2374}
2375
2376/* return code: 0 for OK, 1 for syntax error */
2377static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input)
2378{
2379 int i, advance=0;
2380 char sep[]=" ";
2381 int ch = input->peek(input); /* first character after the $ */
2382 debug_printf("handle_dollar: ch=%c\n",ch);
2383 if (isalpha(ch)) {
2384 b_addchr(dest, SPECIAL_VAR_SYMBOL);
2385 ctx->child->sp++;
2386 while(ch=b_peek(input),isalnum(ch) || ch=='_') {
2387 b_getch(input);
2388 b_addchr(dest,ch);
2389 }
2390 b_addchr(dest, SPECIAL_VAR_SYMBOL);
2391 } else if (isdigit(ch)) {
2392 i = ch-'0'; /* XXX is $0 special? */
2393 if (i<global_argc) {
2394 parse_string(dest, ctx, global_argv[i]); /* recursion */
2395 }
2396 advance = 1;
2397 } else switch (ch) {
2398 case '$':
2399 b_adduint(dest,getpid());
2400 advance = 1;
2401 break;
2402 case '!':
2403 if (last_bg_pid > 0) b_adduint(dest, last_bg_pid);
2404 advance = 1;
2405 break;
2406 case '?':
2407 b_adduint(dest,last_return_code);
2408 advance = 1;
2409 break;
2410 case '#':
2411 b_adduint(dest,global_argc ? global_argc-1 : 0);
2412 advance = 1;
2413 break;
2414 case '{':
2415 b_addchr(dest, SPECIAL_VAR_SYMBOL);
2416 ctx->child->sp++;
2417 b_getch(input);
2418 /* XXX maybe someone will try to escape the '}' */
2419 while(ch=b_getch(input),ch!=EOF && ch!='}') {
2420 b_addchr(dest,ch);
2421 }
2422 if (ch != '}') {
2423 syntax();
2424 return 1;
2425 }
2426 b_addchr(dest, SPECIAL_VAR_SYMBOL);
2427 break;
2428 case '(':
2429 b_getch(input);
2430 process_command_subs(dest, ctx, input, ')');
2431 break;
2432 case '*':
2433 sep[0]=ifs[0];
2434 for (i=1; i<global_argc; i++) {
2435 parse_string(dest, ctx, global_argv[i]);
2436 if (i+1 < global_argc) parse_string(dest, ctx, sep);
2437 }
2438 break;
2439 case '@':
2440 case '-':
2441 case '_':
2442 /* still unhandled, but should be eventually */
2443 bb_error_msg("unhandled syntax: $%c",ch);
2444 return 1;
2445 break;
2446 default:
2447 b_addqchr(dest,'$',dest->quote);
2448 }
2449 /* Eat the character if the flag was set. If the compiler
2450 * is smart enough, we could substitute "b_getch(input);"
2451 * for all the "advance = 1;" above, and also end up with
2452 * a nice size-optimized program. Hah! That'll be the day.
2453 */
2454 if (advance) b_getch(input);
2455 return 0;
2456}
2457
2458int parse_string(o_string *dest, struct p_context *ctx, const char *src)
2459{
2460 struct in_str foo;
2461 setup_string_in_str(&foo, src);
2462 return parse_stream(dest, ctx, &foo, '\0');
2463}
2464
2465/* return code is 0 for normal exit, 1 for syntax error */
2466int parse_stream(o_string *dest, struct p_context *ctx,
2467 struct in_str *input, int end_trigger)
2468{
2469 unsigned int ch, m;
2470 int redir_fd;
2471 redir_type redir_style;
2472 int next;
2473
2474 /* Only double-quote state is handled in the state variable dest->quote.
2475 * A single-quote triggers a bypass of the main loop until its mate is
2476 * found. When recursing, quote state is passed in via dest->quote. */
2477
2478 debug_printf("parse_stream, end_trigger=%d\n",end_trigger);
2479 while ((ch=b_getch(input))!=EOF) {
2480 m = map[ch];
2481 next = (ch == '\n') ? 0 : b_peek(input);
2482 debug_printf("parse_stream: ch=%c (%d) m=%d quote=%d\n",
2483 ch,ch,m,dest->quote);
2484 if (m==0 || ((m==1 || m==2) && dest->quote)) {
2485 b_addqchr(dest, ch, dest->quote);
2486 } else {
2487 if (m==2) { /* unquoted IFS */
2488 if (done_word(dest, ctx)) {
2489 return 1;
2490 }
2491 /* If we aren't performing a substitution, treat a newline as a
2492 * command separator. */
2493 if (end_trigger != '\0' && ch=='\n')
2494 done_pipe(ctx,PIPE_SEQ);
2495 }
2496 if (ch == end_trigger && !dest->quote && ctx->w==RES_NONE) {
2497 debug_printf("leaving parse_stream (triggered)\n");
2498 return 0;
2499 }
2500#if 0
2501 if (ch=='\n') {
2502 /* Yahoo! Time to run with it! */
2503 done_pipe(ctx,PIPE_SEQ);
2504 run_list(ctx->list_head);
2505 initialize_context(ctx);
2506 }
2507#endif
2508 if (m!=2) switch (ch) {
2509 case '#':
2510 if (dest->length == 0 && !dest->quote) {
2511 while(ch=b_peek(input),ch!=EOF && ch!='\n') { b_getch(input); }
2512 } else {
2513 b_addqchr(dest, ch, dest->quote);
2514 }
2515 break;
2516 case '\\':
2517 if (next == EOF) {
2518 syntax();
2519 return 1;
2520 }
2521 b_addqchr(dest, '\\', dest->quote);
2522 b_addqchr(dest, b_getch(input), dest->quote);
2523 break;
2524 case '$':
2525 if (handle_dollar(dest, ctx, input)!=0) return 1;
2526 break;
2527 case '\'':
2528 dest->nonnull = 1;
2529 while(ch=b_getch(input),ch!=EOF && ch!='\'') {
2530 b_addchr(dest,ch);
2531 }
2532 if (ch==EOF) {
2533 syntax();
2534 return 1;
2535 }
2536 break;
2537 case '"':
2538 dest->nonnull = 1;
2539 dest->quote = !dest->quote;
2540 break;
2541 case '`':
2542 process_command_subs(dest, ctx, input, '`');
2543 break;
2544 case '>':
2545 redir_fd = redirect_opt_num(dest);
2546 done_word(dest, ctx);
2547 redir_style=REDIRECT_OVERWRITE;
2548 if (next == '>') {
2549 redir_style=REDIRECT_APPEND;
2550 b_getch(input);
2551 } else if (next == '(') {
2552 syntax(); /* until we support >(list) Process Substitution */
2553 return 1;
2554 }
2555 setup_redirect(ctx, redir_fd, redir_style, input);
2556 break;
2557 case '<':
2558 redir_fd = redirect_opt_num(dest);
2559 done_word(dest, ctx);
2560 redir_style=REDIRECT_INPUT;
2561 if (next == '<') {
2562 redir_style=REDIRECT_HEREIS;
2563 b_getch(input);
2564 } else if (next == '>') {
2565 redir_style=REDIRECT_IO;
2566 b_getch(input);
2567 } else if (next == '(') {
2568 syntax(); /* until we support <(list) Process Substitution */
2569 return 1;
2570 }
2571 setup_redirect(ctx, redir_fd, redir_style, input);
2572 break;
2573 case ';':
2574 done_word(dest, ctx);
2575 done_pipe(ctx,PIPE_SEQ);
2576 break;
2577 case '&':
2578 done_word(dest, ctx);
2579 if (next=='&') {
2580 b_getch(input);
2581 done_pipe(ctx,PIPE_AND);
2582 } else {
2583 done_pipe(ctx,PIPE_BG);
2584 }
2585 break;
2586 case '|':
2587 done_word(dest, ctx);
2588 if (next=='|') {
2589 b_getch(input);
2590 done_pipe(ctx,PIPE_OR);
2591 } else {
2592 /* we could pick up a file descriptor choice here
2593 * with redirect_opt_num(), but bash doesn't do it.
2594 * "echo foo 2| cat" yields "foo 2". */
2595 done_command(ctx);
2596 }
2597 break;
2598 case '(':
2599 case '{':
2600 if (parse_group(dest, ctx, input, ch)!=0) return 1;
2601 break;
2602 case ')':
2603 case '}':
2604 syntax(); /* Proper use of this character caught by end_trigger */
2605 return 1;
2606 break;
2607 default:
2608 syntax(); /* this is really an internal logic error */
2609 return 1;
2610 }
2611 }
2612 }
2613 /* complain if quote? No, maybe we just finished a command substitution
2614 * that was quoted. Example:
2615 * $ echo "`cat foo` plus more"
2616 * and we just got the EOF generated by the subshell that ran "cat foo"
2617 * The only real complaint is if we got an EOF when end_trigger != '\0',
2618 * that is, we were really supposed to get end_trigger, and never got
2619 * one before the EOF. Can't use the standard "syntax error" return code,
2620 * so that parse_stream_outer can distinguish the EOF and exit smoothly. */
2621 debug_printf("leaving parse_stream (EOF)\n");
2622 if (end_trigger != '\0') return -1;
2623 return 0;
2624}
2625
2626void mapset(const unsigned char *set, int code)
2627{
2628 const unsigned char *s;
2629 for (s=set; *s; s++) map[*s] = code;
2630}
2631
2632void update_ifs_map(void)
2633{
2634 /* char *ifs and char map[256] are both globals. */
2635 ifs = getenv("IFS");
2636 if (ifs == NULL) ifs=" \t\n";
2637 /* Precompute a list of 'flow through' behavior so it can be treated
2638 * quickly up front. Computation is necessary because of IFS.
2639 * Special case handling of IFS == " \t\n" is not implemented.
2640 * The map[] array only really needs two bits each, and on most machines
2641 * that would be faster because of the reduced L1 cache footprint.
2642 */
2643 memset(map,0,sizeof(map)); /* most characters flow through always */
2644 mapset("\\$'\"`", 3); /* never flow through */
2645 mapset("<>;&|(){}#", 1); /* flow through if quoted */
2646 mapset(ifs, 2); /* also flow through if quoted */
2647}
2648
2649/* most recursion does not come through here, the exception is
2650 * from builtin_source() */
2651int parse_stream_outer(struct in_str *inp, int flag)
2652{
2653
2654 struct p_context ctx;
2655 o_string temp=NULL_O_STRING;
2656 int rcode;
2657 do {
2658 ctx.type = flag;
2659 initialize_context(&ctx);
2660 update_ifs_map();
2661 if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset(";$&|", 0);
2662 inp->promptmode=1;
2663 rcode = parse_stream(&temp, &ctx, inp, '\n');
2664 if (rcode != 1 && ctx.old_flag != 0) {
2665 syntax();
2666 }
2667 if (rcode != 1 && ctx.old_flag == 0) {
2668 done_word(&temp, &ctx);
2669 done_pipe(&ctx,PIPE_SEQ);
2670 run_list(ctx.list_head);
2671 } else {
2672 if (ctx.old_flag != 0) {
2673 free(ctx.stack);
2674 b_reset(&temp);
2675 }
2676 temp.nonnull = 0;
2677 temp.quote = 0;
2678 inp->p = NULL;
2679 free_pipe_list(ctx.list_head,0);
2680 }
2681 b_free(&temp);
2682 } while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP)); /* loop on syntax errors, return on EOF */
2683 return 0;
2684}
2685
2686static int parse_string_outer(const char *s, int flag)
2687{
2688 struct in_str input;
2689 setup_string_in_str(&input, s);
2690 return parse_stream_outer(&input, flag);
2691}
2692
2693static int parse_file_outer(FILE *f)
2694{
2695 int rcode;
2696 struct in_str input;
2697 setup_file_in_str(&input, f);
2698 rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON);
2699 return rcode;
2700}
2701
2702/* Make sure we have a controlling tty. If we get started under a job
2703 * aware app (like bash for example), make sure we are now in charge so
2704 * we don't fight over who gets the foreground */
2705static void setup_job_control(void)
2706{
2707 static pid_t shell_pgrp;
2708 /* Loop until we are in the foreground. */
2709 while (tcgetpgrp (shell_terminal) != (shell_pgrp = getpgrp ()))
2710 kill (- shell_pgrp, SIGTTIN);
2711
2712 /* Ignore interactive and job-control signals. */
2713 signal(SIGINT, SIG_IGN);
2714 signal(SIGQUIT, SIG_IGN);
2715 signal(SIGTERM, SIG_IGN);
2716 signal(SIGTSTP, SIG_IGN);
2717 signal(SIGTTIN, SIG_IGN);
2718 signal(SIGTTOU, SIG_IGN);
2719 signal(SIGCHLD, SIG_IGN);
2720
2721 /* Put ourselves in our own process group. */
2722 setsid();
2723 shell_pgrp = getpid ();
2724 setpgid (shell_pgrp, shell_pgrp);
2725
2726 /* Grab control of the terminal. */
2727 tcsetpgrp(shell_terminal, shell_pgrp);
2728}
2729
2730int hush_main(int argc, char **argv)
2731{
2732 int opt;
2733 FILE *input;
2734 char **e = environ;
2735
2736 /* XXX what should these be while sourcing /etc/profile? */
2737 global_argc = argc;
2738 global_argv = argv;
2739
2740 /* (re?) initialize globals. Sometimes hush_main() ends up calling
2741 * hush_main(), therefore we cannot rely on the BSS to zero out this
2742 * stuff. Reset these to 0 every time. */
2743 ifs = NULL;
2744 /* map[] is taken care of with call to update_ifs_map() */
2745 fake_mode = 0;
2746 interactive = 0;
2747 close_me_head = NULL;
2748 last_bg_pid = 0;
2749 job_list = NULL;
2750 last_jobid = 0;
2751
2752 /* Initialize some more globals to non-zero values */
2753 set_cwd();
2754#ifdef CONFIG_FEATURE_COMMAND_EDITING
2755 cmdedit_set_initial_prompt();
2756#else
2757 PS1 = NULL;
2758#endif
2759 PS2 = "> ";
2760
2761 /* initialize our shell local variables with the values
2762 * currently living in the environment */
2763 if (e) {
2764 for (; *e; e++)
2765 set_local_var(*e, 2); /* without call putenv() */
2766 }
2767
2768 last_return_code=EXIT_SUCCESS;
2769
2770
2771 if (argv[0] && argv[0][0] == '-') {
2772 debug_printf("\nsourcing /etc/profile\n");
2773 if ((input = fopen("/etc/profile", "r")) != NULL) {
2774 mark_open(fileno(input));
2775 parse_file_outer(input);
2776 mark_closed(fileno(input));
2777 fclose(input);
2778 }
2779 }
2780 input=stdin;
2781
2782 while ((opt = getopt(argc, argv, "c:xif")) > 0) {
2783 switch (opt) {
2784 case 'c':
2785 {
2786 global_argv = argv+optind;
2787 global_argc = argc-optind;
2788 opt = parse_string_outer(optarg, FLAG_PARSE_SEMICOLON);
2789 goto final_return;
2790 }
2791 break;
2792 case 'i':
2793 interactive++;
2794 break;
2795 case 'f':
2796 fake_mode++;
2797 break;
2798 default:
2799#ifndef BB_VER
2800 fprintf(stderr, "Usage: sh [FILE]...\n"
2801 " or: sh -c command [args]...\n\n");
2802 exit(EXIT_FAILURE);
2803#else
2804 bb_show_usage();
2805#endif
2806 }
2807 }
2808 /* A shell is interactive if the `-i' flag was given, or if all of
2809 * the following conditions are met:
2810 * no -c command
2811 * no arguments remaining or the -s flag given
2812 * standard input is a terminal
2813 * standard output is a terminal
2814 * Refer to Posix.2, the description of the `sh' utility. */
2815 if (argv[optind]==NULL && input==stdin &&
2816 isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
2817 interactive++;
2818 }
2819
2820 debug_printf("\ninteractive=%d\n", interactive);
2821 if (interactive) {
2822 /* Looks like they want an interactive shell */
2823#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
2824 printf( "\n\n" BB_BANNER " hush - the humble shell v0.01 (testing)\n");
2825 printf( "Enter 'help' for a list of built-in commands.\n\n");
2826#endif
2827 setup_job_control();
2828 }
2829
2830 if (argv[optind]==NULL) {
2831 opt=parse_file_outer(stdin);
2832 goto final_return;
2833 }
2834
2835 debug_printf("\nrunning script '%s'\n", argv[optind]);
2836 global_argv = argv+optind;
2837 global_argc = argc-optind;
2838 input = bb_xfopen(argv[optind], "r");
2839 opt = parse_file_outer(input);
2840
2841#ifdef CONFIG_FEATURE_CLEAN_UP
2842 fclose(input);
2843 if (cwd && cwd != bb_msg_unknown)
2844 free((char*)cwd);
2845 {
2846 struct variables *cur, *tmp;
2847 for(cur = top_vars; cur; cur = tmp) {
2848 tmp = cur->next;
2849 if (!cur->flg_read_only) {
2850 free(cur->name);
2851 free(cur->value);
2852 free(cur);
2853 }
2854 }
2855 }
2856#endif
2857
2858final_return:
2859 return(opt?opt:last_return_code);
2860}
2861
2862static char *insert_var_value(char *inp)
2863{
2864 int res_str_len = 0;
2865 int len;
2866 int done = 0;
2867 char *p, *p1, *res_str = NULL;
2868
2869 while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) {
2870 if (p != inp) {
2871 len = p - inp;
2872 res_str = xrealloc(res_str, (res_str_len + len));
2873 strncpy((res_str + res_str_len), inp, len);
2874 res_str_len += len;
2875 }
2876 inp = ++p;
2877 p = strchr(inp, SPECIAL_VAR_SYMBOL);
2878 *p = '\0';
2879 if ((p1 = lookup_param(inp))) {
2880 len = res_str_len + strlen(p1);
2881 res_str = xrealloc(res_str, (1 + len));
2882 strcpy((res_str + res_str_len), p1);
2883 res_str_len = len;
2884 }
2885 *p = SPECIAL_VAR_SYMBOL;
2886 inp = ++p;
2887 done = 1;
2888 }
2889 if (done) {
2890 res_str = xrealloc(res_str, (1 + res_str_len + strlen(inp)));
2891 strcpy((res_str + res_str_len), inp);
2892 while ((p = strchr(res_str, '\n'))) {
2893 *p = ' ';
2894 }
2895 }
2896 return (res_str == NULL) ? inp : res_str;
2897}
2898
2899static char **make_list_in(char **inp, char *name)
2900{
2901 int len, i;
2902 int name_len = strlen(name);
2903 int n = 0;
2904 char **list;
2905 char *p1, *p2, *p3;
2906
2907 /* create list of variable values */
2908 list = xmalloc(sizeof(*list));
2909 for (i = 0; inp[i]; i++) {
2910 p3 = insert_var_value(inp[i]);
2911 p1 = p3;
2912 while (*p1) {
2913 if ((*p1 == ' ')) {
2914 p1++;
2915 continue;
2916 }
2917 if ((p2 = strchr(p1, ' '))) {
2918 len = p2 - p1;
2919 } else {
2920 len = strlen(p1);
2921 p2 = p1 + len;
2922 }
2923 /* we use n + 2 in realloc for list,because we add
2924 * new element and then we will add NULL element */
2925 list = xrealloc(list, sizeof(*list) * (n + 2));
2926 list[n] = xmalloc(2 + name_len + len);
2927 strcpy(list[n], name);
2928 strcat(list[n], "=");
2929 strncat(list[n], p1, len);
2930 list[n++][name_len + len + 1] = '\0';
2931 p1 = p2;
2932 }
2933 if (p3 != inp[i]) free(p3);
2934 }
2935 list[n] = NULL;
2936 return list;
2937}
2938
2939/* Make new string for parser */
2940static char * make_string(char ** inp)
2941{
2942 char *p;
2943 char *str = NULL;
2944 int n;
2945 int len = 2;
2946
2947 for (n = 0; inp[n]; n++) {
2948 p = insert_var_value(inp[n]);
2949 str = xrealloc(str, (len + strlen(p)));
2950 if (n) {
2951 strcat(str, " ");
2952 } else {
2953 *str = '\0';
2954 }
2955 strcat(str, p);
2956 len = strlen(str) + 3;
2957 if (p != inp[n]) free(p);
2958 }
2959 len = strlen(str);
2960 *(str + len) = '\n';
2961 *(str + len + 1) = '\0';
2962 return str;
2963}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * lash -- the BusyBox Lame-Ass SHell
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Based in part on ladsh.c by Michael K. Johnson and Erik W. Troan, which is
8 * under the following liberal license: "We have placed this source code in the
9 * public domain. Use it in any project, free or commercial."
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27/* This shell's parsing engine is officially at a dead-end. Future
28 * work shell work should be done using hush, msh, or ash. This is
29 * still a very useful, small shell -- it just don't need any more
30 * features beyond what it already has...
31 */
32
33//For debugging/development on the shell only...
34//#define DEBUG_SHELL
35
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <ctype.h>
40#include <errno.h>
41#include <fcntl.h>
42#include <signal.h>
43#include <string.h>
44#include <sys/ioctl.h>
45#include <sys/wait.h>
46#include <unistd.h>
47#include <getopt.h>
48#include <termios.h>
49#include "busybox.h"
50#include "cmdedit.h"
51
52#ifdef CONFIG_LOCALE_SUPPORT
53#include <locale.h>
54#endif
55
56#include <glob.h>
57#define expand_t glob_t
58
59/* Always enable for the moment... */
60#define CONFIG_LASH_PIPE_N_REDIRECTS
61#define CONFIG_LASH_JOB_CONTROL
62
63static const int MAX_READ = 128; /* size of input buffer for `read' builtin */
64#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
65
66
67#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
68enum redir_type { REDIRECT_INPUT, REDIRECT_OVERWRITE,
69 REDIRECT_APPEND
70};
71#endif
72
73static const unsigned int DEFAULT_CONTEXT=0x1;
74static const unsigned int IF_TRUE_CONTEXT=0x2;
75static const unsigned int IF_FALSE_CONTEXT=0x4;
76static const unsigned int THEN_EXP_CONTEXT=0x8;
77static const unsigned int ELSE_EXP_CONTEXT=0x10;
78
79#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
80struct redir_struct {
81 enum redir_type type; /* type of redirection */
82 int fd; /* file descriptor being redirected */
83 char *filename; /* file to redirect fd to */
84};
85#endif
86
87struct child_prog {
88 pid_t pid; /* 0 if exited */
89 char **argv; /* program name and arguments */
90 int num_redirects; /* elements in redirection array */
91 int is_stopped; /* is the program currently running? */
92 struct job *family; /* pointer back to the child's parent job */
93#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
94 struct redir_struct *redirects; /* I/O redirects */
95#endif
96};
97
98struct jobset {
99 struct job *head; /* head of list of running jobs */
100 struct job *fg; /* current foreground job */
101};
102
103struct job {
104 int jobid; /* job number */
105 int num_progs; /* total number of programs in job */
106 int running_progs; /* number of programs running */
107 char *text; /* name of job */
108 char *cmdbuf; /* buffer various argv's point into */
109 pid_t pgrp; /* process group ID for the job */
110 struct child_prog *progs; /* array of programs in job */
111 struct job *next; /* to track background commands */
112 int stopped_progs; /* number of programs alive, but stopped */
113 unsigned int job_context; /* bitmask defining current context */
114 struct jobset *job_list;
115};
116
117struct built_in_command {
118 char *cmd; /* name */
119 char *descr; /* description */
120 int (*function) (struct child_prog *); /* function ptr */
121};
122
123struct close_me {
124 int fd;
125 struct close_me *next;
126};
127
128/* function prototypes for builtins */
129static int builtin_cd(struct child_prog *cmd);
130static int builtin_exec(struct child_prog *cmd);
131static int builtin_exit(struct child_prog *cmd);
132static int builtin_fg_bg(struct child_prog *cmd);
133static int builtin_help(struct child_prog *cmd);
134static int builtin_jobs(struct child_prog *dummy);
135static int builtin_pwd(struct child_prog *dummy);
136static int builtin_export(struct child_prog *cmd);
137static int builtin_source(struct child_prog *cmd);
138static int builtin_unset(struct child_prog *cmd);
139static int builtin_read(struct child_prog *cmd);
140
141
142/* function prototypes for shell stuff */
143static void mark_open(int fd);
144static void mark_closed(int fd);
145static void close_all(void);
146static void checkjobs(struct jobset *job_list);
147static void remove_job(struct jobset *j_list, struct job *job);
148static int get_command(FILE * source, char *command);
149static int parse_command(char **command_ptr, struct job *job, int *inbg);
150static int run_command(struct job *newjob, int inbg, int outpipe[2]);
151static int pseudo_exec(struct child_prog *cmd) __attribute__ ((noreturn));
152static int busy_loop(FILE * input);
153
154
155/* Table of built-in functions (these are non-forking builtins, meaning they
156 * can change global variables in the parent shell process but they will not
157 * work with pipes and redirects; 'unset foo | whatever' will not work) */
158static struct built_in_command bltins[] = {
159 {"bg", "Resume a job in the background", builtin_fg_bg},
160 {"cd", "Change working directory", builtin_cd},
161 {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
162 {"exit", "Exit from shell()", builtin_exit},
163 {"fg", "Bring job into the foreground", builtin_fg_bg},
164 {"jobs", "Lists the active jobs", builtin_jobs},
165 {"export", "Set environment variable", builtin_export},
166 {"unset", "Unset environment variable", builtin_unset},
167 {"read", "Input environment variable", builtin_read},
168 {".", "Source-in and run commands in a file", builtin_source},
169 /* to do: add ulimit */
170 {NULL, NULL, NULL}
171};
172
173/* Table of forking built-in functions (things that fork cannot change global
174 * variables in the parent process, such as the current working directory) */
175static struct built_in_command bltins_forking[] = {
176 {"pwd", "Print current directory", builtin_pwd},
177 {"help", "List shell built-in commands", builtin_help},
178 {NULL, NULL, NULL}
179};
180
181
182static int shell_context; /* Type prompt trigger (PS1 or PS2) */
183
184
185/* Globals that are static to this file */
186static const char *cwd;
187static char *local_pending_command = NULL;
188static struct jobset job_list = { NULL, NULL };
189static int argc;
190static char **argv;
191static struct close_me *close_me_head;
192static int last_return_code;
193static int last_bg_pid;
194static unsigned int last_jobid;
195static int shell_terminal;
196static char *PS1;
197static char *PS2 = "> ";
198
199
200#ifdef DEBUG_SHELL
201static inline void debug_printf(const char *format, ...)
202{
203 va_list args;
204 va_start(args, format);
205 vfprintf(stderr, format, args);
206 va_end(args);
207}
208#else
209static inline void debug_printf(const char *format, ...) { }
210#endif
211
212/*
213 Most builtins need access to the struct child_prog that has
214 their arguments, previously coded as cmd->progs[0]. That coding
215 can exhibit a bug, if the builtin is not the first command in
216 a pipeline: "echo foo | exec sort" will attempt to exec foo.
217
218builtin previous use notes
219------ ----------------- ---------
220cd cmd->progs[0]
221exec cmd->progs[0] squashed bug: didn't look for applets or forking builtins
222exit cmd->progs[0]
223fg_bg cmd->progs[0], job_list->head, job_list->fg
224help 0
225jobs job_list->head
226pwd 0
227export cmd->progs[0]
228source cmd->progs[0]
229unset cmd->progs[0]
230read cmd->progs[0]
231
232I added "struct job *family;" to struct child_prog,
233and switched API to builtin_foo(struct child_prog *child);
234So cmd->text becomes child->family->text
235 cmd->job_context becomes child->family->job_context
236 cmd->progs[0] becomes *child
237 job_list becomes child->family->job_list
238 */
239
240/* built-in 'cd <path>' handler */
241static int builtin_cd(struct child_prog *child)
242{
243 char *newdir;
244
245 if (child->argv[1] == NULL)
246 newdir = getenv("HOME");
247 else
248 newdir = child->argv[1];
249 if (chdir(newdir)) {
250 printf("cd: %s: %m\n", newdir);
251 return EXIT_FAILURE;
252 }
253 cwd = xgetcwd((char *)cwd);
254 if (!cwd)
255 cwd = bb_msg_unknown;
256 return EXIT_SUCCESS;
257}
258
259/* built-in 'exec' handler */
260static int builtin_exec(struct child_prog *child)
261{
262 if (child->argv[1] == NULL)
263 return EXIT_SUCCESS; /* Really? */
264 child->argv++;
265 close_all();
266 pseudo_exec(child);
267 /* never returns */
268}
269
270/* built-in 'exit' handler */
271static int builtin_exit(struct child_prog *child)
272{
273 if (child->argv[1] == NULL)
274 exit(EXIT_SUCCESS);
275
276 exit (atoi(child->argv[1]));
277}
278
279/* built-in 'fg' and 'bg' handler */
280static int builtin_fg_bg(struct child_prog *child)
281{
282 int i, jobnum;
283 struct job *job=NULL;
284
285 /* If they gave us no args, assume they want the last backgrounded task */
286 if (!child->argv[1]) {
287 for (job = child->family->job_list->head; job; job = job->next) {
288 if (job->jobid == last_jobid) {
289 break;
290 }
291 }
292 if (!job) {
293 bb_error_msg("%s: no current job", child->argv[0]);
294 return EXIT_FAILURE;
295 }
296 } else {
297 if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) {
298 bb_error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]);
299 return EXIT_FAILURE;
300 }
301 for (job = child->family->job_list->head; job; job = job->next) {
302 if (job->jobid == jobnum) {
303 break;
304 }
305 }
306 if (!job) {
307 bb_error_msg("%s: %d: no such job", child->argv[0], jobnum);
308 return EXIT_FAILURE;
309 }
310 }
311
312 if (*child->argv[0] == 'f') {
313 /* Put the job into the foreground. */
314 tcsetpgrp(shell_terminal, job->pgrp);
315
316 child->family->job_list->fg = job;
317 }
318
319 /* Restart the processes in the job */
320 for (i = 0; i < job->num_progs; i++)
321 job->progs[i].is_stopped = 0;
322
323 job->stopped_progs = 0;
324
325 if ( (i=kill(- job->pgrp, SIGCONT)) < 0) {
326 if (i == ESRCH) {
327 remove_job(&job_list, job);
328 } else {
329 bb_perror_msg("kill (SIGCONT)");
330 }
331 }
332
333 return EXIT_SUCCESS;
334}
335
336/* built-in 'help' handler */
337static int builtin_help(struct child_prog *dummy)
338{
339 struct built_in_command *x;
340
341 printf("\nBuilt-in commands:\n");
342 printf("-------------------\n");
343 for (x = bltins; x->cmd; x++) {
344 if (x->descr==NULL)
345 continue;
346 printf("%s\t%s\n", x->cmd, x->descr);
347 }
348 for (x = bltins_forking; x->cmd; x++) {
349 if (x->descr==NULL)
350 continue;
351 printf("%s\t%s\n", x->cmd, x->descr);
352 }
353 printf("\n\n");
354 return EXIT_SUCCESS;
355}
356
357/* built-in 'jobs' handler */
358static int builtin_jobs(struct child_prog *child)
359{
360 struct job *job;
361 char *status_string;
362
363 for (job = child->family->job_list->head; job; job = job->next) {
364 if (job->running_progs == job->stopped_progs)
365 status_string = "Stopped";
366 else
367 status_string = "Running";
368
369 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text);
370 }
371 return EXIT_SUCCESS;
372}
373
374
375/* built-in 'pwd' handler */
376static int builtin_pwd(struct child_prog *dummy)
377{
378 cwd = xgetcwd((char *)cwd);
379 if (!cwd)
380 cwd = bb_msg_unknown;
381 puts(cwd);
382 return EXIT_SUCCESS;
383}
384
385/* built-in 'export VAR=value' handler */
386static int builtin_export(struct child_prog *child)
387{
388 int res;
389 char *v = child->argv[1];
390
391 if (v == NULL) {
392 char **e;
393 for (e = environ; *e; e++) {
394 puts(*e);
395 }
396 return 0;
397 }
398 res = putenv(v);
399 if (res)
400 fprintf(stderr, "export: %m\n");
401#ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
402 if (strncmp(v, "PS1=", 4)==0)
403 PS1 = getenv("PS1");
404#endif
405
406#ifdef CONFIG_LOCALE_SUPPORT
407 if(strncmp(v, "LC_ALL=", 7)==0)
408 setlocale(LC_ALL, getenv("LC_ALL"));
409 if(strncmp(v, "LC_CTYPE=", 9)==0)
410 setlocale(LC_CTYPE, getenv("LC_CTYPE"));
411#endif
412
413 return (res);
414}
415
416/* built-in 'read VAR' handler */
417static int builtin_read(struct child_prog *child)
418{
419 int res = 0, len, newlen;
420 char *s;
421 char string[MAX_READ];
422
423 if (child->argv[1]) {
424 /* argument (VAR) given: put "VAR=" into buffer */
425 safe_strncpy(string, child->argv[1], MAX_READ-1);
426 len = strlen(string);
427 string[len++] = '=';
428 string[len] = '\0';
429 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
430 newlen = strlen(string);
431 if(newlen > len)
432 string[--newlen] = '\0'; /* chomp trailing newline */
433 /*
434 ** string should now contain "VAR=<value>"
435 ** copy it (putenv() won't do that, so we must make sure
436 ** the string resides in a static buffer!)
437 */
438 res = -1;
439 if((s = strdup(string)))
440 res = putenv(s);
441 if (res)
442 fprintf(stderr, "read: %m\n");
443 }
444 else
445 fgets(string, sizeof(string), stdin);
446
447 return (res);
448}
449
450/* Built-in '.' handler (read-in and execute commands from file) */
451static int builtin_source(struct child_prog *child)
452{
453 FILE *input;
454 int status;
455 int fd;
456
457 if (child->argv[1] == NULL)
458 return EXIT_FAILURE;
459
460 input = fopen(child->argv[1], "r");
461 if (!input) {
462 printf( "Couldn't open file '%s'\n", child->argv[1]);
463 return EXIT_FAILURE;
464 }
465
466 fd=fileno(input);
467 mark_open(fd);
468 /* Now run the file */
469 status = busy_loop(input);
470 fclose(input);
471 mark_closed(fd);
472 return (status);
473}
474
475/* built-in 'unset VAR' handler */
476static int builtin_unset(struct child_prog *child)
477{
478 if (child->argv[1] == NULL) {
479 printf( "unset: parameter required.\n");
480 return EXIT_FAILURE;
481 }
482 unsetenv(child->argv[1]);
483 return EXIT_SUCCESS;
484}
485
486static void mark_open(int fd)
487{
488 struct close_me *new = xmalloc(sizeof(struct close_me));
489 new->fd = fd;
490 new->next = close_me_head;
491 close_me_head = new;
492}
493
494static void mark_closed(int fd)
495{
496 struct close_me *tmp;
497 if (close_me_head == NULL || close_me_head->fd != fd)
498 bb_error_msg_and_die("corrupt close_me");
499 tmp = close_me_head;
500 close_me_head = close_me_head->next;
501 free(tmp);
502}
503
504static void close_all()
505{
506 struct close_me *c, *tmp;
507 for (c=close_me_head; c; c=tmp) {
508 close(c->fd);
509 tmp=c->next;
510 free(c);
511 }
512 close_me_head = NULL;
513}
514
515
516#ifdef CONFIG_LASH_JOB_CONTROL
517/* free up all memory from a job */
518static void free_job(struct job *cmd)
519{
520 int i;
521 struct jobset *keep;
522
523 for (i = 0; i < cmd->num_progs; i++) {
524 free(cmd->progs[i].argv);
525#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
526 if (cmd->progs[i].redirects)
527 free(cmd->progs[i].redirects);
528#endif
529 }
530 free(cmd->progs);
531 free(cmd->text);
532 free(cmd->cmdbuf);
533 keep = cmd->job_list;
534 memset(cmd, 0, sizeof(struct job));
535 cmd->job_list = keep;
536}
537
538/* remove a job from a jobset */
539static void remove_job(struct jobset *j_list, struct job *job)
540{
541 struct job *prevjob;
542
543 free_job(job);
544 if (job == j_list->head) {
545 j_list->head = job->next;
546 } else {
547 prevjob = j_list->head;
548 while (prevjob->next != job)
549 prevjob = prevjob->next;
550 prevjob->next = job->next;
551 }
552
553 if (j_list->head)
554 last_jobid = j_list->head->jobid;
555 else
556 last_jobid = 0;
557
558 free(job);
559}
560
561/* Checks to see if any background processes have exited -- if they
562 have, figure out why and see if a job has completed */
563static void checkjobs(struct jobset *j_list)
564{
565 struct job *job;
566 pid_t childpid;
567 int status;
568 int prognum = 0;
569
570 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
571 for (job = j_list->head; job; job = job->next) {
572 prognum = 0;
573 while (prognum < job->num_progs &&
574 job->progs[prognum].pid != childpid) prognum++;
575 if (prognum < job->num_progs)
576 break;
577 }
578
579 /* This happens on backticked commands */
580 if(job==NULL)
581 return;
582
583 if (WIFEXITED(status) || WIFSIGNALED(status)) {
584 /* child exited */
585 job->running_progs--;
586 job->progs[prognum].pid = 0;
587
588 if (!job->running_progs) {
589 printf(JOB_STATUS_FORMAT, job->jobid, "Done", job->text);
590 last_jobid=0;
591 remove_job(j_list, job);
592 }
593 } else {
594 /* child stopped */
595 job->stopped_progs++;
596 job->progs[prognum].is_stopped = 1;
597
598#if 0
599 /* Printing this stuff is a pain, since it tends to
600 * overwrite the prompt an inconveinient moments. So
601 * don't do that. */
602 if (job->stopped_progs == job->num_progs) {
603 printf(JOB_STATUS_FORMAT, job->jobid, "Stopped",
604 job->text);
605 }
606#endif
607 }
608 }
609
610 if (childpid == -1 && errno != ECHILD)
611 bb_perror_msg("waitpid");
612}
613#else
614static void checkjobs(struct jobset *j_list)
615{
616}
617static void free_job(struct job *cmd)
618{
619}
620static void remove_job(struct jobset *j_list, struct job *job)
621{
622}
623#endif
624
625#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
626/* squirrel != NULL means we squirrel away copies of stdin, stdout,
627 * and stderr if they are redirected. */
628static int setup_redirects(struct child_prog *prog, int squirrel[])
629{
630 int i;
631 int openfd;
632 int mode = O_RDONLY;
633 struct redir_struct *redir = prog->redirects;
634
635 for (i = 0; i < prog->num_redirects; i++, redir++) {
636 switch (redir->type) {
637 case REDIRECT_INPUT:
638 mode = O_RDONLY;
639 break;
640 case REDIRECT_OVERWRITE:
641 mode = O_WRONLY | O_CREAT | O_TRUNC;
642 break;
643 case REDIRECT_APPEND:
644 mode = O_WRONLY | O_CREAT | O_APPEND;
645 break;
646 }
647
648 openfd = open(redir->filename, mode, 0666);
649 if (openfd < 0) {
650 /* this could get lost if stderr has been redirected, but
651 bash and ash both lose it as well (though zsh doesn't!) */
652 bb_perror_msg("error opening %s", redir->filename);
653 return 1;
654 }
655
656 if (openfd != redir->fd) {
657 if (squirrel && redir->fd < 3) {
658 squirrel[redir->fd] = dup(redir->fd);
659 fcntl (squirrel[redir->fd], F_SETFD, FD_CLOEXEC);
660 }
661 dup2(openfd, redir->fd);
662 close(openfd);
663 }
664 }
665
666 return 0;
667}
668
669static void restore_redirects(int squirrel[])
670{
671 int i, fd;
672 for (i=0; i<3; i++) {
673 fd = squirrel[i];
674 if (fd != -1) {
675 /* No error checking. I sure wouldn't know what
676 * to do with an error if I found one! */
677 dup2(fd, i);
678 close(fd);
679 }
680 }
681}
682#else
683static inline int setup_redirects(struct child_prog *prog, int squirrel[])
684{
685 return 0;
686}
687static inline void restore_redirects(int squirrel[])
688{
689}
690#endif
691
692static inline void cmdedit_set_initial_prompt(void)
693{
694#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
695 PS1 = NULL;
696#else
697 PS1 = getenv("PS1");
698 if(PS1==0)
699 PS1 = "\\w \\$ ";
700#endif
701}
702
703static inline void setup_prompt_string(char **prompt_str)
704{
705#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
706 /* Set up the prompt */
707 if (shell_context == 0) {
708 free(PS1);
709 PS1=xmalloc(strlen(cwd)+4);
710 sprintf(PS1, "%s %s", cwd, ( geteuid() != 0 ) ? "$ ":"# ");
711 *prompt_str = PS1;
712 } else {
713 *prompt_str = PS2;
714 }
715#else
716 *prompt_str = (shell_context==0)? PS1 : PS2;
717#endif
718}
719
720static int get_command(FILE * source, char *command)
721{
722 char *prompt_str;
723
724 if (source == NULL) {
725 if (local_pending_command) {
726 /* a command specified (-c option): return it & mark it done */
727 strcpy(command, local_pending_command);
728 free(local_pending_command);
729 local_pending_command = NULL;
730 return 0;
731 }
732 return 1;
733 }
734
735 if (source == stdin) {
736 setup_prompt_string(&prompt_str);
737
738#ifdef CONFIG_FEATURE_COMMAND_EDITING
739 /*
740 ** enable command line editing only while a command line
741 ** is actually being read; otherwise, we'll end up bequeathing
742 ** atexit() handlers and other unwanted stuff to our
743 ** child processes (rob@sysgo.de)
744 */
745 cmdedit_read_input(prompt_str, command);
746 return 0;
747#else
748 fputs(prompt_str, stdout);
749#endif
750 }
751
752 if (!fgets(command, BUFSIZ - 2, source)) {
753 if (source == stdin)
754 printf("\n");
755 return 1;
756 }
757
758 return 0;
759}
760
761static char* itoa(register int i)
762{
763 static char a[7]; /* Max 7 ints */
764 register char *b = a + sizeof(a) - 1;
765 int sign = (i < 0);
766
767 if (sign)
768 i = -i;
769 *b = 0;
770 do
771 {
772 *--b = '0' + (i % 10);
773 i /= 10;
774 }
775 while (i);
776 if (sign)
777 *--b = '-';
778 return b;
779}
780
781char * strsep_space( char *string, int * ix)
782{
783 char *token, *begin;
784
785 begin = string;
786
787 /* Short circuit the trivial case */
788 if ( !string || ! string[*ix])
789 return NULL;
790
791 /* Find the end of the token. */
792 while( string && string[*ix] && !isspace(string[*ix]) ) {
793 (*ix)++;
794 }
795
796 /* Find the end of any whitespace trailing behind
797 * the token and let that be part of the token */
798 while( string && string[*ix] && isspace(string[*ix]) ) {
799 (*ix)++;
800 }
801
802 if (! string && *ix==0) {
803 /* Nothing useful was found */
804 return NULL;
805 }
806
807 token = xmalloc(*ix+1);
808 token[*ix] = '\0';
809 strncpy(token, string, *ix);
810
811 return token;
812}
813
814static int expand_arguments(char *command)
815{
816 int total_length=0, length, i, retval, ix = 0;
817 expand_t expand_result;
818 char *tmpcmd, *cmd, *cmd_copy;
819 char *src, *dst, *var;
820 const char *out_of_space = "out of space during expansion";
821 int flags = GLOB_NOCHECK
822#ifdef GLOB_BRACE
823 | GLOB_BRACE
824#endif
825#ifdef GLOB_TILDE
826 | GLOB_TILDE
827#endif
828 ;
829
830 /* get rid of the terminating \n */
831 chomp(command);
832
833 /* Fix up escape sequences to be the Real Thing(tm) */
834 while( command && command[ix]) {
835 if (command[ix] == '\\') {
836 const char *tmp = command+ix+1;
837 command[ix] = bb_process_escape_sequence( &tmp );
838 memmove(command+ix + 1, tmp, strlen(tmp)+1);
839 }
840 ix++;
841 }
842 /* Use glob and then fixup environment variables and such */
843
844 /* It turns out that glob is very stupid. We have to feed it one word at a
845 * time since it can't cope with a full string. Here we convert command
846 * (char*) into cmd (char**, one word per string) */
847
848 /* We need a clean copy, so strsep can mess up the copy while
849 * we write stuff into the original (in a minute) */
850 cmd = cmd_copy = bb_xstrdup(command);
851 *command = '\0';
852 for (ix = 0, tmpcmd = cmd;
853 (tmpcmd = strsep_space(cmd, &ix)) != NULL; cmd += ix, ix=0) {
854 if (*tmpcmd == '\0')
855 break;
856 /* we need to trim() the result for glob! */
857 trim(tmpcmd);
858 retval = glob(tmpcmd, flags, NULL, &expand_result);
859 free(tmpcmd); /* Free mem allocated by strsep_space */
860 if (retval == GLOB_NOSPACE) {
861 /* Mem may have been allocated... */
862 globfree (&expand_result);
863 bb_error_msg(out_of_space);
864 return FALSE;
865 } else if (retval != 0) {
866 /* Some other error. GLOB_NOMATCH shouldn't
867 * happen because of the GLOB_NOCHECK flag in
868 * the glob call. */
869 bb_error_msg("syntax error");
870 return FALSE;
871 } else {
872 /* Convert from char** (one word per string) to a simple char*,
873 * but don't overflow command which is BUFSIZ in length */
874 for (i=0; i < expand_result.gl_pathc; i++) {
875 length=strlen(expand_result.gl_pathv[i]);
876 if (total_length+length+1 >= BUFSIZ) {
877 bb_error_msg(out_of_space);
878 return FALSE;
879 }
880 strcat(command+total_length, " ");
881 total_length+=1;
882 strcat(command+total_length, expand_result.gl_pathv[i]);
883 total_length+=length;
884 }
885 globfree (&expand_result);
886 }
887 }
888 free(cmd_copy);
889 trim(command);
890
891 /* Now do the shell variable substitutions which
892 * wordexp can't do for us, namely $? and $! */
893 src = command;
894 while((dst = strchr(src,'$')) != NULL){
895 var = NULL;
896 switch(*(dst+1)) {
897 case '?':
898 var = itoa(last_return_code);
899 break;
900 case '!':
901 if (last_bg_pid==-1)
902 *(var)='\0';
903 else
904 var = itoa(last_bg_pid);
905 break;
906 /* Everything else like $$, $#, $[0-9], etc. should all be
907 * expanded by wordexp(), so we can in theory skip that stuff
908 * here, but just to be on the safe side (i.e., since uClibc
909 * wordexp doesn't do this stuff yet), lets leave it in for
910 * now. */
911 case '$':
912 var = itoa(getpid());
913 break;
914 case '#':
915 var = itoa(argc-1);
916 break;
917 case '0':case '1':case '2':case '3':case '4':
918 case '5':case '6':case '7':case '8':case '9':
919 {
920 int ixx=*(dst+1)-48+1;
921 if (ixx >= argc) {
922 var='\0';
923 } else {
924 var = argv[ixx];
925 }
926 }
927 break;
928
929 }
930 if (var) {
931 /* a single character construction was found, and
932 * already handled in the case statement */
933 src=dst+2;
934 } else {
935 /* Looks like an environment variable */
936 char delim_hold;
937 int num_skip_chars=0;
938 int dstlen = strlen(dst);
939 /* Is this a ${foo} type variable? */
940 if (dstlen >=2 && *(dst+1) == '{') {
941 src=strchr(dst+1, '}');
942 num_skip_chars=1;
943 } else {
944 src=dst+1;
945 while(isalnum(*src) || *src=='_') src++;
946 }
947 if (src == NULL) {
948 src = dst+dstlen;
949 }
950 delim_hold=*src;
951 *src='\0'; /* temporary */
952 var = getenv(dst + 1 + num_skip_chars);
953 *src=delim_hold;
954 src += num_skip_chars;
955 }
956 if (var == NULL) {
957 /* Seems we got an un-expandable variable. So delete it. */
958 var = "";
959 }
960 {
961 int subst_len = strlen(var);
962 int trail_len = strlen(src);
963 if (dst+subst_len+trail_len >= command+BUFSIZ) {
964 bb_error_msg(out_of_space);
965 return FALSE;
966 }
967 /* Move stuff to the end of the string to accommodate
968 * filling the created gap with the new stuff */
969 memmove(dst+subst_len, src, trail_len+1);
970 /* Now copy in the new stuff */
971 memcpy(dst, var, subst_len);
972 src = dst+subst_len;
973 }
974 }
975
976 return TRUE;
977}
978
979/* Return cmd->num_progs as 0 if no command is present (e.g. an empty
980 line). If a valid command is found, command_ptr is set to point to
981 the beginning of the next command (if the original command had more
982 then one job associated with it) or NULL if no more commands are
983 present. */
984static int parse_command(char **command_ptr, struct job *job, int *inbg)
985{
986 char *command;
987 char *return_command = NULL;
988 char *src, *buf;
989 int argc_l = 0;
990 int done = 0;
991 int argv_alloced;
992 int saw_quote = 0;
993 char quote = '\0';
994 int count;
995 struct child_prog *prog;
996#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
997 int i;
998 char *chptr;
999#endif
1000
1001 /* skip leading white space */
1002 while (**command_ptr && isspace(**command_ptr))
1003 (*command_ptr)++;
1004
1005 /* this handles empty lines or leading '#' characters */
1006 if (!**command_ptr || (**command_ptr == '#')) {
1007 job->num_progs=0;
1008 return 0;
1009 }
1010
1011 *inbg = 0;
1012 job->num_progs = 1;
1013 job->progs = xmalloc(sizeof(*job->progs));
1014
1015 /* We set the argv elements to point inside of this string. The
1016 memory is freed by free_job(). Allocate twice the original
1017 length in case we need to quote every single character.
1018
1019 Getting clean memory relieves us of the task of NULL
1020 terminating things and makes the rest of this look a bit
1021 cleaner (though it is, admittedly, a tad less efficient) */
1022 job->cmdbuf = command = xcalloc(2*strlen(*command_ptr) + 1, sizeof(char));
1023 job->text = NULL;
1024
1025 prog = job->progs;
1026 prog->num_redirects = 0;
1027 prog->is_stopped = 0;
1028 prog->family = job;
1029#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
1030 prog->redirects = NULL;
1031#endif
1032
1033 argv_alloced = 5;
1034 prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced);
1035 prog->argv[0] = job->cmdbuf;
1036
1037 buf = command;
1038 src = *command_ptr;
1039 while (*src && !done) {
1040 if (quote == *src) {
1041 quote = '\0';
1042 } else if (quote) {
1043 if (*src == '\\') {
1044 src++;
1045 if (!*src) {
1046 bb_error_msg("character expected after \\");
1047 free_job(job);
1048 return 1;
1049 }
1050
1051 /* in shell, "\'" should yield \' */
1052 if (*src != quote) {
1053 *buf++ = '\\';
1054 *buf++ = '\\';
1055 }
1056 } else if (*src == '*' || *src == '?' || *src == '[' ||
1057 *src == ']') *buf++ = '\\';
1058 *buf++ = *src;
1059 } else if (isspace(*src)) {
1060 if (*prog->argv[argc_l] || saw_quote) {
1061 buf++, argc_l++;
1062 /* +1 here leaves room for the NULL which ends argv */
1063 if ((argc_l + 1) == argv_alloced) {
1064 argv_alloced += 5;
1065 prog->argv = xrealloc(prog->argv,
1066 sizeof(*prog->argv) *
1067 argv_alloced);
1068 }
1069 prog->argv[argc_l] = buf;
1070 saw_quote = 0;
1071 }
1072 } else
1073 switch (*src) {
1074 case '"':
1075 case '\'':
1076 quote = *src;
1077 saw_quote = 1;
1078 break;
1079
1080 case '#': /* comment */
1081 if (*(src-1)== '$')
1082 *buf++ = *src;
1083 else
1084 done = 1;
1085 break;
1086
1087#ifdef CONFIG_LASH_PIPE_N_REDIRECTS
1088 case '>': /* redirects */
1089 case '<':
1090 i = prog->num_redirects++;
1091 prog->redirects = xrealloc(prog->redirects,
1092 sizeof(*prog->redirects) *
1093 (i + 1));
1094
1095 prog->redirects[i].fd = -1;
1096 if (buf != prog->argv[argc_l]) {
1097 /* the stuff before this character may be the file number
1098 being redirected */
1099 prog->redirects[i].fd =
1100 strtol(prog->argv[argc_l], &chptr, 10);
1101
1102 if (*chptr && *prog->argv[argc_l]) {
1103 buf++, argc_l++;
1104 prog->argv[argc_l] = buf;
1105 }
1106 }
1107
1108 if (prog->redirects[i].fd == -1) {
1109 if (*src == '>')
1110 prog->redirects[i].fd = 1;
1111 else
1112 prog->redirects[i].fd = 0;
1113 }
1114
1115 if (*src++ == '>') {
1116 if (*src == '>')
1117 prog->redirects[i].type =
1118 REDIRECT_APPEND, src++;
1119 else
1120 prog->redirects[i].type = REDIRECT_OVERWRITE;
1121 } else {
1122 prog->redirects[i].type = REDIRECT_INPUT;
1123 }
1124
1125 /* This isn't POSIX sh compliant. Oh well. */
1126 chptr = src;
1127 while (isspace(*chptr))
1128 chptr++;
1129
1130 if (!*chptr) {
1131 bb_error_msg("file name expected after %c", *(src-1));
1132 free_job(job);
1133 job->num_progs=0;
1134 return 1;
1135 }
1136
1137 prog->redirects[i].filename = buf;
1138 while (*chptr && !isspace(*chptr))
1139 *buf++ = *chptr++;
1140
1141 src = chptr - 1; /* we src++ later */
1142 prog->argv[argc_l] = ++buf;
1143 break;
1144
1145 case '|': /* pipe */
1146 /* finish this command */
1147 if (*prog->argv[argc_l] || saw_quote)
1148 argc_l++;
1149 if (!argc_l) {
1150 bb_error_msg("empty command in pipe");
1151 free_job(job);
1152 job->num_progs=0;
1153 return 1;
1154 }
1155 prog->argv[argc_l] = NULL;
1156
1157 /* and start the next */
1158 job->num_progs++;
1159 job->progs = xrealloc(job->progs,
1160 sizeof(*job->progs) * job->num_progs);
1161 prog = job->progs + (job->num_progs - 1);
1162 prog->num_redirects = 0;
1163 prog->redirects = NULL;
1164 prog->is_stopped = 0;
1165 prog->family = job;
1166 argc_l = 0;
1167
1168 argv_alloced = 5;
1169 prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced);
1170 prog->argv[0] = ++buf;
1171
1172 src++;
1173 while (*src && isspace(*src))
1174 src++;
1175
1176 if (!*src) {
1177 bb_error_msg("empty command in pipe");
1178 free_job(job);
1179 job->num_progs=0;
1180 return 1;
1181 }
1182 src--; /* we'll ++ it at the end of the loop */
1183
1184 break;
1185#endif
1186
1187#ifdef CONFIG_LASH_JOB_CONTROL
1188 case '&': /* background */
1189 *inbg = 1;
1190#endif
1191 case ';': /* multiple commands */
1192 done = 1;
1193 return_command = *command_ptr + (src - *command_ptr) + 1;
1194 break;
1195
1196 case '\\':
1197 src++;
1198 if (!*src) {
1199 bb_error_msg("character expected after \\");
1200 free_job(job);
1201 return 1;
1202 }
1203 if (*src == '*' || *src == '[' || *src == ']'
1204 || *src == '?') *buf++ = '\\';
1205 /* fallthrough */
1206 default:
1207 *buf++ = *src;
1208 }
1209
1210 src++;
1211 }
1212
1213 if (*prog->argv[argc_l] || saw_quote) {
1214 argc_l++;
1215 }
1216 if (!argc_l) {
1217 free_job(job);
1218 return 0;
1219 }
1220 prog->argv[argc_l] = NULL;
1221
1222 if (!return_command) {
1223 job->text = xmalloc(strlen(*command_ptr) + 1);
1224 strcpy(job->text, *command_ptr);
1225 } else {
1226 /* This leaves any trailing spaces, which is a bit sloppy */
1227 count = return_command - *command_ptr;
1228 job->text = xmalloc(count + 1);
1229 strncpy(job->text, *command_ptr, count);
1230 job->text[count] = '\0';
1231 }
1232
1233 *command_ptr = return_command;
1234
1235 return 0;
1236}
1237
1238/* Run the child_prog, no matter what kind of command it uses.
1239 */
1240static int pseudo_exec(struct child_prog *child)
1241{
1242 struct built_in_command *x;
1243#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
1244 char *name;
1245#endif
1246
1247 /* Check if the command matches any of the non-forking builtins.
1248 * Depending on context, this might be redundant. But it's
1249 * easier to waste a few CPU cycles than it is to figure out
1250 * if this is one of those cases.
1251 */
1252 for (x = bltins; x->cmd; x++) {
1253 if (strcmp(child->argv[0], x->cmd) == 0 ) {
1254 _exit(x->function(child));
1255 }
1256 }
1257
1258 /* Check if the command matches any of the forking builtins. */
1259 for (x = bltins_forking; x->cmd; x++) {
1260 if (strcmp(child->argv[0], x->cmd) == 0) {
1261 bb_applet_name=x->cmd;
1262 _exit (x->function(child));
1263 }
1264 }
1265#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
1266 /* Check if the command matches any busybox internal
1267 * commands ("applets") here. Following discussions from
1268 * November 2000 on busybox@busybox.net, don't use
1269 * bb_get_last_path_component(). This way explicit (with
1270 * slashes) filenames will never be interpreted as an
1271 * applet, just like with builtins. This way the user can
1272 * override an applet with an explicit filename reference.
1273 * The only downside to this change is that an explicit
1274 * /bin/foo invocation will fork and exec /bin/foo, even if
1275 * /bin/foo is a symlink to busybox.
1276 */
1277 name = child->argv[0];
1278
1279 {
1280 char** argv_l=child->argv;
1281 int argc_l;
1282 for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++);
1283 optind = 1;
1284 run_applet_by_name(name, argc_l, child->argv);
1285 }
1286#endif
1287
1288 execvp(child->argv[0], child->argv);
1289
1290 /* Do not use bb_perror_msg_and_die() here, since we must not
1291 * call exit() but should call _exit() instead */
1292 fprintf(stderr, "%s: %m\n", child->argv[0]);
1293 _exit(EXIT_FAILURE);
1294}
1295
1296static void insert_job(struct job *newjob, int inbg)
1297{
1298 struct job *thejob;
1299 struct jobset *j_list=newjob->job_list;
1300
1301 /* find the ID for thejob to use */
1302 newjob->jobid = 1;
1303 for (thejob = j_list->head; thejob; thejob = thejob->next)
1304 if (thejob->jobid >= newjob->jobid)
1305 newjob->jobid = thejob->jobid + 1;
1306
1307 /* add thejob to the list of running jobs */
1308 if (!j_list->head) {
1309 thejob = j_list->head = xmalloc(sizeof(*thejob));
1310 } else {
1311 for (thejob = j_list->head; thejob->next; thejob = thejob->next) /* nothing */;
1312 thejob->next = xmalloc(sizeof(*thejob));
1313 thejob = thejob->next;
1314 }
1315
1316 *thejob = *newjob; /* physically copy the struct job */
1317 thejob->next = NULL;
1318 thejob->running_progs = thejob->num_progs;
1319 thejob->stopped_progs = 0;
1320
1321#ifdef CONFIG_LASH_JOB_CONTROL
1322 if (inbg) {
1323 /* we don't wait for background thejobs to return -- append it
1324 to the list of backgrounded thejobs and leave it alone */
1325 printf("[%d] %d\n", thejob->jobid,
1326 newjob->progs[newjob->num_progs - 1].pid);
1327 last_jobid = newjob->jobid;
1328 last_bg_pid=newjob->progs[newjob->num_progs - 1].pid;
1329 } else {
1330 newjob->job_list->fg = thejob;
1331
1332 /* move the new process group into the foreground */
1333 /* suppress messages when run from /linuxrc mag@sysgo.de */
1334 if (tcsetpgrp(shell_terminal, newjob->pgrp) && errno != ENOTTY)
1335 bb_perror_msg("tcsetpgrp");
1336 }
1337#endif
1338}
1339
1340static int run_command(struct job *newjob, int inbg, int outpipe[2])
1341{
1342 /* struct job *thejob; */
1343 int i;
1344 int nextin, nextout;
1345 int pipefds[2]; /* pipefd[0] is for reading */
1346 struct built_in_command *x;
1347 struct child_prog *child;
1348
1349 nextin = 0, nextout = 1;
1350 for (i = 0; i < newjob->num_progs; i++) {
1351 child = & (newjob->progs[i]);
1352
1353 if ((i + 1) < newjob->num_progs) {
1354 if (pipe(pipefds)<0) bb_perror_msg_and_die("pipe");
1355 nextout = pipefds[1];
1356 } else {
1357 if (outpipe[1]!=-1) {
1358 nextout = outpipe[1];
1359 } else {
1360 nextout = 1;
1361 }
1362 }
1363
1364
1365 /* Check if the command matches any non-forking builtins,
1366 * but only if this is a simple command.
1367 * Non-forking builtins within pipes have to fork anyway,
1368 * and are handled in pseudo_exec. "echo foo | read bar"
1369 * is doomed to failure, and doesn't work on bash, either.
1370 */
1371 if (newjob->num_progs == 1) {
1372 for (x = bltins; x->cmd; x++) {
1373 if (strcmp(child->argv[0], x->cmd) == 0 ) {
1374 int rcode;
1375 int squirrel[] = {-1, -1, -1};
1376 setup_redirects(child, squirrel);
1377 rcode = x->function(child);
1378 restore_redirects(squirrel);
1379 return rcode;
1380 }
1381 }
1382 }
1383
1384#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
1385 if (!(child->pid = fork()))
1386#else
1387 if (!(child->pid = vfork()))
1388#endif
1389 {
1390 /* Set the handling for job control signals back to the default. */
1391 signal(SIGINT, SIG_DFL);
1392 signal(SIGQUIT, SIG_DFL);
1393 signal(SIGTSTP, SIG_DFL);
1394 signal(SIGTTIN, SIG_DFL);
1395 signal(SIGTTOU, SIG_DFL);
1396 signal(SIGCHLD, SIG_DFL);
1397
1398 close_all();
1399
1400 if (outpipe[1]!=-1) {
1401 close(outpipe[0]);
1402 }
1403 if (nextin != 0) {
1404 dup2(nextin, 0);
1405 close(nextin);
1406 }
1407
1408 if (nextout != 1) {
1409 dup2(nextout, 1);
1410 dup2(nextout, 2); /* Really? */
1411 close(nextout);
1412 close(pipefds[0]);
1413 }
1414
1415 /* explicit redirects override pipes */
1416 setup_redirects(child,NULL);
1417
1418 pseudo_exec(child);
1419 }
1420 if (outpipe[1]!=-1) {
1421 close(outpipe[1]);
1422 }
1423
1424 /* put our child in the process group whose leader is the
1425 first process in this pipe */
1426 setpgid(child->pid, newjob->progs[0].pid);
1427 if (nextin != 0)
1428 close(nextin);
1429 if (nextout != 1)
1430 close(nextout);
1431
1432 /* If there isn't another process, nextin is garbage
1433 but it doesn't matter */
1434 nextin = pipefds[0];
1435 }
1436
1437 newjob->pgrp = newjob->progs[0].pid;
1438
1439 insert_job(newjob, inbg);
1440
1441 return 0;
1442}
1443
1444static int busy_loop(FILE * input)
1445{
1446 char *command;
1447 char *next_command = NULL;
1448 struct job newjob;
1449 int i;
1450 int inbg;
1451 int status;
1452#ifdef CONFIG_LASH_JOB_CONTROL
1453 pid_t parent_pgrp;
1454 /* save current owner of TTY so we can restore it on exit */
1455 parent_pgrp = tcgetpgrp(shell_terminal);
1456#endif
1457 newjob.job_list = &job_list;
1458 newjob.job_context = DEFAULT_CONTEXT;
1459
1460 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1461
1462 while (1) {
1463 if (!job_list.fg) {
1464 /* no job is in the foreground */
1465
1466 /* see if any background processes have exited */
1467 checkjobs(&job_list);
1468
1469 if (!next_command) {
1470 if (get_command(input, command))
1471 break;
1472 next_command = command;
1473 }
1474
1475 if (! expand_arguments(next_command)) {
1476 free(command);
1477 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1478 next_command = NULL;
1479 continue;
1480 }
1481
1482 if (!parse_command(&next_command, &newjob, &inbg) &&
1483 newjob.num_progs) {
1484 int pipefds[2] = {-1,-1};
1485 debug_printf( "job=%p fed to run_command by busy_loop()'\n",
1486 &newjob);
1487 run_command(&newjob, inbg, pipefds);
1488 }
1489 else {
1490 free(command);
1491 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1492 next_command = NULL;
1493 }
1494 } else {
1495 /* a job is running in the foreground; wait for it */
1496 i = 0;
1497 while (!job_list.fg->progs[i].pid ||
1498 job_list.fg->progs[i].is_stopped == 1) i++;
1499
1500 if (waitpid(job_list.fg->progs[i].pid, &status, WUNTRACED)<0) {
1501 if (errno != ECHILD) {
1502 bb_perror_msg_and_die("waitpid(%d)",job_list.fg->progs[i].pid);
1503 }
1504 }
1505
1506 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1507 /* the child exited */
1508 job_list.fg->running_progs--;
1509 job_list.fg->progs[i].pid = 0;
1510
1511 last_return_code=WEXITSTATUS(status);
1512
1513 if (!job_list.fg->running_progs) {
1514 /* child exited */
1515 remove_job(&job_list, job_list.fg);
1516 job_list.fg = NULL;
1517 }
1518 }
1519#ifdef CONFIG_LASH_JOB_CONTROL
1520 else {
1521 /* the child was stopped */
1522 job_list.fg->stopped_progs++;
1523 job_list.fg->progs[i].is_stopped = 1;
1524
1525 if (job_list.fg->stopped_progs == job_list.fg->running_progs) {
1526 printf("\n" JOB_STATUS_FORMAT, job_list.fg->jobid,
1527 "Stopped", job_list.fg->text);
1528 job_list.fg = NULL;
1529 }
1530 }
1531
1532 if (!job_list.fg) {
1533 /* move the shell to the foreground */
1534 /* suppress messages when run from /linuxrc mag@sysgo.de */
1535 if (tcsetpgrp(shell_terminal, getpgrp()) && errno != ENOTTY)
1536 bb_perror_msg("tcsetpgrp");
1537 }
1538#endif
1539 }
1540 }
1541 free(command);
1542
1543#ifdef CONFIG_LASH_JOB_CONTROL
1544 /* return controlling TTY back to parent process group before exiting */
1545 if (tcsetpgrp(shell_terminal, parent_pgrp) && errno != ENOTTY)
1546 bb_perror_msg("tcsetpgrp");
1547#endif
1548
1549 /* return exit status if called with "-c" */
1550 if (input == NULL && WIFEXITED(status))
1551 return WEXITSTATUS(status);
1552
1553 return 0;
1554}
1555
1556#ifdef CONFIG_FEATURE_CLEAN_UP
1557void free_memory(void)
1558{
1559 if (cwd && cwd!=bb_msg_unknown) {
1560 free((char*)cwd);
1561 }
1562 if (local_pending_command)
1563 free(local_pending_command);
1564
1565 if (job_list.fg && !job_list.fg->running_progs) {
1566 remove_job(&job_list, job_list.fg);
1567 }
1568}
1569#endif
1570
1571#ifdef CONFIG_LASH_JOB_CONTROL
1572/* Make sure we have a controlling tty. If we get started under a job
1573 * aware app (like bash for example), make sure we are now in charge so
1574 * we don't fight over who gets the foreground */
1575static void setup_job_control(void)
1576{
1577 int status;
1578 pid_t shell_pgrp;
1579
1580 /* Loop until we are in the foreground. */
1581 while ((status = tcgetpgrp (shell_terminal)) >= 0) {
1582 if (status == (shell_pgrp = getpgrp ())) {
1583 break;
1584 }
1585 kill (- shell_pgrp, SIGTTIN);
1586 }
1587
1588 /* Ignore interactive and job-control signals. */
1589 signal(SIGINT, SIG_IGN);
1590 signal(SIGQUIT, SIG_IGN);
1591 signal(SIGTSTP, SIG_IGN);
1592 signal(SIGTTIN, SIG_IGN);
1593 signal(SIGTTOU, SIG_IGN);
1594 signal(SIGCHLD, SIG_IGN);
1595
1596 /* Put ourselves in our own process group. */
1597 setsid();
1598 shell_pgrp = getpid ();
1599 setpgid (shell_pgrp, shell_pgrp);
1600
1601 /* Grab control of the terminal. */
1602 tcsetpgrp(shell_terminal, shell_pgrp);
1603}
1604#else
1605static inline void setup_job_control(void)
1606{
1607}
1608#endif
1609
1610int lash_main(int argc_l, char **argv_l)
1611{
1612 int opt, interactive=FALSE;
1613 FILE *input = stdin;
1614 argc = argc_l;
1615 argv = argv_l;
1616
1617 /* These variables need re-initializing when recursing */
1618 last_jobid = 0;
1619 local_pending_command = NULL;
1620 close_me_head = NULL;
1621 job_list.head = NULL;
1622 job_list.fg = NULL;
1623 last_return_code=1;
1624
1625 if (argv[0] && argv[0][0] == '-') {
1626 FILE *prof_input;
1627 prof_input = fopen("/etc/profile", "r");
1628 if (prof_input) {
1629 int tmp_fd = fileno(prof_input);
1630 mark_open(tmp_fd);
1631 /* Now run the file */
1632 busy_loop(prof_input);
1633 fclose(prof_input);
1634 mark_closed(tmp_fd);
1635 }
1636 }
1637
1638 while ((opt = getopt(argc_l, argv_l, "cxi")) > 0) {
1639 switch (opt) {
1640 case 'c':
1641 input = NULL;
1642 if (local_pending_command != 0)
1643 bb_error_msg_and_die("multiple -c arguments");
1644 local_pending_command = bb_xstrdup(argv[optind]);
1645 optind++;
1646 argv = argv+optind;
1647 break;
1648 case 'i':
1649 interactive = TRUE;
1650 break;
1651 default:
1652 bb_show_usage();
1653 }
1654 }
1655 /* A shell is interactive if the `-i' flag was given, or if all of
1656 * the following conditions are met:
1657 * no -c command
1658 * no arguments remaining or the -s flag given
1659 * standard input is a terminal
1660 * standard output is a terminal
1661 * Refer to Posix.2, the description of the `sh' utility. */
1662 if (argv[optind]==NULL && input==stdin &&
1663 isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
1664 interactive=TRUE;
1665 }
1666 setup_job_control();
1667 if (interactive==TRUE) {
1668 //printf( "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1669 /* Looks like they want an interactive shell */
1670#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
1671 printf( "\n\n" BB_BANNER " Built-in shell (lash)\n");
1672 printf( "Enter 'help' for a list of built-in commands.\n\n");
1673#endif
1674 } else if (local_pending_command==NULL) {
1675 //printf( "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1676 input = bb_xfopen(argv[optind], "r");
1677 mark_open(fileno(input)); /* be lazy, never mark this closed */
1678 }
1679
1680 /* initialize the cwd -- this is never freed...*/
1681 cwd = xgetcwd(0);
1682 if (!cwd)
1683 cwd = bb_msg_unknown;
1684
1685#ifdef CONFIG_FEATURE_CLEAN_UP
1686 atexit(free_memory);
1687#endif
1688
1689#ifdef CONFIG_FEATURE_COMMAND_EDITING
1690 cmdedit_set_initial_prompt();
1691#else
1692 PS1 = NULL;
1693#endif
1694
1695 return (busy_loop(input));
1696}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Minix shell port for busybox
4 *
5 * This version of the Minix shell was adapted for use in busybox
6 * by Erik Andersen <andersen@codepoet.org>
7 *
8 * - backtick expansion did not work properly
9 * Jonas Holmberg <jonas.holmberg@axis.com>
10 * Robert Schwebel <r.schwebel@pengutronix.de>
11 * Erik Andersen <andersen@codepoet.org>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 *
27 * Original copyright notice is retained at the end of this file.
28 */
29
30#include <ctype.h>
31#include <dirent.h>
32#include <errno.h>
33#include <fcntl.h>
34#include <limits.h>
35#include <setjmp.h>
36#include <signal.h>
37#include <stddef.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <time.h>
42#include <unistd.h>
43#include <sys/stat.h>
44#include <sys/times.h>
45#include <sys/types.h>
46#include <sys/wait.h>
47
48#include "cmdedit.h"
49#include "busybox.h"
50
51
52/* Conditional use of "register" keyword */
53#define REGISTER register
54
55
56/*#define MSHDEBUG 1*/
57
58#ifdef MSHDEBUG
59int mshdbg = 0;
60
61#define DBGPRINTF(x) if(mshdbg>0)printf x
62#define DBGPRINTF0(x) if(mshdbg>0)printf x
63#define DBGPRINTF1(x) if(mshdbg>1)printf x
64#define DBGPRINTF2(x) if(mshdbg>2)printf x
65#define DBGPRINTF3(x) if(mshdbg>3)printf x
66#define DBGPRINTF4(x) if(mshdbg>4)printf x
67#define DBGPRINTF5(x) if(mshdbg>5)printf x
68#define DBGPRINTF6(x) if(mshdbg>6)printf x
69#define DBGPRINTF7(x) if(mshdbg>7)printf x
70#define DBGPRINTF8(x) if(mshdbg>8)printf x
71#define DBGPRINTF9(x) if(mshdbg>9)printf x
72
73int mshdbg_rc = 0;
74
75#define RCPRINTF(x) if(mshdbg_rc)printf x
76
77#else
78
79#define DBGPRINTF(x)
80#define DBGPRINTF0(x)
81#define DBGPRINTF1(x)
82#define DBGPRINTF2(x)
83#define DBGPRINTF3(x)
84#define DBGPRINTF4(x)
85#define DBGPRINTF5(x)
86#define DBGPRINTF6(x)
87#define DBGPRINTF7(x)
88#define DBGPRINTF8(x)
89#define DBGPRINTF9(x)
90
91#define RCPRINTF(x)
92
93#endif /* MSHDEBUG */
94
95
96/* -------- sh.h -------- */
97/*
98 * shell
99 */
100
101#define LINELIM 2100
102#define NPUSH 8 /* limit to input nesting */
103
104#undef NOFILE
105#define NOFILE 20 /* Number of open files */
106#define NUFILE 10 /* Number of user-accessible files */
107#define FDBASE 10 /* First file usable by Shell */
108
109/*
110 * values returned by wait
111 */
112#define WAITSIG(s) ((s)&0177)
113#define WAITVAL(s) (((s)>>8)&0377)
114#define WAITCORE(s) (((s)&0200)!=0)
115
116/*
117 * library and system definitions
118 */
119typedef void xint; /* base type of jmp_buf, for not broken compilers */
120
121/*
122 * shell components
123 */
124
125#define QUOTE 0200
126
127#define NOBLOCK ((struct op *)NULL)
128#define NOWORD ((char *)NULL)
129#define NOWORDS ((char **)NULL)
130#define NOPIPE ((int *)NULL)
131
132/*
133 * Description of a command or an operation on commands.
134 * Might eventually use a union.
135 */
136struct op {
137 int type; /* operation type, see below */
138 char **words; /* arguments to a command */
139 struct ioword **ioact; /* IO actions (eg, < > >>) */
140 struct op *left;
141 struct op *right;
142 char *str; /* identifier for case and for */
143};
144
145#define TCOM 1 /* command */
146#define TPAREN 2 /* (c-list) */
147#define TPIPE 3 /* a | b */
148#define TLIST 4 /* a [&;] b */
149#define TOR 5 /* || */
150#define TAND 6 /* && */
151#define TFOR 7
152#define TDO 8
153#define TCASE 9
154#define TIF 10
155#define TWHILE 11
156#define TUNTIL 12
157#define TELIF 13
158#define TPAT 14 /* pattern in case */
159#define TBRACE 15 /* {c-list} */
160#define TASYNC 16 /* c & */
161/* Added to support "." file expansion */
162#define TDOT 17
163
164/* Strings for names to make debug easier */
165char *T_CMD_NAMES[] = {
166 "PLACEHOLDER",
167 "TCOM",
168 "TPAREN",
169 "TPIPE",
170 "TLIST",
171 "TOR",
172 "TAND",
173 "TFOR",
174 "TDO",
175 "TCASE",
176 "TIF",
177 "TWHILE",
178 "TUNTIL",
179 "TELIF",
180 "TPAT",
181 "TBRACE",
182 "TASYNC",
183 "TDOT",
184};
185
186
187/*
188 * actions determining the environment of a process
189 */
190#define BIT(i) (1<<(i))
191#define FEXEC BIT(0) /* execute without forking */
192
193#if 0 /* Original value */
194#define AREASIZE (65000)
195#else
196#define AREASIZE (90000)
197#endif
198
199/*
200 * flags to control evaluation of words
201 */
202#define DOSUB 1 /* interpret $, `, and quotes */
203#define DOBLANK 2 /* perform blank interpretation */
204#define DOGLOB 4 /* interpret [?* */
205#define DOKEY 8 /* move words with `=' to 2nd arg. list */
206#define DOTRIM 16 /* trim resulting string */
207
208#define DOALL (DOSUB|DOBLANK|DOGLOB|DOKEY|DOTRIM)
209
210
211/* PROTOTYPES */
212static int newfile(char *s);
213static char *findeq(char *cp);
214static char *cclass(char *p, int sub);
215static void initarea(void);
216extern int msh_main(int argc, char **argv);
217
218
219struct brkcon {
220 jmp_buf brkpt;
221 struct brkcon *nextlev;
222};
223
224
225/*
226 * redirection
227 */
228struct ioword {
229 short io_unit; /* unit affected */
230 short io_flag; /* action (below) */
231 char *io_name; /* file name */
232};
233
234#define IOREAD 1 /* < */
235#define IOHERE 2 /* << (here file) */
236#define IOWRITE 4 /* > */
237#define IOCAT 8 /* >> */
238#define IOXHERE 16 /* ${}, ` in << */
239#define IODUP 32 /* >&digit */
240#define IOCLOSE 64 /* >&- */
241
242#define IODEFAULT (-1) /* token for default IO unit */
243
244
245
246/*
247 * parsing & execution environment
248 */
249static struct env {
250 char *linep;
251 struct io *iobase;
252 struct io *iop;
253 xint *errpt; /* void * */
254 int iofd;
255 struct env *oenv;
256} e;
257
258/*
259 * flags:
260 * -e: quit on error
261 * -k: look for name=value everywhere on command line
262 * -n: no execution
263 * -t: exit after reading and executing one command
264 * -v: echo as read
265 * -x: trace
266 * -u: unset variables net diagnostic
267 */
268static char *flag;
269
270static char *null; /* null value for variable */
271static int intr; /* interrupt pending */
272
273static char *trap[_NSIG + 1];
274static char ourtrap[_NSIG + 1];
275static int trapset; /* trap pending */
276
277static int heedint; /* heed interrupt signals */
278
279static int yynerrs; /* yacc */
280
281static char line[LINELIM];
282static char *elinep;
283
284
285/*
286 * other functions
287 */
288static int (*inbuilt(char *s)) (struct op *);
289
290static char *rexecve(char *c, char **v, char **envp);
291static char *space(int n);
292static char *strsave(char *s, int a);
293static char *evalstr(char *cp, int f);
294static char *putn(int n);
295static char *itoa(int n);
296static char *unquote(char *as);
297static struct var *lookup(char *n);
298static int rlookup(char *n);
299static struct wdblock *glob(char *cp, struct wdblock *wb);
300static int my_getc(int ec);
301static int subgetc(int ec, int quoted);
302static char **makenv(int all, struct wdblock *wb);
303static char **eval(char **ap, int f);
304static int setstatus(int s);
305static int waitfor(int lastpid, int canintr);
306
307static void onintr(int s); /* SIGINT handler */
308
309static int newenv(int f);
310static void quitenv(void);
311static void err(char *s);
312static int anys(char *s1, char *s2);
313static int any(int c, char *s);
314static void next(int f);
315static void setdash(void);
316static void onecommand(void);
317static void runtrap(int i);
318static int gmatch(char *s, char *p);
319
320
321/*
322 * error handling
323 */
324static void leave(void); /* abort shell (or fail in subshell) */
325static void fail(void); /* fail but return to process next command */
326static void warn(char *s);
327static void sig(int i); /* default signal handler */
328
329
330
331/* -------- area stuff -------- */
332
333#define REGSIZE sizeof(struct region)
334#define GROWBY (256)
335/* #define SHRINKBY (64) */
336#undef SHRINKBY
337#define FREE (32767)
338#define BUSY (0)
339#define ALIGN (sizeof(int)-1)
340
341
342struct region {
343 struct region *next;
344 int area;
345};
346
347
348
349/* -------- grammar stuff -------- */
350typedef union {
351 char *cp;
352 char **wp;
353 int i;
354 struct op *o;
355} YYSTYPE;
356
357#define WORD 256
358#define LOGAND 257
359#define LOGOR 258
360#define BREAK 259
361#define IF 260
362#define THEN 261
363#define ELSE 262
364#define ELIF 263
365#define FI 264
366#define CASE 265
367#define ESAC 266
368#define FOR 267
369#define WHILE 268
370#define UNTIL 269
371#define DO 270
372#define DONE 271
373#define IN 272
374/* Added for "." file expansion */
375#define DOT 273
376
377#define YYERRCODE 300
378
379/* flags to yylex */
380#define CONTIN 01 /* skip new lines to complete command */
381
382#define SYNTAXERR zzerr()
383
384static struct op *pipeline(int cf);
385static struct op *andor(void);
386static struct op *c_list(void);
387static int synio(int cf);
388static void musthave(int c, int cf);
389static struct op *simple(void);
390static struct op *nested(int type, int mark);
391static struct op *command(int cf);
392static struct op *dogroup(int onlydone);
393static struct op *thenpart(void);
394static struct op *elsepart(void);
395static struct op *caselist(void);
396static struct op *casepart(void);
397static char **pattern(void);
398static char **wordlist(void);
399static struct op *list(struct op *t1, struct op *t2);
400static struct op *block(int type, struct op *t1, struct op *t2, char **wp);
401static struct op *newtp(void);
402static struct op *namelist(struct op *t);
403static char **copyw(void);
404static void word(char *cp);
405static struct ioword **copyio(void);
406static struct ioword *io(int u, int f, char *cp);
407static void zzerr(void);
408static void yyerror(char *s);
409static int yylex(int cf);
410static int collect(int c, int c1);
411static int dual(int c);
412static void diag(int ec);
413static char *tree(unsigned size);
414
415/* -------- var.h -------- */
416
417struct var {
418 char *value;
419 char *name;
420 struct var *next;
421 char status;
422};
423
424#define COPYV 1 /* flag to setval, suggesting copy */
425#define RONLY 01 /* variable is read-only */
426#define EXPORT 02 /* variable is to be exported */
427#define GETCELL 04 /* name & value space was got with getcell */
428
429static int yyparse(void);
430static struct var *lookup(char *n);
431static void setval(struct var *vp, char *val);
432static void nameval(struct var *vp, char *val, char *name);
433static void export(struct var *vp);
434static void ronly(struct var *vp);
435static int isassign(char *s);
436static int checkname(char *cp);
437static int assign(char *s, int cf);
438static void putvlist(int f, int out);
439static int eqname(char *n1, char *n2);
440
441static int execute(struct op *t, int *pin, int *pout, int act);
442
443
444/* -------- io.h -------- */
445/* io buffer */
446struct iobuf {
447 unsigned id; /* buffer id */
448 char buf[512]; /* buffer */
449 char *bufp; /* pointer into buffer */
450 char *ebufp; /* pointer to end of buffer */
451};
452
453/* possible arguments to an IO function */
454struct ioarg {
455 char *aword;
456 char **awordlist;
457 int afile; /* file descriptor */
458 unsigned afid; /* buffer id */
459 long afpos; /* file position */
460 struct iobuf *afbuf; /* buffer for this file */
461};
462
463//static struct ioarg ioargstack[NPUSH];
464#define AFID_NOBUF (~0)
465#define AFID_ID 0
466
467/* an input generator's state */
468struct io {
469 int (*iofn) (struct ioarg *, struct io *);
470 struct ioarg *argp;
471 int peekc;
472 char prev; /* previous character read by readc() */
473 char nlcount; /* for `'s */
474 char xchar; /* for `'s */
475 char task; /* reason for pushed IO */
476};
477
478//static struct io iostack[NPUSH];
479#define XOTHER 0 /* none of the below */
480#define XDOLL 1 /* expanding ${} */
481#define XGRAVE 2 /* expanding `'s */
482#define XIO 3 /* file IO */
483
484/* in substitution */
485#define INSUB() (e.iop->task == XGRAVE || e.iop->task == XDOLL)
486
487
488/*
489 * input generators for IO structure
490 */
491static int nlchar(struct ioarg *ap);
492static int strchar(struct ioarg *ap);
493static int qstrchar(struct ioarg *ap);
494static int filechar(struct ioarg *ap);
495static int herechar(struct ioarg *ap);
496static int linechar(struct ioarg *ap);
497static int gravechar(struct ioarg *ap, struct io *iop);
498static int qgravechar(struct ioarg *ap, struct io *iop);
499static int dolchar(struct ioarg *ap);
500static int wdchar(struct ioarg *ap);
501static void scraphere(void);
502static void freehere(int area);
503static void gethere(void);
504static void markhere(char *s, struct ioword *iop);
505static int herein(char *hname, int xdoll);
506static int run(struct ioarg *argp, int (*f) (struct ioarg *));
507
508
509/*
510 * IO functions
511 */
512static int eofc(void);
513static int readc(void);
514static void unget(int c);
515static void ioecho(int c);
516static void prs(char *s);
517static void prn(unsigned u);
518static void closef(int i);
519static void closeall(void);
520
521
522/*
523 * IO control
524 */
525static void pushio(struct ioarg *argp, int (*f) (struct ioarg *));
526static int remap(int fd);
527static int openpipe(int *pv);
528static void closepipe(int *pv);
529static struct io *setbase(struct io *ip);
530
531#define PUSHIO(what,arg,gen) ((temparg.what = (arg)),pushio(&temparg,(gen)))
532#define RUN(what,arg,gen) ((temparg.what = (arg)), run(&temparg,(gen)))
533
534/* -------- word.h -------- */
535
536#define NSTART 16 /* default number of words to allow for initially */
537
538struct wdblock {
539 short w_bsize;
540 short w_nword;
541 /* bounds are arbitrary */
542 char *w_words[1];
543};
544
545static struct wdblock *addword(char *wd, struct wdblock *wb);
546static struct wdblock *newword(int nw);
547static char **getwords(struct wdblock *wb);
548
549/* -------- area.h -------- */
550
551/*
552 * storage allocation
553 */
554static char *getcell(unsigned nbytes);
555static void garbage(void);
556static void setarea(char *cp, int a);
557static int getarea(char *cp);
558static void freearea(int a);
559static void freecell(char *cp);
560static int areanum; /* current allocation area */
561
562#define NEW(type) (type *)getcell(sizeof(type))
563#define DELETE(obj) freecell((char *)obj)
564
565
566/* -------- misc stuff -------- */
567
568static int forkexec(struct op *t, int *pin, int *pout, int act, char **wp);
569static int iosetup(struct ioword *iop, int pipein, int pipeout);
570static void echo(char **wp);
571static struct op **find1case(struct op *t, char *w);
572static struct op *findcase(struct op *t, char *w);
573static void brkset(struct brkcon *bc);
574static int dolabel(struct op *t);
575static int dohelp(struct op *t);
576static int dochdir(struct op *t);
577static int doshift(struct op *t);
578static int dologin(struct op *t);
579static int doumask(struct op *t);
580static int doexec(struct op *t);
581static int dodot(struct op *t);
582static int dowait(struct op *t);
583static int doread(struct op *t);
584static int doeval(struct op *t);
585static int dotrap(struct op *t);
586static int getsig(char *s);
587static void setsig(int n, sighandler_t f);
588static int getn(char *as);
589static int dobreak(struct op *t);
590static int docontinue(struct op *t);
591static int brkcontin(char *cp, int val);
592static int doexit(struct op *t);
593static int doexport(struct op *t);
594static int doreadonly(struct op *t);
595static void rdexp(char **wp, void (*f) (struct var *), int key);
596static void badid(char *s);
597static int doset(struct op *t);
598static void varput(char *s, int out);
599static int dotimes(struct op *t);
600static int expand(char *cp, struct wdblock **wbp, int f);
601static char *blank(int f);
602static int dollar(int quoted);
603static int grave(int quoted);
604static void globname(char *we, char *pp);
605static char *generate(char *start1, char *end1, char *middle, char *end);
606static int anyspcl(struct wdblock *wb);
607static int xstrcmp(char *p1, char *p2);
608static void glob0(char *a0, unsigned int a1, int a2,
609 int (*a3) (char *, char *));
610static void glob1(char *base, char *lim);
611static void glob2(char *i, char *j);
612static void glob3(char *i, char *j, char *k);
613static void readhere(char **name, char *s, int ec);
614static void pushio(struct ioarg *argp, int (*f) (struct ioarg *));
615static int xxchar(struct ioarg *ap);
616
617struct here {
618 char *h_tag;
619 int h_dosub;
620 struct ioword *h_iop;
621 struct here *h_next;
622};
623
624static char *signame[] = {
625 "Signal 0",
626 "Hangup",
627 (char *) NULL, /* interrupt */
628 "Quit",
629 "Illegal instruction",
630 "Trace/BPT trap",
631 "Abort",
632 "Bus error",
633 "Floating Point Exception",
634 "Killed",
635 "SIGUSR1",
636 "SIGSEGV",
637 "SIGUSR2",
638 (char *) NULL, /* broken pipe */
639 "Alarm clock",
640 "Terminated",
641};
642
643#define NSIGNAL (sizeof(signame)/sizeof(signame[0]))
644
645struct res {
646 char *r_name;
647 int r_val;
648};
649static struct res restab[] = {
650 {"for", FOR},
651 {"case", CASE},
652 {"esac", ESAC},
653 {"while", WHILE},
654 {"do", DO},
655 {"done", DONE},
656 {"if", IF},
657 {"in", IN},
658 {"then", THEN},
659 {"else", ELSE},
660 {"elif", ELIF},
661 {"until", UNTIL},
662 {"fi", FI},
663 {";;", BREAK},
664 {"||", LOGOR},
665 {"&&", LOGAND},
666 {"{", '{'},
667 {"}", '}'},
668 {".", DOT},
669 {0, 0},
670};
671
672
673struct builtincmd {
674 const char *name;
675 int (*builtinfunc) (struct op * t);
676};
677static const struct builtincmd builtincmds[] = {
678 {".", dodot},
679 {":", dolabel},
680 {"break", dobreak},
681 {"cd", dochdir},
682 {"continue", docontinue},
683 {"eval", doeval},
684 {"exec", doexec},
685 {"exit", doexit},
686 {"export", doexport},
687 {"help", dohelp},
688 {"login", dologin},
689 {"newgrp", dologin},
690 {"read", doread},
691 {"readonly", doreadonly},
692 {"set", doset},
693 {"shift", doshift},
694 {"times", dotimes},
695 {"trap", dotrap},
696 {"umask", doumask},
697 {"wait", dowait},
698 {0, 0}
699};
700
701struct op *scantree(struct op *);
702static struct op *dowholefile(int, int);
703
704/* Globals */
705extern char **environ; /* environment pointer */
706
707static char **dolv;
708static int dolc;
709static int exstat;
710static char gflg;
711static int interactive = 0; /* Is this an interactive shell */
712static int execflg;
713static int multiline; /* \n changed to ; */
714static struct op *outtree; /* result from parser */
715static xint *failpt;
716static xint *errpt;
717static struct brkcon *brklist;
718static int isbreak;
719static struct wdblock *wdlist;
720static struct wdblock *iolist;
721static char *trap[_NSIG + 1];
722static char ourtrap[_NSIG + 1];
723static int trapset; /* trap pending */
724static int yynerrs; /* yacc */
725static char line[LINELIM];
726
727#ifdef MSHDEBUG
728static struct var *mshdbg_var;
729#endif
730static struct var *vlist; /* dictionary */
731static struct var *homedir; /* home directory */
732static struct var *prompt; /* main prompt */
733static struct var *cprompt; /* continuation prompt */
734static struct var *path; /* search path for commands */
735static struct var *shell; /* shell to interpret command files */
736static struct var *ifs; /* field separators */
737
738static int areanum; /* current allocation area */
739static int intr;
740static int inparse;
741static char flags['z' - 'a' + 1];
742static char *flag = flags - 'a';
743static char *null = "";
744static int heedint = 1;
745static void (*qflag) (int) = SIG_IGN;
746static int startl;
747static int peeksym;
748static int nlseen;
749static int iounit = IODEFAULT;
750static YYSTYPE yylval;
751static char *elinep = line + sizeof(line) - 5;
752
753static struct ioarg temparg = { 0, 0, 0, AFID_NOBUF, 0 }; /* temporary for PUSHIO */
754static struct ioarg ioargstack[NPUSH];
755static struct io iostack[NPUSH];
756static struct iobuf sharedbuf = { AFID_NOBUF };
757static struct iobuf mainbuf = { AFID_NOBUF };
758static unsigned bufid = AFID_ID; /* buffer id counter */
759
760static struct here *inhere; /* list of hear docs while parsing */
761static struct here *acthere; /* list of active here documents */
762static struct region *areabot; /* bottom of area */
763static struct region *areatop; /* top of area */
764static struct region *areanxt; /* starting point of scan */
765static void *brktop;
766static void *brkaddr;
767
768static struct env e = {
769 line, /* linep: char ptr */
770 iostack, /* iobase: struct io ptr */
771 iostack - 1, /* iop: struct io ptr */
772 (xint *) NULL, /* errpt: void ptr for errors? */
773 FDBASE, /* iofd: file desc */
774 (struct env *) NULL /* oenv: struct env ptr */
775};
776
777#ifdef MSHDEBUG
778void print_t(struct op *t)
779{
780 DBGPRINTF(("T: t=0x%x, type %s, words=0x%x, IOword=0x%x\n", t,
781 T_CMD_NAMES[t->type], t->words, t->ioact));
782
783 if (t->words) {
784 DBGPRINTF(("T: W1: %s", t->words[0]));
785 }
786
787 return;
788}
789
790void print_tree(struct op *head)
791{
792 if (head == NULL) {
793 DBGPRINTF(("PRINT_TREE: no tree\n"));
794 return;
795 }
796
797 DBGPRINTF(("NODE: 0x%x, left 0x%x, right 0x%x\n", head, head->left,
798 head->right));
799
800 if (head->left)
801 print_tree(head->left);
802
803 if (head->right)
804 print_tree(head->right);
805
806 return;
807}
808#endif /* MSHDEBUG */
809
810
811#ifdef CONFIG_FEATURE_COMMAND_EDITING
812static char *current_prompt;
813#endif
814
815/* -------- sh.c -------- */
816/*
817 * shell
818 */
819
820
821extern int msh_main(int argc, char **argv)
822{
823 REGISTER int f;
824 REGISTER char *s;
825 int cflag;
826 char *name, **ap;
827 int (*iof) (struct ioarg *);
828
829 DBGPRINTF(("MSH_MAIN: argc %d, environ 0x%x\n", argc, environ));
830
831 initarea();
832 if ((ap = environ) != NULL) {
833 while (*ap)
834 assign(*ap++, !COPYV);
835 for (ap = environ; *ap;)
836 export(lookup(*ap++));
837 }
838 closeall();
839 areanum = 1;
840
841 shell = lookup("SHELL");
842 if (shell->value == null)
843 setval(shell, (char *)DEFAULT_SHELL);
844 export(shell);
845
846 homedir = lookup("HOME");
847 if (homedir->value == null)
848 setval(homedir, "/");
849 export(homedir);
850
851 setval(lookup("$"), putn(getpid()));
852
853 path = lookup("PATH");
854 if (path->value == null) {
855 if (geteuid() == 0)
856 setval(path, "/sbin:/bin:/usr/sbin:/usr/bin");
857 else
858 setval(path, "/bin:/usr/bin");
859 }
860 export(path);
861
862 ifs = lookup("IFS");
863 if (ifs->value == null)
864 setval(ifs, " \t\n");
865
866#ifdef MSHDEBUG
867 mshdbg_var = lookup("MSHDEBUG");
868 if (mshdbg_var->value == null)
869 setval(mshdbg_var, "0");
870#endif
871
872
873 prompt = lookup("PS1");
874#ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
875 if (prompt->value == null)
876#endif
877 setval(prompt, "$ ");
878 if (geteuid() == 0) {
879 setval(prompt, "# ");
880 prompt->status &= ~EXPORT;
881 }
882 cprompt = lookup("PS2");
883#ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
884 if (cprompt->value == null)
885#endif
886 setval(cprompt, "> ");
887
888 iof = filechar;
889 cflag = 0;
890 name = *argv++;
891 if (--argc >= 1) {
892 if (argv[0][0] == '-' && argv[0][1] != '\0') {
893 for (s = argv[0] + 1; *s; s++)
894 switch (*s) {
895 case 'c':
896 prompt->status &= ~EXPORT;
897 cprompt->status &= ~EXPORT;
898 setval(prompt, "");
899 setval(cprompt, "");
900 cflag = 1;
901 if (--argc > 0)
902 PUSHIO(aword, *++argv, iof = nlchar);
903 break;
904
905 case 'q':
906 qflag = SIG_DFL;
907 break;
908
909 case 's':
910 /* standard input */
911 break;
912
913 case 't':
914 prompt->status &= ~EXPORT;
915 setval(prompt, "");
916 iof = linechar;
917 break;
918
919 case 'i':
920 interactive++;
921 default:
922 if (*s >= 'a' && *s <= 'z')
923 flag[(int) *s]++;
924 }
925 } else {
926 argv--;
927 argc++;
928 }
929
930 if (iof == filechar && --argc > 0) {
931 setval(prompt, "");
932 setval(cprompt, "");
933 prompt->status &= ~EXPORT;
934 cprompt->status &= ~EXPORT;
935
936/* Shell is non-interactive, activate printf-based debug */
937#ifdef MSHDEBUG
938 mshdbg = (int) (((char) (mshdbg_var->value[0])) - '0');
939 if (mshdbg < 0)
940 mshdbg = 0;
941#endif
942 DBGPRINTF(("MSH_MAIN: calling newfile()\n"));
943
944 if (newfile(name = *++argv))
945 exit(1); /* Exit on error */
946 }
947 }
948
949 setdash();
950
951 /* This won't be true if PUSHIO has been called, say from newfile() above */
952 if (e.iop < iostack) {
953 PUSHIO(afile, 0, iof);
954 if (isatty(0) && isatty(1) && !cflag) {
955 interactive++;
956#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
957#ifdef MSHDEBUG
958 printf("\n\n" BB_BANNER " Built-in shell (msh with debug)\n");
959#else
960 printf("\n\n" BB_BANNER " Built-in shell (msh)\n");
961#endif
962 printf("Enter 'help' for a list of built-in commands.\n\n");
963#endif
964 }
965 }
966
967 signal(SIGQUIT, qflag);
968 if (name && name[0] == '-') {
969 interactive++;
970 if ((f = open(".profile", 0)) >= 0)
971 next(remap(f));
972 if ((f = open("/etc/profile", 0)) >= 0)
973 next(remap(f));
974 }
975 if (interactive)
976 signal(SIGTERM, sig);
977
978 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
979 signal(SIGINT, onintr);
980 dolv = argv;
981 dolc = argc;
982 dolv[0] = name;
983 if (dolc > 1) {
984 for (ap = ++argv; --argc > 0;) {
985 if (assign(*ap = *argv++, !COPYV)) {
986 dolc--; /* keyword */
987 } else {
988 ap++;
989 }
990 }
991 }
992 setval(lookup("#"), putn((--dolc < 0) ? (dolc = 0) : dolc));
993
994 DBGPRINTF(("MSH_MAIN: begin FOR loop, interactive %d, e.iop 0x%x, iostack 0x%x\n", interactive, e.iop, iostack));
995
996 for (;;) {
997 if (interactive && e.iop <= iostack) {
998#ifdef CONFIG_FEATURE_COMMAND_EDITING
999 current_prompt = prompt->value;
1000#else
1001 prs(prompt->value);
1002#endif
1003 }
1004 onecommand();
1005 /* Ensure that getenv("PATH") stays current */
1006 setenv("PATH", path->value, 1);
1007 }
1008
1009 DBGPRINTF(("MSH_MAIN: returning.\n"));
1010}
1011
1012static void setdash()
1013{
1014 REGISTER char *cp;
1015 REGISTER int c;
1016 char m['z' - 'a' + 1];
1017
1018 cp = m;
1019 for (c = 'a'; c <= 'z'; c++)
1020 if (flag[(int) c])
1021 *cp++ = c;
1022 *cp = 0;
1023 setval(lookup("-"), m);
1024}
1025
1026static int newfile(s)
1027REGISTER char *s;
1028{
1029 REGISTER int f;
1030
1031 DBGPRINTF7(("NEWFILE: opening %s\n", s));
1032
1033 if (strcmp(s, "-") != 0) {
1034 DBGPRINTF(("NEWFILE: s is %s\n", s));
1035 f = open(s, 0);
1036 if (f < 0) {
1037 prs(s);
1038 err(": cannot open");
1039 return (1);
1040 }
1041 } else
1042 f = 0;
1043
1044 next(remap(f));
1045 return (0);
1046}
1047
1048
1049struct op *scantree(head)
1050struct op *head;
1051{
1052 struct op *dotnode;
1053
1054 if (head == NULL)
1055 return (NULL);
1056
1057 if (head->left != NULL) {
1058 dotnode = scantree(head->left);
1059 if (dotnode)
1060 return (dotnode);
1061 }
1062
1063 if (head->right != NULL) {
1064 dotnode = scantree(head->right);
1065 if (dotnode)
1066 return (dotnode);
1067 }
1068
1069 if (head->words == NULL)
1070 return (NULL);
1071
1072 DBGPRINTF5(("SCANTREE: checking node 0x%x\n", head));
1073
1074 if ((head->type != TDOT) && (strcmp(".", head->words[0]) == 0)) {
1075 DBGPRINTF5(("SCANTREE: dot found in node 0x%x\n", head));
1076 return (head);
1077 }
1078
1079 return (NULL);
1080}
1081
1082
1083static void onecommand()
1084{
1085 REGISTER int i;
1086 jmp_buf m1;
1087
1088 DBGPRINTF(("ONECOMMAND: enter, outtree=0x%x\n", outtree));
1089
1090 while (e.oenv)
1091 quitenv();
1092
1093 areanum = 1;
1094 freehere(areanum);
1095 freearea(areanum);
1096 garbage();
1097 wdlist = 0;
1098 iolist = 0;
1099 e.errpt = 0;
1100 e.linep = line;
1101 yynerrs = 0;
1102 multiline = 0;
1103 inparse = 1;
1104 intr = 0;
1105 execflg = 0;
1106
1107 setjmp(failpt = m1); /* Bruce Evans' fix */
1108 if (setjmp(failpt = m1) || yyparse() || intr) {
1109
1110 DBGPRINTF(("ONECOMMAND: this is not good.\n"));
1111
1112 while (e.oenv)
1113 quitenv();
1114 scraphere();
1115 if (!interactive && intr)
1116 leave();
1117 inparse = 0;
1118 intr = 0;
1119 return;
1120 }
1121
1122 inparse = 0;
1123 brklist = 0;
1124 intr = 0;
1125 execflg = 0;
1126
1127 if (!flag['n']) {
1128 DBGPRINTF(("ONECOMMAND: calling execute, t=outtree=0x%x\n",
1129 outtree));
1130 execute(outtree, NOPIPE, NOPIPE, 0);
1131 }
1132
1133 if (!interactive && intr) {
1134 execflg = 0;
1135 leave();
1136 }
1137
1138 if ((i = trapset) != 0) {
1139 trapset = 0;
1140 runtrap(i);
1141 }
1142}
1143
1144static void fail()
1145{
1146 longjmp(failpt, 1);
1147 /* NOTREACHED */
1148}
1149
1150static void leave()
1151{
1152 DBGPRINTF(("LEAVE: leave called!\n"));
1153
1154 if (execflg)
1155 fail();
1156 scraphere();
1157 freehere(1);
1158 runtrap(0);
1159 _exit(exstat);
1160 /* NOTREACHED */
1161}
1162
1163static void warn(s)
1164REGISTER char *s;
1165{
1166 if (*s) {
1167 prs(s);
1168 exstat = -1;
1169 }
1170 prs("\n");
1171 if (flag['e'])
1172 leave();
1173}
1174
1175static void err(s)
1176char *s;
1177{
1178 warn(s);
1179 if (flag['n'])
1180 return;
1181 if (!interactive)
1182 leave();
1183 if (e.errpt)
1184 longjmp(e.errpt, 1);
1185 closeall();
1186 e.iop = e.iobase = iostack;
1187}
1188
1189static int newenv(f)
1190int f;
1191{
1192 REGISTER struct env *ep;
1193
1194 DBGPRINTF(("NEWENV: f=%d (indicates quitenv and return)\n", f));
1195
1196 if (f) {
1197 quitenv();
1198 return (1);
1199 }
1200
1201 ep = (struct env *) space(sizeof(*ep));
1202 if (ep == NULL) {
1203 while (e.oenv)
1204 quitenv();
1205 fail();
1206 }
1207 *ep = e;
1208 e.oenv = ep;
1209 e.errpt = errpt;
1210
1211 return (0);
1212}
1213
1214static void quitenv()
1215{
1216 REGISTER struct env *ep;
1217 REGISTER int fd;
1218
1219 DBGPRINTF(("QUITENV: e.oenv=0x%x\n", e.oenv));
1220
1221 if ((ep = e.oenv) != NULL) {
1222 fd = e.iofd;
1223 e = *ep;
1224 /* should close `'d files */
1225 DELETE(ep);
1226 while (--fd >= e.iofd)
1227 close(fd);
1228 }
1229}
1230
1231/*
1232 * Is any character from s1 in s2?
1233 */
1234static int anys(s1, s2)
1235REGISTER char *s1, *s2;
1236{
1237 while (*s1)
1238 if (any(*s1++, s2))
1239 return (1);
1240 return (0);
1241}
1242
1243/*
1244 * Is character c in s?
1245 */
1246static int any(c, s)
1247REGISTER int c;
1248REGISTER char *s;
1249{
1250 while (*s)
1251 if (*s++ == c)
1252 return (1);
1253 return (0);
1254}
1255
1256static char *putn(n)
1257REGISTER int n;
1258{
1259 return (itoa(n));
1260}
1261
1262static char *itoa(n)
1263REGISTER int n;
1264{
1265 static char s[20];
1266
1267 snprintf(s, sizeof(s), "%u", n);
1268 return (s);
1269}
1270
1271
1272static void next(int f)
1273{
1274 PUSHIO(afile, f, filechar);
1275}
1276
1277static void onintr(s)
1278int s; /* ANSI C requires a parameter */
1279{
1280 signal(SIGINT, onintr);
1281 intr = 1;
1282 if (interactive) {
1283 if (inparse) {
1284 prs("\n");
1285 fail();
1286 }
1287 } else if (heedint) {
1288 execflg = 0;
1289 leave();
1290 }
1291}
1292
1293static char *space(n)
1294int n;
1295{
1296 REGISTER char *cp;
1297
1298 if ((cp = getcell(n)) == 0)
1299 err("out of string space");
1300 return (cp);
1301}
1302
1303static char *strsave(s, a)
1304REGISTER char *s;
1305int a;
1306{
1307 REGISTER char *cp, *xp;
1308
1309 if ((cp = space(strlen(s) + 1)) != NULL) {
1310 setarea((char *) cp, a);
1311 for (xp = cp; (*xp++ = *s++) != '\0';);
1312 return (cp);
1313 }
1314 return ("");
1315}
1316
1317/*
1318 * trap handling
1319 */
1320static void sig(i)
1321REGISTER int i;
1322{
1323 trapset = i;
1324 signal(i, sig);
1325}
1326
1327static void runtrap(i)
1328int i;
1329{
1330 char *trapstr;
1331
1332 if ((trapstr = trap[i]) == NULL)
1333 return;
1334
1335 if (i == 0)
1336 trap[i] = 0;
1337
1338 RUN(aword, trapstr, nlchar);
1339}
1340
1341/* -------- var.c -------- */
1342
1343/*
1344 * Find the given name in the dictionary
1345 * and return its value. If the name was
1346 * not previously there, enter it now and
1347 * return a null value.
1348 */
1349static struct var *lookup(n)
1350REGISTER char *n;
1351{
1352 REGISTER struct var *vp;
1353 REGISTER char *cp;
1354 REGISTER int c;
1355 static struct var dummy;
1356
1357 if (isdigit(*n)) {
1358 dummy.name = n;
1359 for (c = 0; isdigit(*n) && c < 1000; n++)
1360 c = c * 10 + *n - '0';
1361 dummy.status = RONLY;
1362 dummy.value = c <= dolc ? dolv[c] : null;
1363 return (&dummy);
1364 }
1365 for (vp = vlist; vp; vp = vp->next)
1366 if (eqname(vp->name, n))
1367 return (vp);
1368 cp = findeq(n);
1369 vp = (struct var *) space(sizeof(*vp));
1370 if (vp == 0 || (vp->name = space((int) (cp - n) + 2)) == 0) {
1371 dummy.name = dummy.value = "";
1372 return (&dummy);
1373 }
1374 for (cp = vp->name; (*cp = *n++) && *cp != '='; cp++);
1375 if (*cp == 0)
1376 *cp = '=';
1377 *++cp = 0;
1378 setarea((char *) vp, 0);
1379 setarea((char *) vp->name, 0);
1380 vp->value = null;
1381 vp->next = vlist;
1382 vp->status = GETCELL;
1383 vlist = vp;
1384 return (vp);
1385}
1386
1387/*
1388 * give variable at `vp' the value `val'.
1389 */
1390static void setval(vp, val)
1391struct var *vp;
1392char *val;
1393{
1394 nameval(vp, val, (char *) NULL);
1395}
1396
1397/*
1398 * if name is not NULL, it must be
1399 * a prefix of the space `val',
1400 * and end with `='.
1401 * this is all so that exporting
1402 * values is reasonably painless.
1403 */
1404static void nameval(vp, val, name)
1405REGISTER struct var *vp;
1406char *val, *name;
1407{
1408 REGISTER char *cp, *xp;
1409 char *nv;
1410 int fl;
1411
1412 if (vp->status & RONLY) {
1413 for (xp = vp->name; *xp && *xp != '=';)
1414 putc(*xp++, stderr);
1415 err(" is read-only");
1416 return;
1417 }
1418 fl = 0;
1419 if (name == NULL) {
1420 xp = space(strlen(vp->name) + strlen(val) + 2);
1421 if (xp == 0)
1422 return;
1423 /* make string: name=value */
1424 setarea((char *) xp, 0);
1425 name = xp;
1426 for (cp = vp->name; (*xp = *cp++) && *xp != '='; xp++);
1427 if (*xp++ == 0)
1428 xp[-1] = '=';
1429 nv = xp;
1430 for (cp = val; (*xp++ = *cp++) != '\0';);
1431 val = nv;
1432 fl = GETCELL;
1433 }
1434 if (vp->status & GETCELL)
1435 freecell(vp->name); /* form new string `name=value' */
1436 vp->name = name;
1437 vp->value = val;
1438 vp->status |= fl;
1439}
1440
1441static void export(vp)
1442struct var *vp;
1443{
1444 vp->status |= EXPORT;
1445}
1446
1447static void ronly(vp)
1448struct var *vp;
1449{
1450 if (isalpha(vp->name[0]) || vp->name[0] == '_') /* not an internal symbol */
1451 vp->status |= RONLY;
1452}
1453
1454static int isassign(s)
1455REGISTER char *s;
1456{
1457 DBGPRINTF7(("ISASSIGN: enter, s=%s\n", s));
1458
1459 if (!isalpha((int) *s) && *s != '_')
1460 return (0);
1461 for (; *s != '='; s++)
1462 if (*s == 0 || (!isalnum(*s) && *s != '_'))
1463 return (0);
1464
1465 return (1);
1466}
1467
1468static int assign(s, cf)
1469REGISTER char *s;
1470int cf;
1471{
1472 REGISTER char *cp;
1473 struct var *vp;
1474
1475 DBGPRINTF7(("ASSIGN: enter, s=%s, cf=%d\n", s, cf));
1476
1477 if (!isalpha(*s) && *s != '_')
1478 return (0);
1479 for (cp = s; *cp != '='; cp++)
1480 if (*cp == 0 || (!isalnum(*cp) && *cp != '_'))
1481 return (0);
1482 vp = lookup(s);
1483 nameval(vp, ++cp, cf == COPYV ? (char *) NULL : s);
1484 if (cf != COPYV)
1485 vp->status &= ~GETCELL;
1486 return (1);
1487}
1488
1489static int checkname(cp)
1490REGISTER char *cp;
1491{
1492 DBGPRINTF7(("CHECKNAME: enter, cp=%s\n", cp));
1493
1494 if (!isalpha(*cp++) && *(cp - 1) != '_')
1495 return (0);
1496 while (*cp)
1497 if (!isalnum(*cp++) && *(cp - 1) != '_')
1498 return (0);
1499 return (1);
1500}
1501
1502static void putvlist(f, out)
1503REGISTER int f, out;
1504{
1505 REGISTER struct var *vp;
1506
1507 for (vp = vlist; vp; vp = vp->next)
1508 if (vp->status & f && (isalpha(*vp->name) || *vp->name == '_')) {
1509 if (vp->status & EXPORT)
1510 write(out, "export ", 7);
1511 if (vp->status & RONLY)
1512 write(out, "readonly ", 9);
1513 write(out, vp->name, (int) (findeq(vp->name) - vp->name));
1514 write(out, "\n", 1);
1515 }
1516}
1517
1518static int eqname(n1, n2)
1519REGISTER char *n1, *n2;
1520{
1521 for (; *n1 != '=' && *n1 != 0; n1++)
1522 if (*n2++ != *n1)
1523 return (0);
1524 return (*n2 == 0 || *n2 == '=');
1525}
1526
1527static char *findeq(cp)
1528REGISTER char *cp;
1529{
1530 while (*cp != '\0' && *cp != '=')
1531 cp++;
1532 return (cp);
1533}
1534
1535/* -------- gmatch.c -------- */
1536/*
1537 * int gmatch(string, pattern)
1538 * char *string, *pattern;
1539 *
1540 * Match a pattern as in sh(1).
1541 */
1542
1543#define CMASK 0377
1544#define QUOTE 0200
1545#define QMASK (CMASK&~QUOTE)
1546#define NOT '!' /* might use ^ */
1547
1548static int gmatch(s, p)
1549REGISTER char *s, *p;
1550{
1551 REGISTER int sc, pc;
1552
1553 if (s == NULL || p == NULL)
1554 return (0);
1555 while ((pc = *p++ & CMASK) != '\0') {
1556 sc = *s++ & QMASK;
1557 switch (pc) {
1558 case '[':
1559 if ((p = cclass(p, sc)) == NULL)
1560 return (0);
1561 break;
1562
1563 case '?':
1564 if (sc == 0)
1565 return (0);
1566 break;
1567
1568 case '*':
1569 s--;
1570 do {
1571 if (*p == '\0' || gmatch(s, p))
1572 return (1);
1573 } while (*s++ != '\0');
1574 return (0);
1575
1576 default:
1577 if (sc != (pc & ~QUOTE))
1578 return (0);
1579 }
1580 }
1581 return (*s == 0);
1582}
1583
1584static char *cclass(p, sub)
1585REGISTER char *p;
1586REGISTER int sub;
1587{
1588 REGISTER int c, d, not, found;
1589
1590 if ((not = *p == NOT) != 0)
1591 p++;
1592 found = not;
1593 do {
1594 if (*p == '\0')
1595 return ((char *) NULL);
1596 c = *p & CMASK;
1597 if (p[1] == '-' && p[2] != ']') {
1598 d = p[2] & CMASK;
1599 p++;
1600 } else
1601 d = c;
1602 if (c == sub || (c <= sub && sub <= d))
1603 found = !not;
1604 } while (*++p != ']');
1605 return (found ? p + 1 : (char *) NULL);
1606}
1607
1608
1609/* -------- area.c -------- */
1610
1611/*
1612 * All memory between (char *)areabot and (char *)(areatop+1) is
1613 * exclusively administered by the area management routines.
1614 * It is assumed that sbrk() and brk() manipulate the high end.
1615 */
1616
1617#define sbrk(X) ({ void * __q = (void *)-1; if (brkaddr + (int)(X) < brktop) { __q = brkaddr; brkaddr+=(int)(X); } __q;})
1618
1619static void initarea()
1620{
1621 brkaddr = malloc(AREASIZE);
1622 brktop = brkaddr + AREASIZE;
1623
1624 while ((int) sbrk(0) & ALIGN)
1625 sbrk(1);
1626 areabot = (struct region *) sbrk(REGSIZE);
1627
1628 areabot->next = areabot;
1629 areabot->area = BUSY;
1630 areatop = areabot;
1631 areanxt = areabot;
1632}
1633
1634char *getcell(nbytes)
1635unsigned nbytes;
1636{
1637 REGISTER int nregio;
1638 REGISTER struct region *p, *q;
1639 REGISTER int i;
1640
1641 if (nbytes == 0) {
1642 puts("getcell(0)");
1643 abort();
1644 }
1645 /* silly and defeats the algorithm */
1646 /*
1647 * round upwards and add administration area
1648 */
1649 nregio = (nbytes + (REGSIZE - 1)) / REGSIZE + 1;
1650 for (p = areanxt;;) {
1651 if (p->area > areanum) {
1652 /*
1653 * merge free cells
1654 */
1655 while ((q = p->next)->area > areanum && q != areanxt)
1656 p->next = q->next;
1657 /*
1658 * exit loop if cell big enough
1659 */
1660 if (q >= p + nregio)
1661 goto found;
1662 }
1663 p = p->next;
1664 if (p == areanxt)
1665 break;
1666 }
1667 i = nregio >= GROWBY ? nregio : GROWBY;
1668 p = (struct region *) sbrk(i * REGSIZE);
1669 if (p == (struct region *) -1)
1670 return ((char *) NULL);
1671 p--;
1672 if (p != areatop) {
1673 puts("not contig");
1674 abort(); /* allocated areas are contiguous */
1675 }
1676 q = p + i;
1677 p->next = q;
1678 p->area = FREE;
1679 q->next = areabot;
1680 q->area = BUSY;
1681 areatop = q;
1682 found:
1683 /*
1684 * we found a FREE area big enough, pointed to by 'p', and up to 'q'
1685 */
1686 areanxt = p + nregio;
1687 if (areanxt < q) {
1688 /*
1689 * split into requested area and rest
1690 */
1691 if (areanxt + 1 > q) {
1692 puts("OOM");
1693 abort(); /* insufficient space left for admin */
1694 }
1695 areanxt->next = q;
1696 areanxt->area = FREE;
1697 p->next = areanxt;
1698 }
1699 p->area = areanum;
1700 return ((char *) (p + 1));
1701}
1702
1703static void freecell(cp)
1704char *cp;
1705{
1706 REGISTER struct region *p;
1707
1708 if ((p = (struct region *) cp) != NULL) {
1709 p--;
1710 if (p < areanxt)
1711 areanxt = p;
1712 p->area = FREE;
1713 }
1714}
1715
1716static void freearea(a)
1717REGISTER int a;
1718{
1719 REGISTER struct region *p, *top;
1720
1721 top = areatop;
1722 for (p = areabot; p != top; p = p->next)
1723 if (p->area >= a)
1724 p->area = FREE;
1725}
1726
1727static void setarea(cp, a)
1728char *cp;
1729int a;
1730{
1731 REGISTER struct region *p;
1732
1733 if ((p = (struct region *) cp) != NULL)
1734 (p - 1)->area = a;
1735}
1736
1737int getarea(cp)
1738char *cp;
1739{
1740 return ((struct region *) cp - 1)->area;
1741}
1742
1743static void garbage()
1744{
1745 REGISTER struct region *p, *q, *top;
1746
1747 top = areatop;
1748 for (p = areabot; p != top; p = p->next) {
1749 if (p->area > areanum) {
1750 while ((q = p->next)->area > areanum)
1751 p->next = q->next;
1752 areanxt = p;
1753 }
1754 }
1755#ifdef SHRINKBY
1756 if (areatop >= q + SHRINKBY && q->area > areanum) {
1757 brk((char *) (q + 1));
1758 q->next = areabot;
1759 q->area = BUSY;
1760 areatop = q;
1761 }
1762#endif
1763}
1764
1765/* -------- csyn.c -------- */
1766/*
1767 * shell: syntax (C version)
1768 */
1769
1770int yyparse()
1771{
1772 DBGPRINTF7(("YYPARSE: enter...\n"));
1773
1774 startl = 1;
1775 peeksym = 0;
1776 yynerrs = 0;
1777 outtree = c_list();
1778 musthave('\n', 0);
1779 return (yynerrs != 0);
1780}
1781
1782static struct op *pipeline(cf)
1783int cf;
1784{
1785 REGISTER struct op *t, *p;
1786 REGISTER int c;
1787
1788 DBGPRINTF7(("PIPELINE: enter, cf=%d\n", cf));
1789
1790 t = command(cf);
1791
1792 DBGPRINTF9(("PIPELINE: t=0x%x\n", t));
1793
1794 if (t != NULL) {
1795 while ((c = yylex(0)) == '|') {
1796 if ((p = command(CONTIN)) == NULL) {
1797 DBGPRINTF8(("PIPELINE: error!\n"));
1798 SYNTAXERR;
1799 }
1800
1801 if (t->type != TPAREN && t->type != TCOM) {
1802 /* shell statement */
1803 t = block(TPAREN, t, NOBLOCK, NOWORDS);
1804 }
1805
1806 t = block(TPIPE, t, p, NOWORDS);
1807 }
1808 peeksym = c;
1809 }
1810
1811 DBGPRINTF7(("PIPELINE: returning t=0x%x\n", t));
1812 return (t);
1813}
1814
1815static struct op *andor()
1816{
1817 REGISTER struct op *t, *p;
1818 REGISTER int c;
1819
1820 DBGPRINTF7(("ANDOR: enter...\n"));
1821
1822 t = pipeline(0);
1823
1824 DBGPRINTF9(("ANDOR: t=0x%x\n", t));
1825
1826 if (t != NULL) {
1827 while ((c = yylex(0)) == LOGAND || c == LOGOR) {
1828 if ((p = pipeline(CONTIN)) == NULL) {
1829 DBGPRINTF8(("ANDOR: error!\n"));
1830 SYNTAXERR;
1831 }
1832
1833 t = block(c == LOGAND ? TAND : TOR, t, p, NOWORDS);
1834 } /* WHILE */
1835
1836 peeksym = c;
1837 }
1838
1839 DBGPRINTF7(("ANDOR: returning t=0x%x\n", t));
1840 return (t);
1841}
1842
1843static struct op *c_list()
1844{
1845 REGISTER struct op *t, *p;
1846 REGISTER int c;
1847
1848 DBGPRINTF7(("C_LIST: enter...\n"));
1849
1850 t = andor();
1851
1852 if (t != NULL) {
1853 if ((peeksym = yylex(0)) == '&')
1854 t = block(TASYNC, t, NOBLOCK, NOWORDS);
1855
1856 while ((c = yylex(0)) == ';' || c == '&'
1857 || (multiline && c == '\n')) {
1858
1859 if ((p = andor()) == NULL)
1860 return (t);
1861
1862 if ((peeksym = yylex(0)) == '&')
1863 p = block(TASYNC, p, NOBLOCK, NOWORDS);
1864
1865 t = list(t, p);
1866 } /* WHILE */
1867
1868 peeksym = c;
1869 }
1870 /* IF */
1871 DBGPRINTF7(("C_LIST: returning t=0x%x\n", t));
1872 return (t);
1873}
1874
1875static int synio(cf)
1876int cf;
1877{
1878 REGISTER struct ioword *iop;
1879 REGISTER int i;
1880 REGISTER int c;
1881
1882 DBGPRINTF7(("SYNIO: enter, cf=%d\n", cf));
1883
1884 if ((c = yylex(cf)) != '<' && c != '>') {
1885 peeksym = c;
1886 return (0);
1887 }
1888
1889 i = yylval.i;
1890 musthave(WORD, 0);
1891 iop = io(iounit, i, yylval.cp);
1892 iounit = IODEFAULT;
1893
1894 if (i & IOHERE)
1895 markhere(yylval.cp, iop);
1896
1897 DBGPRINTF7(("SYNIO: returning 1\n"));
1898 return (1);
1899}
1900
1901static void musthave(c, cf)
1902int c, cf;
1903{
1904 if ((peeksym = yylex(cf)) != c) {
1905 DBGPRINTF7(("MUSTHAVE: error!\n"));
1906 SYNTAXERR;
1907 }
1908
1909 peeksym = 0;
1910}
1911
1912static struct op *simple()
1913{
1914 REGISTER struct op *t;
1915
1916 t = NULL;
1917 for (;;) {
1918 switch (peeksym = yylex(0)) {
1919 case '<':
1920 case '>':
1921 (void) synio(0);
1922 break;
1923
1924 case WORD:
1925 if (t == NULL) {
1926 t = newtp();
1927 t->type = TCOM;
1928 }
1929 peeksym = 0;
1930 word(yylval.cp);
1931 break;
1932
1933 default:
1934 return (t);
1935 }
1936 }
1937}
1938
1939static struct op *nested(type, mark)
1940int type, mark;
1941{
1942 REGISTER struct op *t;
1943
1944 DBGPRINTF3(("NESTED: enter, type=%d, mark=%d\n", type, mark));
1945
1946 multiline++;
1947 t = c_list();
1948 musthave(mark, 0);
1949 multiline--;
1950 return (block(type, t, NOBLOCK, NOWORDS));
1951}
1952
1953static struct op *command(cf)
1954int cf;
1955{
1956 REGISTER struct op *t;
1957 struct wdblock *iosave;
1958 REGISTER int c;
1959
1960 DBGPRINTF(("COMMAND: enter, cf=%d\n", cf));
1961
1962 iosave = iolist;
1963 iolist = NULL;
1964
1965 if (multiline)
1966 cf |= CONTIN;
1967
1968 while (synio(cf))
1969 cf = 0;
1970
1971 c = yylex(cf);
1972
1973 switch (c) {
1974 default:
1975 peeksym = c;
1976 if ((t = simple()) == NULL) {
1977 if (iolist == NULL)
1978 return ((struct op *) NULL);
1979 t = newtp();
1980 t->type = TCOM;
1981 }
1982 break;
1983
1984 case '(':
1985 t = nested(TPAREN, ')');
1986 break;
1987
1988 case '{':
1989 t = nested(TBRACE, '}');
1990 break;
1991
1992 case FOR:
1993 t = newtp();
1994 t->type = TFOR;
1995 musthave(WORD, 0);
1996 startl = 1;
1997 t->str = yylval.cp;
1998 multiline++;
1999 t->words = wordlist();
2000 if ((c = yylex(0)) != '\n' && c != ';')
2001 peeksym = c;
2002 t->left = dogroup(0);
2003 multiline--;
2004 break;
2005
2006 case WHILE:
2007 case UNTIL:
2008 multiline++;
2009 t = newtp();
2010 t->type = c == WHILE ? TWHILE : TUNTIL;
2011 t->left = c_list();
2012 t->right = dogroup(1);
2013 t->words = NULL;
2014 multiline--;
2015 break;
2016
2017 case CASE:
2018 t = newtp();
2019 t->type = TCASE;
2020 musthave(WORD, 0);
2021 t->str = yylval.cp;
2022 startl++;
2023 multiline++;
2024 musthave(IN, CONTIN);
2025 startl++;
2026
2027 t->left = caselist();
2028
2029 musthave(ESAC, 0);
2030 multiline--;
2031 break;
2032
2033 case IF:
2034 multiline++;
2035 t = newtp();
2036 t->type = TIF;
2037 t->left = c_list();
2038 t->right = thenpart();
2039 musthave(FI, 0);
2040 multiline--;
2041 break;
2042
2043 case DOT:
2044 t = newtp();
2045 t->type = TDOT;
2046
2047 musthave(WORD, 0); /* gets name of file */
2048 DBGPRINTF7(("COMMAND: DOT clause, yylval.cp is %s\n", yylval.cp));
2049
2050 word(yylval.cp); /* add word to wdlist */
2051 word(NOWORD); /* terminate wdlist */
2052 t->words = copyw(); /* dup wdlist */
2053 break;
2054
2055 }
2056
2057 while (synio(0));
2058
2059 t = namelist(t);
2060 iolist = iosave;
2061
2062 DBGPRINTF(("COMMAND: returning 0x%x\n", t));
2063
2064 return (t);
2065}
2066
2067static struct op *dowholefile(type, mark)
2068int type;
2069int mark;
2070{
2071 REGISTER struct op *t;
2072
2073 DBGPRINTF(("DOWHOLEFILE: enter, type=%d, mark=%d\n", type, mark));
2074
2075 multiline++;
2076 t = c_list();
2077 multiline--;
2078 t = block(type, t, NOBLOCK, NOWORDS);
2079 DBGPRINTF(("DOWHOLEFILE: return t=0x%x\n", t));
2080 return (t);
2081}
2082
2083static struct op *dogroup(onlydone)
2084int onlydone;
2085{
2086 REGISTER int c;
2087 REGISTER struct op *mylist;
2088
2089 c = yylex(CONTIN);
2090 if (c == DONE && onlydone)
2091 return ((struct op *) NULL);
2092 if (c != DO)
2093 SYNTAXERR;
2094 mylist = c_list();
2095 musthave(DONE, 0);
2096 return (mylist);
2097}
2098
2099static struct op *thenpart()
2100{
2101 REGISTER int c;
2102 REGISTER struct op *t;
2103
2104 if ((c = yylex(0)) != THEN) {
2105 peeksym = c;
2106 return ((struct op *) NULL);
2107 }
2108 t = newtp();
2109 t->type = 0;
2110 t->left = c_list();
2111 if (t->left == NULL)
2112 SYNTAXERR;
2113 t->right = elsepart();
2114 return (t);
2115}
2116
2117static struct op *elsepart()
2118{
2119 REGISTER int c;
2120 REGISTER struct op *t;
2121
2122 switch (c = yylex(0)) {
2123 case ELSE:
2124 if ((t = c_list()) == NULL)
2125 SYNTAXERR;
2126 return (t);
2127
2128 case ELIF:
2129 t = newtp();
2130 t->type = TELIF;
2131 t->left = c_list();
2132 t->right = thenpart();
2133 return (t);
2134
2135 default:
2136 peeksym = c;
2137 return ((struct op *) NULL);
2138 }
2139}
2140
2141static struct op *caselist()
2142{
2143 REGISTER struct op *t;
2144
2145 t = NULL;
2146 while ((peeksym = yylex(CONTIN)) != ESAC) {
2147 DBGPRINTF(("CASELIST, doing yylex, peeksym=%d\n", peeksym));
2148 t = list(t, casepart());
2149 }
2150
2151 DBGPRINTF(("CASELIST, returning t=0x%x\n", t));
2152 return (t);
2153}
2154
2155static struct op *casepart()
2156{
2157 REGISTER struct op *t;
2158
2159 DBGPRINTF7(("CASEPART: enter...\n"));
2160
2161 t = newtp();
2162 t->type = TPAT;
2163 t->words = pattern();
2164 musthave(')', 0);
2165 t->left = c_list();
2166 if ((peeksym = yylex(CONTIN)) != ESAC)
2167 musthave(BREAK, CONTIN);
2168
2169 DBGPRINTF7(("CASEPART: made newtp(TPAT, t=0x%x)\n", t));
2170
2171 return (t);
2172}
2173
2174static char **pattern()
2175{
2176 REGISTER int c, cf;
2177
2178 cf = CONTIN;
2179 do {
2180 musthave(WORD, cf);
2181 word(yylval.cp);
2182 cf = 0;
2183 } while ((c = yylex(0)) == '|');
2184 peeksym = c;
2185 word(NOWORD);
2186
2187 return (copyw());
2188}
2189
2190static char **wordlist()
2191{
2192 REGISTER int c;
2193
2194 if ((c = yylex(0)) != IN) {
2195 peeksym = c;
2196 return ((char **) NULL);
2197 }
2198 startl = 0;
2199 while ((c = yylex(0)) == WORD)
2200 word(yylval.cp);
2201 word(NOWORD);
2202 peeksym = c;
2203 return (copyw());
2204}
2205
2206/*
2207 * supporting functions
2208 */
2209static struct op *list(t1, t2)
2210REGISTER struct op *t1, *t2;
2211{
2212 DBGPRINTF7(("LIST: enter, t1=0x%x, t2=0x%x\n", t1, t2));
2213
2214 if (t1 == NULL)
2215 return (t2);
2216 if (t2 == NULL)
2217 return (t1);
2218
2219 return (block(TLIST, t1, t2, NOWORDS));
2220}
2221
2222static struct op *block(type, t1, t2, wp)
2223int type;
2224struct op *t1, *t2;
2225char **wp;
2226{
2227 REGISTER struct op *t;
2228
2229 DBGPRINTF7(("BLOCK: enter, type=%d (%s)\n", type, T_CMD_NAMES[type]));
2230
2231 t = newtp();
2232 t->type = type;
2233 t->left = t1;
2234 t->right = t2;
2235 t->words = wp;
2236
2237 DBGPRINTF7(("BLOCK: inserted 0x%x between 0x%x and 0x%x\n", t, t1,
2238 t2));
2239
2240 return (t);
2241}
2242
2243/* See if given string is a shell multiline (FOR, IF, etc) */
2244static int rlookup(n)
2245REGISTER char *n;
2246{
2247 REGISTER struct res *rp;
2248
2249 DBGPRINTF7(("RLOOKUP: enter, n is %s\n", n));
2250
2251 for (rp = restab; rp->r_name; rp++)
2252 if (strcmp(rp->r_name, n) == 0) {
2253 DBGPRINTF7(("RLOOKUP: match, returning %d\n", rp->r_val));
2254 return (rp->r_val); /* Return numeric code for shell multiline */
2255 }
2256
2257 DBGPRINTF7(("RLOOKUP: NO match, returning 0\n"));
2258 return (0); /* Not a shell multiline */
2259}
2260
2261static struct op *newtp()
2262{
2263 REGISTER struct op *t;
2264
2265 t = (struct op *) tree(sizeof(*t));
2266 t->type = 0;
2267 t->words = NULL;
2268 t->ioact = NULL;
2269 t->left = NULL;
2270 t->right = NULL;
2271 t->str = NULL;
2272
2273 DBGPRINTF3(("NEWTP: allocated 0x%x\n", t));
2274
2275 return (t);
2276}
2277
2278static struct op *namelist(t)
2279REGISTER struct op *t;
2280{
2281
2282 DBGPRINTF7(("NAMELIST: enter, t=0x%x, type %s, iolist=0x%x\n", t,
2283 T_CMD_NAMES[t->type], iolist));
2284
2285 if (iolist) {
2286 iolist = addword((char *) NULL, iolist);
2287 t->ioact = copyio();
2288 } else
2289 t->ioact = NULL;
2290
2291 if (t->type != TCOM) {
2292 if (t->type != TPAREN && t->ioact != NULL) {
2293 t = block(TPAREN, t, NOBLOCK, NOWORDS);
2294 t->ioact = t->left->ioact;
2295 t->left->ioact = NULL;
2296 }
2297 return (t);
2298 }
2299
2300 word(NOWORD);
2301 t->words = copyw();
2302
2303
2304 return (t);
2305}
2306
2307static char **copyw()
2308{
2309 REGISTER char **wd;
2310
2311 wd = getwords(wdlist);
2312 wdlist = 0;
2313 return (wd);
2314}
2315
2316static void word(cp)
2317char *cp;
2318{
2319 wdlist = addword(cp, wdlist);
2320}
2321
2322static struct ioword **copyio()
2323{
2324 REGISTER struct ioword **iop;
2325
2326 iop = (struct ioword **) getwords(iolist);
2327 iolist = 0;
2328 return (iop);
2329}
2330
2331static struct ioword *io(u, f, cp)
2332int u;
2333int f;
2334char *cp;
2335{
2336 REGISTER struct ioword *iop;
2337
2338 iop = (struct ioword *) tree(sizeof(*iop));
2339 iop->io_unit = u;
2340 iop->io_flag = f;
2341 iop->io_name = cp;
2342 iolist = addword((char *) iop, iolist);
2343 return (iop);
2344}
2345
2346static void zzerr()
2347{
2348 yyerror("syntax error");
2349}
2350
2351static void yyerror(s)
2352char *s;
2353{
2354 yynerrs++;
2355 if (interactive && e.iop <= iostack) {
2356 multiline = 0;
2357 while (eofc() == 0 && yylex(0) != '\n');
2358 }
2359 err(s);
2360 fail();
2361}
2362
2363static int yylex(cf)
2364int cf;
2365{
2366 REGISTER int c, c1;
2367 int atstart;
2368
2369 if ((c = peeksym) > 0) {
2370 peeksym = 0;
2371 if (c == '\n')
2372 startl = 1;
2373 return (c);
2374 }
2375
2376
2377 nlseen = 0;
2378 atstart = startl;
2379 startl = 0;
2380 yylval.i = 0;
2381 e.linep = line;
2382
2383/* MALAMO */
2384 line[LINELIM - 1] = '\0';
2385
2386 loop:
2387 while ((c = my_getc(0)) == ' ' || c == '\t') /* Skip whitespace */
2388 ;
2389
2390 switch (c) {
2391 default:
2392 if (any(c, "0123456789")) {
2393 unget(c1 = my_getc(0));
2394 if (c1 == '<' || c1 == '>') {
2395 iounit = c - '0';
2396 goto loop;
2397 }
2398 *e.linep++ = c;
2399 c = c1;
2400 }
2401 break;
2402
2403 case '#': /* Comment, skip to next newline or End-of-string */
2404 while ((c = my_getc(0)) != 0 && c != '\n');
2405 unget(c);
2406 goto loop;
2407
2408 case 0:
2409 DBGPRINTF5(("YYLEX: return 0, c=%d\n", c));
2410 return (c);
2411
2412 case '$':
2413 DBGPRINTF9(("YYLEX: found $\n"));
2414 *e.linep++ = c;
2415 if ((c = my_getc(0)) == '{') {
2416 if ((c = collect(c, '}')) != '\0')
2417 return (c);
2418 goto pack;
2419 }
2420 break;
2421
2422 case '`':
2423 case '\'':
2424 case '"':
2425 if ((c = collect(c, c)) != '\0')
2426 return (c);
2427 goto pack;
2428
2429 case '|':
2430 case '&':
2431 case ';':
2432 startl = 1;
2433 /* If more chars process them, else return NULL char */
2434 if ((c1 = dual(c)) != '\0')
2435 return (c1);
2436 else
2437 return (c);
2438
2439 case '^':
2440 startl = 1;
2441 return ('|');
2442 case '>':
2443 case '<':
2444 diag(c);
2445 return (c);
2446
2447 case '\n':
2448 nlseen++;
2449 gethere();
2450 startl = 1;
2451 if (multiline || cf & CONTIN) {
2452 if (interactive && e.iop <= iostack) {
2453#ifdef CONFIG_FEATURE_COMMAND_EDITING
2454 current_prompt = cprompt->value;
2455#else
2456 prs(cprompt->value);
2457#endif
2458 }
2459 if (cf & CONTIN)
2460 goto loop;
2461 }
2462 return (c);
2463
2464 case '(':
2465 case ')':
2466 startl = 1;
2467 return (c);
2468 }
2469
2470 unget(c);
2471
2472 pack:
2473 while ((c = my_getc(0)) != 0 && !any(c, "`$ '\"\t;&<>()|^\n")) {
2474 if (e.linep >= elinep)
2475 err("word too long");
2476 else
2477 *e.linep++ = c;
2478 };
2479
2480 unget(c);
2481
2482 if (any(c, "\"'`$"))
2483 goto loop;
2484
2485 *e.linep++ = '\0';
2486
2487 if (atstart && (c = rlookup(line)) != 0) {
2488 startl = 1;
2489 return (c);
2490 }
2491
2492 yylval.cp = strsave(line, areanum);
2493 return (WORD);
2494}
2495
2496
2497static int collect(c, c1)
2498REGISTER int c, c1;
2499{
2500 char s[2];
2501
2502 DBGPRINTF8(("COLLECT: enter, c=%d, c1=%d\n", c, c1));
2503
2504 *e.linep++ = c;
2505 while ((c = my_getc(c1)) != c1) {
2506 if (c == 0) {
2507 unget(c);
2508 s[0] = c1;
2509 s[1] = 0;
2510 prs("no closing ");
2511 yyerror(s);
2512 return (YYERRCODE);
2513 }
2514 if (interactive && c == '\n' && e.iop <= iostack) {
2515#ifdef CONFIG_FEATURE_COMMAND_EDITING
2516 current_prompt = cprompt->value;
2517#else
2518 prs(cprompt->value);
2519#endif
2520 }
2521 *e.linep++ = c;
2522 }
2523
2524 *e.linep++ = c;
2525
2526 DBGPRINTF8(("COLLECT: return 0, line is %s\n", line));
2527
2528 return (0);
2529}
2530
2531/* "multiline commands" helper func */
2532/* see if next 2 chars form a shell multiline */
2533static int dual(c)
2534REGISTER int c;
2535{
2536 char s[3];
2537 REGISTER char *cp = s;
2538
2539 DBGPRINTF8(("DUAL: enter, c=%d\n", c));
2540
2541 *cp++ = c; /* c is the given "peek" char */
2542 *cp++ = my_getc(0); /* get next char of input */
2543 *cp = 0; /* add EOS marker */
2544
2545 c = rlookup(s); /* see if 2 chars form a shell multiline */
2546 if (c == 0)
2547 unget(*--cp); /* String is not a shell multiline, put peek char back */
2548
2549 return (c); /* String is multiline, return numeric multiline (restab) code */
2550}
2551
2552static void diag(ec)
2553REGISTER int ec;
2554{
2555 REGISTER int c;
2556
2557 DBGPRINTF8(("DIAG: enter, ec=%d\n", ec));
2558
2559 c = my_getc(0);
2560 if (c == '>' || c == '<') {
2561 if (c != ec)
2562 zzerr();
2563 yylval.i = ec == '>' ? IOWRITE | IOCAT : IOHERE;
2564 c = my_getc(0);
2565 } else
2566 yylval.i = ec == '>' ? IOWRITE : IOREAD;
2567 if (c != '&' || yylval.i == IOHERE)
2568 unget(c);
2569 else
2570 yylval.i |= IODUP;
2571}
2572
2573static char *tree(size)
2574unsigned size;
2575{
2576 REGISTER char *t;
2577
2578 if ((t = getcell(size)) == NULL) {
2579 DBGPRINTF2(("TREE: getcell(%d) failed!\n", size));
2580 prs("command line too complicated\n");
2581 fail();
2582 /* NOTREACHED */
2583 }
2584 return (t);
2585}
2586
2587/* VARARGS1 */
2588/* ARGSUSED */
2589
2590/* -------- exec.c -------- */
2591
2592/*
2593 * execute tree
2594 */
2595
2596
2597static int execute(t, pin, pout, act)
2598REGISTER struct op *t;
2599int *pin, *pout;
2600int act;
2601{
2602 REGISTER struct op *t1;
2603 volatile int i, rv, a;
2604 char *cp, **wp, **wp2;
2605 struct var *vp;
2606 struct op *outtree_save;
2607 struct brkcon bc;
2608
2609#if __GNUC__
2610 /* Avoid longjmp clobbering */
2611 (void) &wp;
2612#endif
2613
2614 if (t == NULL) {
2615 DBGPRINTF4(("EXECUTE: enter, t==null, returning.\n"));
2616 return (0);
2617 }
2618
2619 DBGPRINTF(("EXECUTE: t=0x%x, t->type=%d (%s), t->words is %s\n", t,
2620 t->type, T_CMD_NAMES[t->type],
2621 ((t->words == NULL) ? "NULL" : t->words[0])));
2622
2623 rv = 0;
2624 a = areanum++;
2625 wp = (wp2 = t->words) != NULL
2626 ? eval(wp2, t->type == TCOM ? DOALL : DOALL & ~DOKEY)
2627 : NULL;
2628
2629/* Hard to know how many words there are, be careful of garbage pointer values */
2630/* They are likely to cause "PCI bus fault" errors */
2631#if 0
2632 DBGPRINTF(("EXECUTE: t->left=0x%x, t->right=0x%x, t->words[1] is %s\n",
2633 t->left, t->right,
2634 ((t->words[1] == NULL) ? "NULL" : t->words[1])));
2635 DBGPRINTF7(("EXECUTE: t->words[2] is %s, t->words[3] is %s\n",
2636 ((t->words[2] == NULL) ? "NULL" : t->words[2]),
2637 ((t->words[3] == NULL) ? "NULL" : t->words[3])));
2638#endif
2639
2640
2641 switch (t->type) {
2642 case TDOT:
2643 DBGPRINTF3(("EXECUTE: TDOT\n"));
2644
2645 outtree_save = outtree;
2646
2647 newfile(evalstr(t->words[0], DOALL));
2648
2649 t->left = dowholefile(TLIST, 0);
2650 t->right = NULL;
2651
2652 outtree = outtree_save;
2653
2654 if (t->left)
2655 rv = execute(t->left, pin, pout, 0);
2656 if (t->right)
2657 rv = execute(t->right, pin, pout, 0);
2658 break;
2659
2660 case TPAREN:
2661 rv = execute(t->left, pin, pout, 0);
2662 break;
2663
2664 case TCOM:
2665 {
2666 rv = forkexec(t, pin, pout, act, wp);
2667 }
2668 break;
2669
2670 case TPIPE:
2671 {
2672 int pv[2];
2673
2674 if ((rv = openpipe(pv)) < 0)
2675 break;
2676 pv[0] = remap(pv[0]);
2677 pv[1] = remap(pv[1]);
2678 (void) execute(t->left, pin, pv, 0);
2679 rv = execute(t->right, pv, pout, 0);
2680 }
2681 break;
2682
2683 case TLIST:
2684 (void) execute(t->left, pin, pout, 0);
2685 rv = execute(t->right, pin, pout, 0);
2686 break;
2687
2688 case TASYNC:
2689 {
2690 int hinteractive = interactive;
2691
2692 DBGPRINTF7(("EXECUTE: TASYNC clause, calling vfork()...\n"));
2693
2694 i = vfork();
2695 if (i != 0) {
2696 interactive = hinteractive;
2697 if (i != -1) {
2698 setval(lookup("!"), putn(i));
2699 if (pin != NULL)
2700 closepipe(pin);
2701 if (interactive) {
2702 prs(putn(i));
2703 prs("\n");
2704 }
2705 } else
2706 rv = -1;
2707 setstatus(rv);
2708 } else {
2709 signal(SIGINT, SIG_IGN);
2710 signal(SIGQUIT, SIG_IGN);
2711 if (interactive)
2712 signal(SIGTERM, SIG_DFL);
2713 interactive = 0;
2714 if (pin == NULL) {
2715 close(0);
2716 open("/dev/null", 0);
2717 }
2718 _exit(execute(t->left, pin, pout, FEXEC));
2719 }
2720 }
2721 break;
2722
2723 case TOR:
2724 case TAND:
2725 rv = execute(t->left, pin, pout, 0);
2726 if ((t1 = t->right) != NULL && (rv == 0) == (t->type == TAND))
2727 rv = execute(t1, pin, pout, 0);
2728 break;
2729
2730 case TFOR:
2731 if (wp == NULL) {
2732 wp = dolv + 1;
2733 if ((i = dolc) < 0)
2734 i = 0;
2735 } else {
2736 i = -1;
2737 while (*wp++ != NULL);
2738 }
2739 vp = lookup(t->str);
2740 while (setjmp(bc.brkpt))
2741 if (isbreak)
2742 goto broken;
2743 brkset(&bc);
2744 for (t1 = t->left; i-- && *wp != NULL;) {
2745 setval(vp, *wp++);
2746 rv = execute(t1, pin, pout, 0);
2747 }
2748 brklist = brklist->nextlev;
2749 break;
2750
2751 case TWHILE:
2752 case TUNTIL:
2753 while (setjmp(bc.brkpt))
2754 if (isbreak)
2755 goto broken;
2756 brkset(&bc);
2757 t1 = t->left;
2758 while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE))
2759 rv = execute(t->right, pin, pout, 0);
2760 brklist = brklist->nextlev;
2761 break;
2762
2763 case TIF:
2764 case TELIF:
2765 if (t->right != NULL) {
2766 rv = !execute(t->left, pin, pout, 0) ?
2767 execute(t->right->left, pin, pout, 0) :
2768 execute(t->right->right, pin, pout, 0);
2769 }
2770 break;
2771
2772 case TCASE:
2773 if ((cp = evalstr(t->str, DOSUB | DOTRIM)) == 0)
2774 cp = "";
2775
2776 DBGPRINTF7(("EXECUTE: TCASE, t->str is %s, cp is %s\n",
2777 ((t->str == NULL) ? "NULL" : t->str),
2778 ((cp == NULL) ? "NULL" : cp)));
2779
2780 if ((t1 = findcase(t->left, cp)) != NULL) {
2781 DBGPRINTF7(("EXECUTE: TCASE, calling execute(t=0x%x, t1=0x%x)...\n", t, t1));
2782 rv = execute(t1, pin, pout, 0);
2783 DBGPRINTF7(("EXECUTE: TCASE, back from execute(t=0x%x, t1=0x%x)...\n", t, t1));
2784 }
2785 break;
2786
2787 case TBRACE:
2788/*
2789 if (iopp = t->ioact)
2790 while (*iopp)
2791 if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) {
2792 rv = -1;
2793 break;
2794 }
2795*/
2796 if (rv >= 0 && (t1 = t->left))
2797 rv = execute(t1, pin, pout, 0);
2798 break;
2799
2800 };
2801
2802 broken:
2803 t->words = wp2;
2804 isbreak = 0;
2805 freehere(areanum);
2806 freearea(areanum);
2807 areanum = a;
2808 if (interactive && intr) {
2809 closeall();
2810 fail();
2811 }
2812
2813 if ((i = trapset) != 0) {
2814 trapset = 0;
2815 runtrap(i);
2816 }
2817
2818 DBGPRINTF(("EXECUTE: returning from t=0x%x, rv=%d\n", t, rv));
2819 return (rv);
2820}
2821
2822static int
2823forkexec(REGISTER struct op *t, int *pin, int *pout, int act, char **wp)
2824{
2825 pid_t newpid;
2826 int i, rv;
2827 int (*shcom) (struct op *) = NULL;
2828 REGISTER int f;
2829 char *cp = NULL;
2830 struct ioword **iopp;
2831 int resetsig;
2832 char **owp;
2833 int forked = 0;
2834
2835 int *hpin = pin;
2836 int *hpout = pout;
2837 char *hwp;
2838 int hinteractive;
2839 int hintr;
2840 struct brkcon *hbrklist;
2841 int hexecflg;
2842
2843#if __GNUC__
2844 /* Avoid longjmp clobbering */
2845 (void) &pin;
2846 (void) &pout;
2847 (void) &wp;
2848 (void) &shcom;
2849 (void) &cp;
2850 (void) &resetsig;
2851 (void) &owp;
2852#endif
2853
2854 DBGPRINTF(("FORKEXEC: t=0x%x, pin 0x%x, pout 0x%x, act %d\n", t, pin,
2855 pout, act));
2856 DBGPRINTF7(("FORKEXEC: t->words is %s\n",
2857 ((t->words == NULL) ? "NULL" : t->words[0])));
2858
2859/* Hard to know how many words there are, be careful of garbage pointer values */
2860/* They are likely to cause "PCI bus fault" errors */
2861#if 0
2862 DBGPRINTF7(("FORKEXEC: t->words is %s, t->words[1] is %s\n",
2863 ((t->words == NULL) ? "NULL" : t->words[0]),
2864 ((t->words == NULL) ? "NULL" : t->words[1])));
2865 DBGPRINTF7(("FORKEXEC: wp is %s, wp[1] is %s\n",
2866 ((wp == NULL) ? "NULL" : wp[0]),
2867 ((wp[1] == NULL) ? "NULL" : wp[1])));
2868 DBGPRINTF7(("FORKEXEC: wp2 is %s, wp[3] is %s\n",
2869 ((wp[2] == NULL) ? "NULL" : wp[2]),
2870 ((wp[3] == NULL) ? "NULL" : wp[3])));
2871#endif
2872
2873
2874 owp = wp;
2875 resetsig = 0;
2876 rv = -1; /* system-detected error */
2877 if (t->type == TCOM) {
2878 while ((cp = *wp++) != NULL);
2879 cp = *wp;
2880
2881 /* strip all initial assignments */
2882 /* not correct wrt PATH=yyy command etc */
2883 if (flag['x']) {
2884 DBGPRINTF9(("FORKEXEC: echo'ing, cp=0x%x, wp=0x%x, owp=0x%x\n",
2885 cp, wp, owp));
2886 echo(cp ? wp : owp);
2887 }
2888#if 0
2889 DBGPRINTF9(("FORKEXEC: t->words is %s, t->words[1] is %s\n",
2890 ((t->words == NULL) ? "NULL" : t->words[0]),
2891 ((t->words == NULL) ? "NULL" : t->words[1])));
2892 DBGPRINTF9(("FORKEXEC: wp is %s, wp[1] is %s\n",
2893 ((wp == NULL) ? "NULL" : wp[0]),
2894 ((wp == NULL) ? "NULL" : wp[1])));
2895#endif
2896
2897 if (cp == NULL && t->ioact == NULL) {
2898 while ((cp = *owp++) != NULL && assign(cp, COPYV));
2899 DBGPRINTF(("FORKEXEC: returning setstatus()\n"));
2900 return (setstatus(0));
2901 } else if (cp != NULL) {
2902 shcom = inbuilt(cp);
2903 }
2904 }
2905
2906 t->words = wp;
2907 f = act;
2908
2909#if 0
2910 DBGPRINTF3(("FORKEXEC: t->words is %s, t->words[1] is %s\n",
2911 ((t->words == NULL) ? "NULL" : t->words[0]),
2912 ((t->words == NULL) ? "NULL" : t->words[1])));
2913#endif
2914 DBGPRINTF(("FORKEXEC: shcom 0x%x, f&FEXEC 0x%x, owp 0x%x\n", shcom,
2915 f & FEXEC, owp));
2916
2917 if (shcom == NULL && (f & FEXEC) == 0) {
2918 /* Save values in case the child process alters them */
2919 hpin = pin;
2920 hpout = pout;
2921 hwp = *wp;
2922 hinteractive = interactive;
2923 hintr = intr;
2924 hbrklist = brklist;
2925 hexecflg = execflg;
2926
2927 DBGPRINTF3(("FORKEXEC: calling vfork()...\n"));
2928
2929 newpid = vfork();
2930
2931 if (newpid == -1) {
2932 DBGPRINTF(("FORKEXEC: ERROR, unable to vfork()!\n"));
2933 return (-1);
2934 }
2935
2936
2937 if (newpid > 0) { /* Parent */
2938
2939 /* Restore values */
2940 pin = hpin;
2941 pout = hpout;
2942 *wp = hwp;
2943 interactive = hinteractive;
2944 intr = hintr;
2945 brklist = hbrklist;
2946 execflg = hexecflg;
2947
2948/* moved up
2949 if (i == -1)
2950 return(rv);
2951*/
2952
2953 if (pin != NULL)
2954 closepipe(pin);
2955
2956 return (pout == NULL ? setstatus(waitfor(newpid, 0)) : 0);
2957 }
2958
2959 /* Must be the child process, pid should be 0 */
2960 DBGPRINTF(("FORKEXEC: child process, shcom=0x%x\n", shcom));
2961
2962 if (interactive) {
2963 signal(SIGINT, SIG_IGN);
2964 signal(SIGQUIT, SIG_IGN);
2965 resetsig = 1;
2966 }
2967 interactive = 0;
2968 intr = 0;
2969 forked = 1;
2970 brklist = 0;
2971 execflg = 0;
2972 }
2973
2974
2975 if (owp != NULL)
2976 while ((cp = *owp++) != NULL && assign(cp, COPYV))
2977 if (shcom == NULL)
2978 export(lookup(cp));
2979
2980#ifdef COMPIPE
2981 if ((pin != NULL || pout != NULL) && shcom != NULL && shcom != doexec) {
2982 err("piping to/from shell builtins not yet done");
2983 if (forked)
2984 _exit(-1);
2985 return (-1);
2986 }
2987#endif
2988
2989 if (pin != NULL) {
2990 dup2(pin[0], 0);
2991 closepipe(pin);
2992 }
2993 if (pout != NULL) {
2994 dup2(pout[1], 1);
2995 closepipe(pout);
2996 }
2997
2998 if ((iopp = t->ioact) != NULL) {
2999 if (shcom != NULL && shcom != doexec) {
3000 prs(cp);
3001 err(": cannot redirect shell command");
3002 if (forked)
3003 _exit(-1);
3004 return (-1);
3005 }
3006 while (*iopp)
3007 if (iosetup(*iopp++, pin != NULL, pout != NULL)) {
3008 if (forked)
3009 _exit(rv);
3010 return (rv);
3011 }
3012 }
3013
3014 if (shcom) {
3015 i = setstatus((*shcom) (t));
3016 if (forked)
3017 _exit(i);
3018 DBGPRINTF(("FORKEXEC: returning i=%d\n", i));
3019 return (i);
3020 }
3021
3022 /* should use FIOCEXCL */
3023 for (i = FDBASE; i < NOFILE; i++)
3024 close(i);
3025 if (resetsig) {
3026 signal(SIGINT, SIG_DFL);
3027 signal(SIGQUIT, SIG_DFL);
3028 }
3029
3030 if (t->type == TPAREN)
3031 _exit(execute(t->left, NOPIPE, NOPIPE, FEXEC));
3032 if (wp[0] == NULL)
3033 _exit(0);
3034
3035 cp = rexecve(wp[0], wp, makenv(0, NULL));
3036 prs(wp[0]);
3037 prs(": ");
3038 err(cp);
3039 if (!execflg)
3040 trap[0] = NULL;
3041
3042 DBGPRINTF(("FORKEXEC: calling leave(), pid=%d\n", newpid));
3043
3044 leave();
3045 /* NOTREACHED */
3046 _exit(1);
3047}
3048
3049/*
3050 * 0< 1> are ignored as required
3051 * within pipelines.
3052 */
3053static int iosetup(iop, pipein, pipeout)
3054REGISTER struct ioword *iop;
3055int pipein, pipeout;
3056{
3057 REGISTER int u = -1;
3058 char *cp = NULL, *msg;
3059
3060 DBGPRINTF(("IOSETUP: iop 0x%x, pipein 0x%x, pipeout 0x%x\n", iop,
3061 pipein, pipeout));
3062
3063 if (iop->io_unit == IODEFAULT) /* take default */
3064 iop->io_unit = iop->io_flag & (IOREAD | IOHERE) ? 0 : 1;
3065
3066 if (pipein && iop->io_unit == 0)
3067 return (0);
3068
3069 if (pipeout && iop->io_unit == 1)
3070 return (0);
3071
3072 msg = iop->io_flag & (IOREAD | IOHERE) ? "open" : "create";
3073 if ((iop->io_flag & IOHERE) == 0) {
3074 cp = iop->io_name;
3075 if ((cp = evalstr(cp, DOSUB | DOTRIM)) == NULL)
3076 return (1);
3077 }
3078
3079 if (iop->io_flag & IODUP) {
3080 if (cp[1] || (!isdigit(*cp) && *cp != '-')) {
3081 prs(cp);
3082 err(": illegal >& argument");
3083 return (1);
3084 }
3085 if (*cp == '-')
3086 iop->io_flag = IOCLOSE;
3087 iop->io_flag &= ~(IOREAD | IOWRITE);
3088 }
3089 switch (iop->io_flag) {
3090 case IOREAD:
3091 u = open(cp, 0);
3092 break;
3093
3094 case IOHERE:
3095 case IOHERE | IOXHERE:
3096 u = herein(iop->io_name, iop->io_flag & IOXHERE);
3097 cp = "here file";
3098 break;
3099
3100 case IOWRITE | IOCAT:
3101 if ((u = open(cp, 1)) >= 0) {
3102 lseek(u, (long) 0, 2);
3103 break;
3104 }
3105 case IOWRITE:
3106 u = creat(cp, 0666);
3107 break;
3108
3109 case IODUP:
3110 u = dup2(*cp - '0', iop->io_unit);
3111 break;
3112
3113 case IOCLOSE:
3114 close(iop->io_unit);
3115 return (0);
3116 }
3117 if (u < 0) {
3118 prs(cp);
3119 prs(": cannot ");
3120 warn(msg);
3121 return (1);
3122 } else {
3123 if (u != iop->io_unit) {
3124 dup2(u, iop->io_unit);
3125 close(u);
3126 }
3127 }
3128 return (0);
3129}
3130
3131static void echo(wp)
3132REGISTER char **wp;
3133{
3134 REGISTER int i;
3135
3136 prs("+");
3137 for (i = 0; wp[i]; i++) {
3138 if (i)
3139 prs(" ");
3140 prs(wp[i]);
3141 }
3142 prs("\n");
3143}
3144
3145static struct op **find1case(t, w)
3146struct op *t;
3147char *w;
3148{
3149 REGISTER struct op *t1;
3150 struct op **tp;
3151 REGISTER char **wp, *cp;
3152
3153
3154 if (t == NULL) {
3155 DBGPRINTF3(("FIND1CASE: enter, t==NULL, returning.\n"));
3156 return ((struct op **) NULL);
3157 }
3158
3159 DBGPRINTF3(("FIND1CASE: enter, t->type=%d (%s)\n", t->type,
3160 T_CMD_NAMES[t->type]));
3161
3162 if (t->type == TLIST) {
3163 if ((tp = find1case(t->left, w)) != NULL) {
3164 DBGPRINTF3(("FIND1CASE: found one to the left, returning tp=0x%x\n", tp));
3165 return (tp);
3166 }
3167 t1 = t->right; /* TPAT */
3168 } else
3169 t1 = t;
3170
3171 for (wp = t1->words; *wp;)
3172 if ((cp = evalstr(*wp++, DOSUB)) && gmatch(w, cp)) {
3173 DBGPRINTF3(("FIND1CASE: returning &t1->left= 0x%x.\n",
3174 &t1->left));
3175 return (&t1->left);
3176 }
3177
3178 DBGPRINTF(("FIND1CASE: returning NULL\n"));
3179 return ((struct op **) NULL);
3180}
3181
3182static struct op *findcase(t, w)
3183struct op *t;
3184char *w;
3185{
3186 REGISTER struct op **tp;
3187
3188 return ((tp = find1case(t, w)) != NULL ? *tp : (struct op *) NULL);
3189}
3190
3191/*
3192 * Enter a new loop level (marked for break/continue).
3193 */
3194static void brkset(bc)
3195struct brkcon *bc;
3196{
3197 bc->nextlev = brklist;
3198 brklist = bc;
3199}
3200
3201/*
3202 * Wait for the last process created.
3203 * Print a message for each process found
3204 * that was killed by a signal.
3205 * Ignore interrupt signals while waiting
3206 * unless `canintr' is true.
3207 */
3208static int waitfor(lastpid, canintr)
3209REGISTER int lastpid;
3210int canintr;
3211{
3212 REGISTER int pid, rv;
3213 int s;
3214 int oheedint = heedint;
3215
3216 heedint = 0;
3217 rv = 0;
3218 do {
3219 pid = wait(&s);
3220 if (pid == -1) {
3221 if (errno != EINTR || canintr)
3222 break;
3223 } else {
3224 if ((rv = WAITSIG(s)) != 0) {
3225 if (rv < NSIGNAL) {
3226 if (signame[rv] != NULL) {
3227 if (pid != lastpid) {
3228 prn(pid);
3229 prs(": ");
3230 }
3231 prs(signame[rv]);
3232 }
3233 } else {
3234 if (pid != lastpid) {
3235 prn(pid);
3236 prs(": ");
3237 }
3238 prs("Signal ");
3239 prn(rv);
3240 prs(" ");
3241 }
3242 if (WAITCORE(s))
3243 prs(" - core dumped");
3244 if (rv >= NSIGNAL || signame[rv])
3245 prs("\n");
3246 rv = -1;
3247 } else
3248 rv = WAITVAL(s);
3249 }
3250 } while (pid != lastpid);
3251 heedint = oheedint;
3252 if (intr) {
3253 if (interactive) {
3254 if (canintr)
3255 intr = 0;
3256 } else {
3257 if (exstat == 0)
3258 exstat = rv;
3259 onintr(0);
3260 }
3261 }
3262 return (rv);
3263}
3264
3265static int setstatus(s)
3266REGISTER int s;
3267{
3268 exstat = s;
3269 setval(lookup("?"), putn(s));
3270 return (s);
3271}
3272
3273/*
3274 * PATH-searching interface to execve.
3275 * If getenv("PATH") were kept up-to-date,
3276 * execvp might be used.
3277 */
3278static char *rexecve(c, v, envp)
3279char *c, **v, **envp;
3280{
3281 REGISTER int i;
3282 REGISTER char *sp, *tp;
3283 int eacces = 0, asis = 0;
3284
3285#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
3286 char *name = c;
3287
3288 optind = 1;
3289 if (find_applet_by_name(name)) {
3290 /* We have to exec here since we vforked. Running
3291 * run_applet_by_name() won't work and bad things
3292 * will happen. */
3293 execve("/proc/self/exe", v, envp);
3294 execve("busybox", v, envp);
3295 }
3296#endif
3297
3298 DBGPRINTF(("REXECVE: c=0x%x, v=0x%x, envp=0x%x\n", c, v, envp));
3299
3300 sp = any('/', c) ? "" : path->value;
3301 asis = *sp == '\0';
3302 while (asis || *sp != '\0') {
3303 asis = 0;
3304 tp = e.linep;
3305 for (; *sp != '\0'; tp++)
3306 if ((*tp = *sp++) == ':') {
3307 asis = *sp == '\0';
3308 break;
3309 }
3310 if (tp != e.linep)
3311 *tp++ = '/';
3312 for (i = 0; (*tp++ = c[i++]) != '\0';);
3313
3314 DBGPRINTF3(("REXECVE: e.linep is %s\n", e.linep));
3315
3316 execve(e.linep, v, envp);
3317
3318 switch (errno) {
3319 case ENOEXEC:
3320 *v = e.linep;
3321 tp = *--v;
3322 *v = e.linep;
3323 execve(DEFAULT_SHELL, v, envp);
3324 *v = tp;
3325 return ("no Shell");
3326
3327 case ENOMEM:
3328 return ((char *) bb_msg_memory_exhausted);
3329
3330 case E2BIG:
3331 return ("argument list too long");
3332
3333 case EACCES:
3334 eacces++;
3335 break;
3336 }
3337 }
3338 return (errno == ENOENT ? "not found" : "cannot execute");
3339}
3340
3341/*
3342 * Run the command produced by generator `f'
3343 * applied to stream `arg'.
3344 */
3345static int run(struct ioarg *argp, int (*f) (struct ioarg *))
3346{
3347 struct op *otree;
3348 struct wdblock *swdlist;
3349 struct wdblock *siolist;
3350 jmp_buf ev, rt;
3351 xint *ofail;
3352 int rv;
3353
3354#if __GNUC__
3355 /* Avoid longjmp clobbering */
3356 (void) &rv;
3357#endif
3358
3359 DBGPRINTF(("RUN: enter, areanum %d, outtree 0x%x, failpt 0x%x\n",
3360 areanum, outtree, failpt));
3361
3362 areanum++;
3363 swdlist = wdlist;
3364 siolist = iolist;
3365 otree = outtree;
3366 ofail = failpt;
3367 rv = -1;
3368
3369 if (newenv(setjmp(errpt = ev)) == 0) {
3370 wdlist = 0;
3371 iolist = 0;
3372 pushio(argp, f);
3373 e.iobase = e.iop;
3374 yynerrs = 0;
3375 if (setjmp(failpt = rt) == 0 && yyparse() == 0)
3376 rv = execute(outtree, NOPIPE, NOPIPE, 0);
3377 quitenv();
3378 } else {
3379 DBGPRINTF(("RUN: error from newenv()!\n"));
3380 }
3381
3382 wdlist = swdlist;
3383 iolist = siolist;
3384 failpt = ofail;
3385 outtree = otree;
3386 freearea(areanum--);
3387
3388 return (rv);
3389}
3390
3391/* -------- do.c -------- */
3392
3393/*
3394 * built-in commands: doX
3395 */
3396
3397static int dohelp(struct op *t)
3398{
3399 int col;
3400 const struct builtincmd *x;
3401
3402 printf("\nBuilt-in commands:\n");
3403 printf("-------------------\n");
3404
3405 for (col = 0, x = builtincmds; x->builtinfunc != NULL; x++) {
3406 if (!x->name)
3407 continue;
3408 col += printf("%s%s", ((col == 0) ? "\t" : " "), x->name);
3409 if (col > 60) {
3410 printf("\n");
3411 col = 0;
3412 }
3413 }
3414#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
3415 {
3416 int i;
3417 const struct BB_applet *applet;
3418 extern const struct BB_applet applets[];
3419 extern const size_t NUM_APPLETS;
3420
3421 for (i = 0, applet = applets; i < NUM_APPLETS; applet++, i++) {
3422 if (!applet->name)
3423 continue;
3424
3425 col += printf("%s%s", ((col == 0) ? "\t" : " "), applet->name);
3426 if (col > 60) {
3427 printf("\n");
3428 col = 0;
3429 }
3430 }
3431 }
3432#endif
3433 printf("\n\n");
3434 return EXIT_SUCCESS;
3435}
3436
3437
3438
3439static int dolabel(struct op *t)
3440{
3441 return (0);
3442}
3443
3444static int dochdir(t)
3445REGISTER struct op *t;
3446{
3447 REGISTER char *cp, *er;
3448
3449 if ((cp = t->words[1]) == NULL && (cp = homedir->value) == NULL)
3450 er = ": no home directory";
3451 else if (chdir(cp) < 0)
3452 er = ": bad directory";
3453 else
3454 return (0);
3455 prs(cp != NULL ? cp : "cd");
3456 err(er);
3457 return (1);
3458}
3459
3460static int doshift(t)
3461REGISTER struct op *t;
3462{
3463 REGISTER int n;
3464
3465 n = t->words[1] ? getn(t->words[1]) : 1;
3466 if (dolc < n) {
3467 err("nothing to shift");
3468 return (1);
3469 }
3470 dolv[n] = dolv[0];
3471 dolv += n;
3472 dolc -= n;
3473 setval(lookup("#"), putn(dolc));
3474 return (0);
3475}
3476
3477/*
3478 * execute login and newgrp directly
3479 */
3480static int dologin(t)
3481struct op *t;
3482{
3483 REGISTER char *cp;
3484
3485 if (interactive) {
3486 signal(SIGINT, SIG_DFL);
3487 signal(SIGQUIT, SIG_DFL);
3488 }
3489 cp = rexecve(t->words[0], t->words, makenv(0, NULL));
3490 prs(t->words[0]);
3491 prs(": ");
3492 err(cp);
3493 return (1);
3494}
3495
3496static int doumask(t)
3497REGISTER struct op *t;
3498{
3499 REGISTER int i, n;
3500 REGISTER char *cp;
3501
3502 if ((cp = t->words[1]) == NULL) {
3503 i = umask(0);
3504 umask(i);
3505 for (n = 3 * 4; (n -= 3) >= 0;)
3506 putc('0' + ((i >> n) & 07), stderr);
3507 putc('\n', stderr);
3508 } else {
3509 for (n = 0; *cp >= '0' && *cp <= '9'; cp++)
3510 n = n * 8 + (*cp - '0');
3511 umask(n);
3512 }
3513 return (0);
3514}
3515
3516static int doexec(t)
3517REGISTER struct op *t;
3518{
3519 REGISTER int i;
3520 jmp_buf ex;
3521 xint *ofail;
3522
3523 t->ioact = NULL;
3524 for (i = 0; (t->words[i] = t->words[i + 1]) != NULL; i++);
3525 if (i == 0)
3526 return (1);
3527 execflg = 1;
3528 ofail = failpt;
3529 if (setjmp(failpt = ex) == 0)
3530 execute(t, NOPIPE, NOPIPE, FEXEC);
3531 failpt = ofail;
3532 execflg = 0;
3533 return (1);
3534}
3535
3536static int dodot(t)
3537struct op *t;
3538{
3539 REGISTER int i;
3540 REGISTER char *sp, *tp;
3541 char *cp;
3542 int maltmp;
3543
3544 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)));
3545
3546 if ((cp = t->words[1]) == NULL) {
3547 DBGPRINTF(("DODOT: bad args, ret 0\n"));
3548 return (0);
3549 } else {
3550 DBGPRINTF(("DODOT: cp is %s\n", cp));
3551 }
3552
3553 sp = any('/', cp) ? ":" : path->value;
3554
3555 DBGPRINTF(("DODOT: sp is %s, e.linep is %s\n",
3556 ((sp == NULL) ? "NULL" : sp),
3557 ((e.linep == NULL) ? "NULL" : e.linep)));
3558
3559 while (*sp) {
3560 tp = e.linep;
3561 while (*sp && (*tp = *sp++) != ':')
3562 tp++;
3563 if (tp != e.linep)
3564 *tp++ = '/';
3565
3566 for (i = 0; (*tp++ = cp[i++]) != '\0';);
3567
3568 /* Original code */
3569 if ((i = open(e.linep, 0)) >= 0) {
3570 exstat = 0;
3571 maltmp = remap(i);
3572 DBGPRINTF(("DODOT: remap=%d, exstat=%d, e.iofd %d, i %d, e.linep is %s\n", maltmp, exstat, e.iofd, i, e.linep));
3573
3574 next(maltmp); /* Basically a PUSHIO */
3575
3576 DBGPRINTF(("DODOT: returning exstat=%d\n", exstat));
3577
3578 return (exstat);
3579 }
3580
3581 } /* While */
3582
3583 prs(cp);
3584 err(": not found");
3585
3586 return (-1);
3587}
3588
3589static int dowait(t)
3590struct op *t;
3591{
3592 REGISTER int i;
3593 REGISTER char *cp;
3594
3595 if ((cp = t->words[1]) != NULL) {
3596 i = getn(cp);
3597 if (i == 0)
3598 return (0);
3599 } else
3600 i = -1;
3601 setstatus(waitfor(i, 1));
3602 return (0);
3603}
3604
3605static int doread(t)
3606struct op *t;
3607{
3608 REGISTER char *cp, **wp;
3609 REGISTER int nb = 0;
3610 REGISTER int nl = 0;
3611
3612 if (t->words[1] == NULL) {
3613 err("Usage: read name ...");
3614 return (1);
3615 }
3616 for (wp = t->words + 1; *wp; wp++) {
3617 for (cp = e.linep; !nl && cp < elinep - 1; cp++)
3618 if ((nb = read(0, cp, sizeof(*cp))) != sizeof(*cp) ||
3619 (nl = (*cp == '\n')) || (wp[1] && any(*cp, ifs->value)))
3620 break;
3621 *cp = 0;
3622 if (nb <= 0)
3623 break;
3624 setval(lookup(*wp), e.linep);
3625 }
3626 return (nb <= 0);
3627}
3628
3629static int doeval(t)
3630REGISTER struct op *t;
3631{
3632 return (RUN(awordlist, t->words + 1, wdchar));
3633}
3634
3635static int dotrap(t)
3636REGISTER struct op *t;
3637{
3638 REGISTER int n, i;
3639 REGISTER int resetsig;
3640
3641 if (t->words[1] == NULL) {
3642 for (i = 0; i <= _NSIG; i++)
3643 if (trap[i]) {
3644 prn(i);
3645 prs(": ");
3646 prs(trap[i]);
3647 prs("\n");
3648 }
3649 return (0);
3650 }
3651 resetsig = isdigit(*t->words[1]);
3652 for (i = resetsig ? 1 : 2; t->words[i] != NULL; ++i) {
3653 n = getsig(t->words[i]);
3654 freecell(trap[n]);
3655 trap[n] = 0;
3656 if (!resetsig) {
3657 if (*t->words[1] != '\0') {
3658 trap[n] = strsave(t->words[1], 0);
3659 setsig(n, sig);
3660 } else
3661 setsig(n, SIG_IGN);
3662 } else {
3663 if (interactive)
3664 if (n == SIGINT)
3665 setsig(n, onintr);
3666 else
3667 setsig(n, n == SIGQUIT ? SIG_IGN : SIG_DFL);
3668 else
3669 setsig(n, SIG_DFL);
3670 }
3671 }
3672 return (0);
3673}
3674
3675static int getsig(s)
3676char *s;
3677{
3678 REGISTER int n;
3679
3680 if ((n = getn(s)) < 0 || n > _NSIG) {
3681 err("trap: bad signal number");
3682 n = 0;
3683 }
3684 return (n);
3685}
3686
3687static void setsig(REGISTER int n, sighandler_t f)
3688{
3689 if (n == 0)
3690 return;
3691 if (signal(n, SIG_IGN) != SIG_IGN || ourtrap[n]) {
3692 ourtrap[n] = 1;
3693 signal(n, f);
3694 }
3695}
3696
3697static int getn(as)
3698char *as;
3699{
3700 REGISTER char *s;
3701 REGISTER int n, m;
3702
3703 s = as;
3704 m = 1;
3705 if (*s == '-') {
3706 m = -1;
3707 s++;
3708 }
3709 for (n = 0; isdigit(*s); s++)
3710 n = (n * 10) + (*s - '0');
3711 if (*s) {
3712 prs(as);
3713 err(": bad number");
3714 }
3715 return (n * m);
3716}
3717
3718static int dobreak(t)
3719struct op *t;
3720{
3721 return (brkcontin(t->words[1], 1));
3722}
3723
3724static int docontinue(t)
3725struct op *t;
3726{
3727 return (brkcontin(t->words[1], 0));
3728}
3729
3730static int brkcontin(cp, val)
3731REGISTER char *cp;
3732int val;
3733{
3734 REGISTER struct brkcon *bc;
3735 REGISTER int nl;
3736
3737 nl = cp == NULL ? 1 : getn(cp);
3738 if (nl <= 0)
3739 nl = 999;
3740 do {
3741 if ((bc = brklist) == NULL)
3742 break;
3743 brklist = bc->nextlev;
3744 } while (--nl);
3745 if (nl) {
3746 err("bad break/continue level");
3747 return (1);
3748 }
3749 isbreak = val;
3750 longjmp(bc->brkpt, 1);
3751 /* NOTREACHED */
3752}
3753
3754static int doexit(t)
3755struct op *t;
3756{
3757 REGISTER char *cp;
3758
3759 execflg = 0;
3760 if ((cp = t->words[1]) != NULL)
3761 setstatus(getn(cp));
3762
3763 DBGPRINTF(("DOEXIT: calling leave(), t=0x%x\n", t));
3764
3765 leave();
3766 /* NOTREACHED */
3767 return (0);
3768}
3769
3770static int doexport(t)
3771struct op *t;
3772{
3773 rdexp(t->words + 1, export, EXPORT);
3774 return (0);
3775}
3776
3777static int doreadonly(t)
3778struct op *t;
3779{
3780 rdexp(t->words + 1, ronly, RONLY);
3781 return (0);
3782}
3783
3784static void rdexp(char **wp, void (*f) (struct var *), int key)
3785{
3786 DBGPRINTF6(("RDEXP: enter, wp=0x%x, func=0x%x, key=%d\n", wp, f, key));
3787 DBGPRINTF6(("RDEXP: *wp=%s\n", *wp));
3788
3789 if (*wp != NULL) {
3790 for (; *wp != NULL; wp++) {
3791 if (isassign(*wp)) {
3792 char *cp;
3793
3794 assign(*wp, COPYV);
3795 for (cp = *wp; *cp != '='; cp++);
3796 *cp = '\0';
3797 }
3798 if (checkname(*wp))
3799 (*f) (lookup(*wp));
3800 else
3801 badid(*wp);
3802 }
3803 } else
3804 putvlist(key, 1);
3805}
3806
3807static void badid(s)
3808REGISTER char *s;
3809{
3810 prs(s);
3811 err(": bad identifier");
3812}
3813
3814static int doset(t)
3815REGISTER struct op *t;
3816{
3817 REGISTER struct var *vp;
3818 REGISTER char *cp;
3819 REGISTER int n;
3820
3821 if ((cp = t->words[1]) == NULL) {
3822 for (vp = vlist; vp; vp = vp->next)
3823 varput(vp->name, 1);
3824 return (0);
3825 }
3826 if (*cp == '-') {
3827 /* bad: t->words++; */
3828 for (n = 0; (t->words[n] = t->words[n + 1]) != NULL; n++);
3829 if (*++cp == 0)
3830 flag['x'] = flag['v'] = 0;
3831 else
3832 for (; *cp; cp++)
3833 switch (*cp) {
3834 case 'e':
3835 if (!interactive)
3836 flag['e']++;
3837 break;
3838
3839 default:
3840 if (*cp >= 'a' && *cp <= 'z')
3841 flag[(int) *cp]++;
3842 break;
3843 }
3844 setdash();
3845 }
3846 if (t->words[1]) {
3847 t->words[0] = dolv[0];
3848 for (n = 1; t->words[n]; n++)
3849 setarea((char *) t->words[n], 0);
3850 dolc = n - 1;
3851 dolv = t->words;
3852 setval(lookup("#"), putn(dolc));
3853 setarea((char *) (dolv - 1), 0);
3854 }
3855 return (0);
3856}
3857
3858static void varput(s, out)
3859REGISTER char *s;
3860int out;
3861{
3862 if (isalnum(*s) || *s == '_') {
3863 write(out, s, strlen(s));
3864 write(out, "\n", 1);
3865 }
3866}
3867
3868
3869/*
3870 * Copyright (c) 1999 Herbert Xu <herbert@debian.org>
3871 * This file contains code for the times builtin.
3872 */
3873static int dotimes(struct op *t)
3874{
3875 struct tms buf;
3876 long int clk_tck = sysconf(_SC_CLK_TCK);
3877
3878 times(&buf);
3879 printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n",
3880 (int) (buf.tms_utime / clk_tck / 60),
3881 ((double) buf.tms_utime) / clk_tck,
3882 (int) (buf.tms_stime / clk_tck / 60),
3883 ((double) buf.tms_stime) / clk_tck,
3884 (int) (buf.tms_cutime / clk_tck / 60),
3885 ((double) buf.tms_cutime) / clk_tck,
3886 (int) (buf.tms_cstime / clk_tck / 60),
3887 ((double) buf.tms_cstime) / clk_tck);
3888 return 0;
3889}
3890
3891
3892static int (*inbuilt(char *s)) (struct op *) {
3893 const struct builtincmd *bp;
3894
3895 for (bp = builtincmds; bp->name != NULL; bp++)
3896 if (strcmp(bp->name, s) == 0)
3897 return (bp->builtinfunc);
3898
3899 return (NULL);
3900}
3901
3902/* -------- eval.c -------- */
3903
3904/*
3905 * ${}
3906 * `command`
3907 * blank interpretation
3908 * quoting
3909 * glob
3910 */
3911
3912static char **eval(char **ap, int f)
3913{
3914 struct wdblock *wb;
3915 char **wp;
3916 char **wf;
3917 jmp_buf ev;
3918
3919#if __GNUC__
3920 /* Avoid longjmp clobbering */
3921 (void) &wp;
3922 (void) &ap;
3923#endif
3924
3925 DBGPRINTF4(("EVAL: enter, f=%d\n", f));
3926
3927 wp = NULL;
3928 wb = NULL;
3929 wf = NULL;
3930 if (newenv(setjmp(errpt = ev)) == 0) {
3931 while (*ap && isassign(*ap))
3932 expand(*ap++, &wb, f & ~DOGLOB);
3933 if (flag['k']) {
3934 for (wf = ap; *wf; wf++) {
3935 if (isassign(*wf))
3936 expand(*wf, &wb, f & ~DOGLOB);
3937 }
3938 }
3939 for (wb = addword((char *) 0, wb); *ap; ap++) {
3940 if (!flag['k'] || !isassign(*ap))
3941 expand(*ap, &wb, f & ~DOKEY);
3942 }
3943 wb = addword((char *) 0, wb);
3944 wp = getwords(wb);
3945 quitenv();
3946 } else
3947 gflg = 1;
3948
3949 return (gflg ? (char **) NULL : wp);
3950}
3951
3952/*
3953 * Make the exported environment from the exported
3954 * names in the dictionary. Keyword assignments
3955 * will already have been done.
3956 */
3957static char **makenv(int all, struct wdblock *wb)
3958{
3959 REGISTER struct var *vp;
3960
3961 DBGPRINTF5(("MAKENV: enter, all=%d\n", all));
3962
3963 for (vp = vlist; vp; vp = vp->next)
3964 if (all || vp->status & EXPORT)
3965 wb = addword(vp->name, wb);
3966 wb = addword((char *) 0, wb);
3967 return (getwords(wb));
3968}
3969
3970static char *evalstr(cp, f)
3971REGISTER char *cp;
3972int f;
3973{
3974 struct wdblock *wb;
3975
3976 DBGPRINTF6(("EVALSTR: enter, cp=0x%x, f=%d\n", cp, f));
3977
3978 wb = NULL;
3979 if (expand(cp, &wb, f)) {
3980 if (wb == NULL || wb->w_nword == 0
3981 || (cp = wb->w_words[0]) == NULL)
3982 cp = "";
3983 DELETE(wb);
3984 } else
3985 cp = NULL;
3986 return (cp);
3987}
3988
3989static int expand(char *cp, REGISTER struct wdblock **wbp, int f)
3990{
3991 jmp_buf ev;
3992
3993#if __GNUC__
3994 /* Avoid longjmp clobbering */
3995 (void) &cp;
3996#endif
3997
3998 DBGPRINTF3(("EXPAND: enter, f=%d\n", f));
3999
4000 gflg = 0;
4001
4002 if (cp == NULL)
4003 return (0);
4004
4005 if (!anys("$`'\"", cp) &&
4006 !anys(ifs->value, cp) && ((f & DOGLOB) == 0 || !anys("[*?", cp))) {
4007 cp = strsave(cp, areanum);
4008 if (f & DOTRIM)
4009 unquote(cp);
4010 *wbp = addword(cp, *wbp);
4011 return (1);
4012 }
4013 if (newenv(setjmp(errpt = ev)) == 0) {
4014 PUSHIO(aword, cp, strchar);
4015 e.iobase = e.iop;
4016 while ((cp = blank(f)) && gflg == 0) {
4017 e.linep = cp;
4018 cp = strsave(cp, areanum);
4019 if ((f & DOGLOB) == 0) {
4020 if (f & DOTRIM)
4021 unquote(cp);
4022 *wbp = addword(cp, *wbp);
4023 } else
4024 *wbp = glob(cp, *wbp);
4025 }
4026 quitenv();
4027 } else
4028 gflg = 1;
4029 return (gflg == 0);
4030}
4031
4032/*
4033 * Blank interpretation and quoting
4034 */
4035static char *blank(f)
4036int f;
4037{
4038 REGISTER int c, c1;
4039 REGISTER char *sp;
4040 int scanequals, foundequals;
4041
4042 DBGPRINTF3(("BLANK: enter, f=%d\n", f));
4043
4044 sp = e.linep;
4045 scanequals = f & DOKEY;
4046 foundequals = 0;
4047
4048 loop:
4049 switch (c = subgetc('"', foundequals)) {
4050 case 0:
4051 if (sp == e.linep)
4052 return (0);
4053 *e.linep++ = 0;
4054 return (sp);
4055
4056 default:
4057 if (f & DOBLANK && any(c, ifs->value))
4058 goto loop;
4059 break;
4060
4061 case '"':
4062 case '\'':
4063 scanequals = 0;
4064 if (INSUB())
4065 break;
4066 for (c1 = c; (c = subgetc(c1, 1)) != c1;) {
4067 if (c == 0)
4068 break;
4069 if (c == '\'' || !any(c, "$`\""))
4070 c |= QUOTE;
4071 *e.linep++ = c;
4072 }
4073 c = 0;
4074 }
4075 unget(c);
4076 if (!isalpha(c) && c != '_')
4077 scanequals = 0;
4078 for (;;) {
4079 c = subgetc('"', foundequals);
4080 if (c == 0 ||
4081 f & (DOBLANK && any(c, ifs->value)) ||
4082 (!INSUB() && any(c, "\"'"))) {
4083 scanequals = 0;
4084 unget(c);
4085 if (any(c, "\"'"))
4086 goto loop;
4087 break;
4088 }
4089 if (scanequals) {
4090 if (c == '=') {
4091 foundequals = 1;
4092 scanequals = 0;
4093 } else if (!isalnum(c) && c != '_')
4094 scanequals = 0;
4095 }
4096 *e.linep++ = c;
4097 }
4098 *e.linep++ = 0;
4099 return (sp);
4100}
4101
4102/*
4103 * Get characters, substituting for ` and $
4104 */
4105static int subgetc(ec, quoted)
4106REGISTER char ec;
4107int quoted;
4108{
4109 REGISTER char c;
4110
4111 DBGPRINTF3(("SUBGETC: enter, quoted=%d\n", quoted));
4112
4113 again:
4114 c = my_getc(ec);
4115 if (!INSUB() && ec != '\'') {
4116 if (c == '`') {
4117 if (grave(quoted) == 0)
4118 return (0);
4119 e.iop->task = XGRAVE;
4120 goto again;
4121 }
4122 if (c == '$' && (c = dollar(quoted)) == 0) {
4123 e.iop->task = XDOLL;
4124 goto again;
4125 }
4126 }
4127 return (c);
4128}
4129
4130/*
4131 * Prepare to generate the string returned by ${} substitution.
4132 */
4133static int dollar(quoted)
4134int quoted;
4135{
4136 int otask;
4137 struct io *oiop;
4138 char *dolp;
4139 REGISTER char *s, c, *cp = NULL;
4140 struct var *vp;
4141
4142 DBGPRINTF3(("DOLLAR: enter, quoted=%d\n", quoted));
4143
4144 c = readc();
4145 s = e.linep;
4146 if (c != '{') {
4147 *e.linep++ = c;
4148 if (isalpha(c) || c == '_') {
4149 while ((c = readc()) != 0 && (isalnum(c) || c == '_'))
4150 if (e.linep < elinep)
4151 *e.linep++ = c;
4152 unget(c);
4153 }
4154 c = 0;
4155 } else {
4156 oiop = e.iop;
4157 otask = e.iop->task;
4158
4159 e.iop->task = XOTHER;
4160 while ((c = subgetc('"', 0)) != 0 && c != '}' && c != '\n')
4161 if (e.linep < elinep)
4162 *e.linep++ = c;
4163 if (oiop == e.iop)
4164 e.iop->task = otask;
4165 if (c != '}') {
4166 err("unclosed ${");
4167 gflg++;
4168 return (c);
4169 }
4170 }
4171 if (e.linep >= elinep) {
4172 err("string in ${} too long");
4173 gflg++;
4174 e.linep -= 10;
4175 }
4176 *e.linep = 0;
4177 if (*s)
4178 for (cp = s + 1; *cp; cp++)
4179 if (any(*cp, "=-+?")) {
4180 c = *cp;
4181 *cp++ = 0;
4182 break;
4183 }
4184 if (s[1] == 0 && (*s == '*' || *s == '@')) {
4185 if (dolc > 1) {
4186 /* currently this does not distinguish $* and $@ */
4187 /* should check dollar */
4188 e.linep = s;
4189 PUSHIO(awordlist, dolv + 1, dolchar);
4190 return (0);
4191 } else { /* trap the nasty ${=} */
4192 s[0] = '1';
4193 s[1] = 0;
4194 }
4195 }
4196 vp = lookup(s);
4197 if ((dolp = vp->value) == null) {
4198 switch (c) {
4199 case '=':
4200 if (isdigit(*s)) {
4201 err("cannot use ${...=...} with $n");
4202 gflg++;
4203 break;
4204 }
4205 setval(vp, cp);
4206 dolp = vp->value;
4207 break;
4208
4209 case '-':
4210 dolp = strsave(cp, areanum);
4211 break;
4212
4213 case '?':
4214 if (*cp == 0) {
4215 prs("missing value for ");
4216 err(s);
4217 } else
4218 err(cp);
4219 gflg++;
4220 break;
4221 }
4222 } else if (c == '+')
4223 dolp = strsave(cp, areanum);
4224 if (flag['u'] && dolp == null) {
4225 prs("unset variable: ");
4226 err(s);
4227 gflg++;
4228 }
4229 e.linep = s;
4230 PUSHIO(aword, dolp, quoted ? qstrchar : strchar);
4231 return (0);
4232}
4233
4234/*
4235 * Run the command in `...` and read its output.
4236 */
4237
4238static int grave(quoted)
4239int quoted;
4240{
4241 char *cp;
4242 REGISTER int i;
4243 int j;
4244 int pf[2];
4245 static char child_cmd[LINELIM];
4246 char *src;
4247 char *dest;
4248 int count;
4249 int ignore;
4250 int ignore_once;
4251 char *argument_list[4];
4252 struct wdblock *wb = NULL;
4253
4254#if __GNUC__
4255 /* Avoid longjmp clobbering */
4256 (void) &cp;
4257#endif
4258
4259 for (cp = e.iop->argp->aword; *cp != '`'; cp++)
4260 if (*cp == 0) {
4261 err("no closing `");
4262 return (0);
4263 }
4264
4265 /* string copy with dollar expansion */
4266 src = e.iop->argp->aword;
4267 dest = child_cmd;
4268 count = 0;
4269 ignore = 0;
4270 ignore_once = 0;
4271 while ((*src != '`') && (count < LINELIM)) {
4272 if (*src == '\'')
4273 ignore = !ignore;
4274 if (*src == '\\')
4275 ignore_once = 1;
4276 if (*src == '$' && !ignore && !ignore_once) {
4277 struct var *vp;
4278 char var_name[LINELIM];
4279 char alt_value[LINELIM];
4280 int var_index = 0;
4281 int alt_index = 0;
4282 char operator = 0;
4283 int braces = 0;
4284 char *value;
4285
4286 src++;
4287 if (*src == '{') {
4288 braces = 1;
4289 src++;
4290 }
4291
4292 var_name[var_index++] = *src++;
4293 while (isalnum(*src))
4294 var_name[var_index++] = *src++;
4295 var_name[var_index] = 0;
4296
4297 if (braces) {
4298 switch (*src) {
4299 case '}':
4300 break;
4301 case '-':
4302 case '=':
4303 case '+':
4304 case '?':
4305 operator = * src;
4306 break;
4307 default:
4308 err("unclosed ${\n");
4309 return (0);
4310 }
4311 if (operator) {
4312 src++;
4313 while (*src && (*src != '}')) {
4314 alt_value[alt_index++] = *src++;
4315 }
4316 alt_value[alt_index] = 0;
4317 if (*src != '}') {
4318 err("unclosed ${\n");
4319 return (0);
4320 }
4321 }
4322 src++;
4323 }
4324
4325 if (isalpha(*var_name)) {
4326 /* let subshell handle it instead */
4327
4328 char *namep = var_name;
4329
4330 *dest++ = '$';
4331 if (braces)
4332 *dest++ = '{';
4333 while (*namep)
4334 *dest++ = *namep++;
4335 if (operator) {
4336 char *altp = alt_value;
4337 *dest++ = operator;
4338 while (*altp)
4339 *dest++ = *altp++;
4340 }
4341 if (braces)
4342 *dest++ = '}';
4343
4344 wb = addword(lookup(var_name)->name, wb);
4345 } else {
4346 /* expand */
4347
4348 vp = lookup(var_name);
4349 if (vp->value != null)
4350 value = (operator == '+') ?
4351 alt_value : vp->value;
4352 else if (operator == '?') {
4353 err(alt_value);
4354 return (0);
4355 } else if (alt_index && (operator != '+')) {
4356 value = alt_value;
4357 if (operator == '=')
4358 setval(vp, value);
4359 } else
4360 continue;
4361
4362 while (*value && (count < LINELIM)) {
4363 *dest++ = *value++;
4364 count++;
4365 }
4366 }
4367 } else {
4368 *dest++ = *src++;
4369 count++;
4370 ignore_once = 0;
4371 }
4372 }
4373 *dest = '\0';
4374
4375 if (openpipe(pf) < 0)
4376 return (0);
4377
4378 while ((i = vfork()) == -1 && errno == EAGAIN);
4379
4380 DBGPRINTF3(("GRAVE: i is %d\n", io));
4381
4382 if (i < 0) {
4383 closepipe(pf);
4384 err((char *) bb_msg_memory_exhausted);
4385 return (0);
4386 }
4387 if (i != 0) {
4388 waitpid(i, NULL, 0);
4389 e.iop->argp->aword = ++cp;
4390 close(pf[1]);
4391 PUSHIO(afile, remap(pf[0]),
4392 (int (*)(struct ioarg *)) ((quoted) ? qgravechar :
4393 gravechar));
4394 return (1);
4395 }
4396 /* allow trapped signals */
4397 /* XXX - Maybe this signal stuff should go as well? */
4398 for (j = 0; j <= _NSIG; j++)
4399 if (ourtrap[j] && signal(j, SIG_IGN) != SIG_IGN)
4400 signal(j, SIG_DFL);
4401
4402 dup2(pf[1], 1);
4403 closepipe(pf);
4404
4405 argument_list[0] = (char *) DEFAULT_SHELL;
4406 argument_list[1] = "-c";
4407 argument_list[2] = child_cmd;
4408 argument_list[3] = 0;
4409
4410 cp = rexecve(argument_list[0], argument_list, makenv(1, wb));
4411 prs(argument_list[0]);
4412 prs(": ");
4413 err(cp);
4414 _exit(1);
4415}
4416
4417
4418static char *unquote(as)
4419REGISTER char *as;
4420{
4421 REGISTER char *s;
4422
4423 if ((s = as) != NULL)
4424 while (*s)
4425 *s++ &= ~QUOTE;
4426 return (as);
4427}
4428
4429/* -------- glob.c -------- */
4430
4431/*
4432 * glob
4433 */
4434
4435#define scopy(x) strsave((x), areanum)
4436#define BLKSIZ 512
4437#define NDENT ((BLKSIZ+sizeof(struct dirent)-1)/sizeof(struct dirent))
4438
4439static struct wdblock *cl, *nl;
4440static char spcl[] = "[?*";
4441
4442static struct wdblock *glob(cp, wb)
4443char *cp;
4444struct wdblock *wb;
4445{
4446 REGISTER int i;
4447 REGISTER char *pp;
4448
4449 if (cp == 0)
4450 return (wb);
4451 i = 0;
4452 for (pp = cp; *pp; pp++)
4453 if (any(*pp, spcl))
4454 i++;
4455 else if (!any(*pp & ~QUOTE, spcl))
4456 *pp &= ~QUOTE;
4457 if (i != 0) {
4458 for (cl = addword(scopy(cp), (struct wdblock *) 0); anyspcl(cl);
4459 cl = nl) {
4460 nl = newword(cl->w_nword * 2);
4461 for (i = 0; i < cl->w_nword; i++) { /* for each argument */
4462 for (pp = cl->w_words[i]; *pp; pp++)
4463 if (any(*pp, spcl)) {
4464 globname(cl->w_words[i], pp);
4465 break;
4466 }
4467 if (*pp == '\0')
4468 nl = addword(scopy(cl->w_words[i]), nl);
4469 }
4470 for (i = 0; i < cl->w_nword; i++)
4471 DELETE(cl->w_words[i]);
4472 DELETE(cl);
4473 }
4474 for (i = 0; i < cl->w_nword; i++)
4475 unquote(cl->w_words[i]);
4476 glob0((char *) cl->w_words, cl->w_nword, sizeof(char *), xstrcmp);
4477 if (cl->w_nword) {
4478 for (i = 0; i < cl->w_nword; i++)
4479 wb = addword(cl->w_words[i], wb);
4480 DELETE(cl);
4481 return (wb);
4482 }
4483 }
4484 wb = addword(unquote(cp), wb);
4485 return (wb);
4486}
4487
4488static void globname(we, pp)
4489char *we;
4490REGISTER char *pp;
4491{
4492 REGISTER char *np, *cp;
4493 char *name, *gp, *dp;
4494 int k;
4495 DIR *dirp;
4496 struct dirent *de;
4497 char dname[NAME_MAX + 1];
4498 struct stat dbuf;
4499
4500 for (np = we; np != pp; pp--)
4501 if (pp[-1] == '/')
4502 break;
4503 for (dp = cp = space((int) (pp - np) + 3); np < pp;)
4504 *cp++ = *np++;
4505 *cp++ = '.';
4506 *cp = '\0';
4507 for (gp = cp = space(strlen(pp) + 1); *np && *np != '/';)
4508 *cp++ = *np++;
4509 *cp = '\0';
4510 dirp = opendir(dp);
4511 if (dirp == 0) {
4512 DELETE(dp);
4513 DELETE(gp);
4514 return;
4515 }
4516 dname[NAME_MAX] = '\0';
4517 while ((de = readdir(dirp)) != NULL) {
4518 /* XXX Hmmm... What this could be? (abial) */
4519 /*
4520 if (ent[j].d_ino == 0)
4521 continue;
4522 */
4523 strncpy(dname, de->d_name, NAME_MAX);
4524 if (dname[0] == '.')
4525 if (*gp != '.')
4526 continue;
4527 for (k = 0; k < NAME_MAX; k++)
4528 if (any(dname[k], spcl))
4529 dname[k] |= QUOTE;
4530 if (gmatch(dname, gp)) {
4531 name = generate(we, pp, dname, np);
4532 if (*np && !anys(np, spcl)) {
4533 if (stat(name, &dbuf)) {
4534 DELETE(name);
4535 continue;
4536 }
4537 }
4538 nl = addword(name, nl);
4539 }
4540 }
4541 closedir(dirp);
4542 DELETE(dp);
4543 DELETE(gp);
4544}
4545
4546/*
4547 * generate a pathname as below.
4548 * start..end1 / middle end
4549 * the slashes come for free
4550 */
4551static char *generate(start1, end1, middle, end)
4552char *start1;
4553REGISTER char *end1;
4554char *middle, *end;
4555{
4556 char *p;
4557 REGISTER char *op, *xp;
4558
4559 p = op =
4560 space((int) (end1 - start1) + strlen(middle) + strlen(end) + 2);
4561 for (xp = start1; xp != end1;)
4562 *op++ = *xp++;
4563 for (xp = middle; (*op++ = *xp++) != '\0';);
4564 op--;
4565 for (xp = end; (*op++ = *xp++) != '\0';);
4566 return (p);
4567}
4568
4569static int anyspcl(wb)
4570REGISTER struct wdblock *wb;
4571{
4572 REGISTER int i;
4573 REGISTER char **wd;
4574
4575 wd = wb->w_words;
4576 for (i = 0; i < wb->w_nword; i++)
4577 if (anys(spcl, *wd++))
4578 return (1);
4579 return (0);
4580}
4581
4582static int xstrcmp(p1, p2)
4583char *p1, *p2;
4584{
4585 return (strcmp(*(char **) p1, *(char **) p2));
4586}
4587
4588/* -------- word.c -------- */
4589
4590static struct wdblock *newword(nw)
4591REGISTER int nw;
4592{
4593 REGISTER struct wdblock *wb;
4594
4595 wb = (struct wdblock *) space(sizeof(*wb) + nw * sizeof(char *));
4596 wb->w_bsize = nw;
4597 wb->w_nword = 0;
4598 return (wb);
4599}
4600
4601static struct wdblock *addword(wd, wb)
4602char *wd;
4603REGISTER struct wdblock *wb;
4604{
4605 REGISTER struct wdblock *wb2;
4606 REGISTER int nw;
4607
4608 if (wb == NULL)
4609 wb = newword(NSTART);
4610 if ((nw = wb->w_nword) >= wb->w_bsize) {
4611 wb2 = newword(nw * 2);
4612 memcpy((char *) wb2->w_words, (char *) wb->w_words,
4613 nw * sizeof(char *));
4614 wb2->w_nword = nw;
4615 DELETE(wb);
4616 wb = wb2;
4617 }
4618 wb->w_words[wb->w_nword++] = wd;
4619 return (wb);
4620}
4621
4622static
4623char **getwords(wb)
4624REGISTER struct wdblock *wb;
4625{
4626 REGISTER char **wd;
4627 REGISTER int nb;
4628
4629 if (wb == NULL)
4630 return ((char **) NULL);
4631 if (wb->w_nword == 0) {
4632 DELETE(wb);
4633 return ((char **) NULL);
4634 }
4635 wd = (char **) space(nb = sizeof(*wd) * wb->w_nword);
4636 memcpy((char *) wd, (char *) wb->w_words, nb);
4637 DELETE(wb); /* perhaps should done by caller */
4638 return (wd);
4639}
4640
4641int (*func) (char *, char *);
4642int globv;
4643
4644static void glob0(a0, a1, a2, a3)
4645char *a0;
4646unsigned a1;
4647int a2;
4648int (*a3) (char *, char *);
4649{
4650 func = a3;
4651 globv = a2;
4652 glob1(a0, a0 + a1 * a2);
4653}
4654
4655static void glob1(base, lim)
4656char *base, *lim;
4657{
4658 REGISTER char *i, *j;
4659 int v2;
4660 char *lptr, *hptr;
4661 int c;
4662 unsigned n;
4663
4664
4665 v2 = globv;
4666
4667 top:
4668 if ((n = (int) (lim - base)) <= v2)
4669 return;
4670 n = v2 * (n / (2 * v2));
4671 hptr = lptr = base + n;
4672 i = base;
4673 j = lim - v2;
4674 for (;;) {
4675 if (i < lptr) {
4676 if ((c = (*func) (i, lptr)) == 0) {
4677 glob2(i, lptr -= v2);
4678 continue;
4679 }
4680 if (c < 0) {
4681 i += v2;
4682 continue;
4683 }
4684 }
4685
4686 begin:
4687 if (j > hptr) {
4688 if ((c = (*func) (hptr, j)) == 0) {
4689 glob2(hptr += v2, j);
4690 goto begin;
4691 }
4692 if (c > 0) {
4693 if (i == lptr) {
4694 glob3(i, hptr += v2, j);
4695 i = lptr += v2;
4696 goto begin;
4697 }
4698 glob2(i, j);
4699 j -= v2;
4700 i += v2;
4701 continue;
4702 }
4703 j -= v2;
4704 goto begin;
4705 }
4706
4707
4708 if (i == lptr) {
4709 if (lptr - base >= lim - hptr) {
4710 glob1(hptr + v2, lim);
4711 lim = lptr;
4712 } else {
4713 glob1(base, lptr);
4714 base = hptr + v2;
4715 }
4716 goto top;
4717 }
4718
4719
4720 glob3(j, lptr -= v2, i);
4721 j = hptr -= v2;
4722 }
4723}
4724
4725static void glob2(i, j)
4726char *i, *j;
4727{
4728 REGISTER char *index1, *index2, c;
4729 int m;
4730
4731 m = globv;
4732 index1 = i;
4733 index2 = j;
4734 do {
4735 c = *index1;
4736 *index1++ = *index2;
4737 *index2++ = c;
4738 } while (--m);
4739}
4740
4741static void glob3(i, j, k)
4742char *i, *j, *k;
4743{
4744 REGISTER char *index1, *index2, *index3;
4745 int c;
4746 int m;
4747
4748 m = globv;
4749 index1 = i;
4750 index2 = j;
4751 index3 = k;
4752 do {
4753 c = *index1;
4754 *index1++ = *index3;
4755 *index3++ = *index2;
4756 *index2++ = c;
4757 } while (--m);
4758}
4759
4760/* -------- io.c -------- */
4761
4762/*
4763 * shell IO
4764 */
4765
4766static int my_getc(int ec)
4767{
4768 REGISTER int c;
4769
4770 if (e.linep > elinep) {
4771 while ((c = readc()) != '\n' && c);
4772 err("input line too long");
4773 gflg++;
4774 return (c);
4775 }
4776 c = readc();
4777 if ((ec != '\'') && (ec != '`') && (e.iop->task != XGRAVE)) {
4778 if (c == '\\') {
4779 c = readc();
4780 if (c == '\n' && ec != '\"')
4781 return (my_getc(ec));
4782 c |= QUOTE;
4783 }
4784 }
4785 return (c);
4786}
4787
4788static void unget(c)
4789int c;
4790{
4791 if (e.iop >= e.iobase)
4792 e.iop->peekc = c;
4793}
4794
4795static int eofc()
4796{
4797 return e.iop < e.iobase || (e.iop->peekc == 0 && e.iop->prev == 0);
4798}
4799
4800static int readc()
4801{
4802 REGISTER int c;
4803
4804 RCPRINTF(("READC: e.iop 0x%x, e.iobase 0x%x\n", e.iop, e.iobase));
4805
4806 for (; e.iop >= e.iobase; e.iop--) {
4807 RCPRINTF(("READC: e.iop 0x%x, peekc 0x%x\n", e.iop, e.iop->peekc));
4808 if ((c = e.iop->peekc) != '\0') {
4809 e.iop->peekc = 0;
4810 return (c);
4811 } else {
4812 if (e.iop->prev != 0) {
4813 if ((c = (*e.iop->iofn) (e.iop->argp, e.iop)) != '\0') {
4814 if (c == -1) {
4815 e.iop++;
4816 continue;
4817 }
4818 if (e.iop == iostack)
4819 ioecho(c);
4820 return (e.iop->prev = c);
4821 } else if (e.iop->task == XIO && e.iop->prev != '\n') {
4822 e.iop->prev = 0;
4823 if (e.iop == iostack)
4824 ioecho('\n');
4825 return '\n';
4826 }
4827 }
4828 if (e.iop->task == XIO) {
4829 if (multiline) {
4830 return e.iop->prev = 0;
4831 }
4832 if (interactive && e.iop == iostack + 1) {
4833#ifdef CONFIG_FEATURE_COMMAND_EDITING
4834 current_prompt = prompt->value;
4835#else
4836 prs(prompt->value);
4837#endif
4838 }
4839 }
4840 }
4841
4842 } /* FOR */
4843
4844 if (e.iop >= iostack) {
4845 RCPRINTF(("READC: return 0, e.iop 0x%x\n", e.iop));
4846 return (0);
4847 }
4848
4849 DBGPRINTF(("READC: leave()...\n"));
4850 leave();
4851
4852 /* NOTREACHED */
4853 return (0);
4854}
4855
4856static void ioecho(c)
4857char c;
4858{
4859 if (flag['v'])
4860 write(2, &c, sizeof c);
4861}
4862
4863
4864static void pushio(struct ioarg *argp, int (*fn) (struct ioarg *))
4865{
4866 DBGPRINTF(("PUSHIO: argp 0x%x, argp->afid 0x%x, e.iop 0x%x\n", argp,
4867 argp->afid, e.iop));
4868
4869 /* Set env ptr for io source to next array spot and check for array overflow */
4870 if (++e.iop >= &iostack[NPUSH]) {
4871 e.iop--;
4872 err("Shell input nested too deeply");
4873 gflg++;
4874 return;
4875 }
4876
4877 /* We did not overflow the NPUSH array spots so setup data structs */
4878
4879 e.iop->iofn = (int (*)(struct ioarg *, struct io *)) fn; /* Store data source func ptr */
4880
4881 if (argp->afid != AFID_NOBUF)
4882 e.iop->argp = argp;
4883 else {
4884
4885 e.iop->argp = ioargstack + (e.iop - iostack); /* MAL - index into stack */
4886 *e.iop->argp = *argp; /* copy data from temp area into stack spot */
4887
4888 /* MAL - mainbuf is for 1st data source (command line?) and all nested use a single shared buffer? */
4889
4890 if (e.iop == &iostack[0])
4891 e.iop->argp->afbuf = &mainbuf;
4892 else
4893 e.iop->argp->afbuf = &sharedbuf;
4894
4895 /* MAL - if not a termimal AND (commandline OR readable file) then give it a buffer id? */
4896 /* This line appears to be active when running scripts from command line */
4897 if ((isatty(e.iop->argp->afile) == 0)
4898 && (e.iop == &iostack[0]
4899 || lseek(e.iop->argp->afile, 0L, 1) != -1)) {
4900 if (++bufid == AFID_NOBUF) /* counter rollover check, AFID_NOBUF = 11111111 */
4901 bufid = AFID_ID; /* AFID_ID = 0 */
4902
4903 e.iop->argp->afid = bufid; /* assign buffer id */
4904 }
4905
4906 DBGPRINTF(("PUSHIO: iostack 0x%x, e.iop 0x%x, afbuf 0x%x\n",
4907 iostack, e.iop, e.iop->argp->afbuf));
4908 DBGPRINTF(("PUSHIO: mbuf 0x%x, sbuf 0x%x, bid %d, e.iop 0x%x\n",
4909 &mainbuf, &sharedbuf, bufid, e.iop));
4910
4911 }
4912
4913 e.iop->prev = ~'\n';
4914 e.iop->peekc = 0;
4915 e.iop->xchar = 0;
4916 e.iop->nlcount = 0;
4917
4918 if (fn == filechar || fn == linechar)
4919 e.iop->task = XIO;
4920 else if (fn == (int (*)(struct ioarg *)) gravechar
4921 || fn == (int (*)(struct ioarg *)) qgravechar)
4922 e.iop->task = XGRAVE;
4923 else
4924 e.iop->task = XOTHER;
4925
4926 return;
4927}
4928
4929static struct io *setbase(ip)
4930struct io *ip;
4931{
4932 REGISTER struct io *xp;
4933
4934 xp = e.iobase;
4935 e.iobase = ip;
4936 return (xp);
4937}
4938
4939/*
4940 * Input generating functions
4941 */
4942
4943/*
4944 * Produce the characters of a string, then a newline, then EOF.
4945 */
4946static int nlchar(ap)
4947REGISTER struct ioarg *ap;
4948{
4949 REGISTER int c;
4950
4951 if (ap->aword == NULL)
4952 return (0);
4953 if ((c = *ap->aword++) == 0) {
4954 ap->aword = NULL;
4955 return ('\n');
4956 }
4957 return (c);
4958}
4959
4960/*
4961 * Given a list of words, produce the characters
4962 * in them, with a space after each word.
4963 */
4964static int wdchar(ap)
4965REGISTER struct ioarg *ap;
4966{
4967 REGISTER char c;
4968 REGISTER char **wl;
4969
4970 if ((wl = ap->awordlist) == NULL)
4971 return (0);
4972 if (*wl != NULL) {
4973 if ((c = *(*wl)++) != 0)
4974 return (c & 0177);
4975 ap->awordlist++;
4976 return (' ');
4977 }
4978 ap->awordlist = NULL;
4979 return ('\n');
4980}
4981
4982/*
4983 * Return the characters of a list of words,
4984 * producing a space between them.
4985 */
4986static int dolchar(ap)
4987REGISTER struct ioarg *ap;
4988{
4989 REGISTER char *wp;
4990
4991 if ((wp = *ap->awordlist++) != NULL) {
4992 PUSHIO(aword, wp, *ap->awordlist == NULL ? strchar : xxchar);
4993 return (-1);
4994 }
4995 return (0);
4996}
4997
4998static int xxchar(ap)
4999REGISTER struct ioarg *ap;
5000{
5001 REGISTER int c;
5002
5003 if (ap->aword == NULL)
5004 return (0);
5005 if ((c = *ap->aword++) == '\0') {
5006 ap->aword = NULL;
5007 return (' ');
5008 }
5009 return (c);
5010}
5011
5012/*
5013 * Produce the characters from a single word (string).
5014 */
5015static int strchar(ap)
5016REGISTER struct ioarg *ap;
5017{
5018 REGISTER int c;
5019
5020 if (ap->aword == NULL || (c = *ap->aword++) == 0)
5021 return (0);
5022 return (c);
5023}
5024
5025/*
5026 * Produce quoted characters from a single word (string).
5027 */
5028static int qstrchar(ap)
5029REGISTER struct ioarg *ap;
5030{
5031 REGISTER int c;
5032
5033 if (ap->aword == NULL || (c = *ap->aword++) == 0)
5034 return (0);
5035 return (c | QUOTE);
5036}
5037
5038/*
5039 * Return the characters from a file.
5040 */
5041static int filechar(ap)
5042REGISTER struct ioarg *ap;
5043{
5044 REGISTER int i;
5045 char c;
5046 struct iobuf *bp = ap->afbuf;
5047
5048 if (ap->afid != AFID_NOBUF) {
5049 if ((i = ap->afid != bp->id) || bp->bufp == bp->ebufp) {
5050
5051 if (i)
5052 lseek(ap->afile, ap->afpos, 0);
5053
5054 i = safe_read(ap->afile, bp->buf, sizeof(bp->buf));
5055
5056 if (i <= 0) {
5057 closef(ap->afile);
5058 return 0;
5059 }
5060
5061 bp->id = ap->afid;
5062 bp->ebufp = (bp->bufp = bp->buf) + i;
5063 }
5064
5065 ap->afpos++;
5066 return *bp->bufp++ & 0177;
5067 }
5068#ifdef CONFIG_FEATURE_COMMAND_EDITING
5069 if (interactive && isatty(ap->afile)) {
5070 static char mycommand[BUFSIZ];
5071 static int position = 0, size = 0;
5072
5073 while (size == 0 || position >= size) {
5074 cmdedit_read_input(current_prompt, mycommand);
5075 size = strlen(mycommand);
5076 position = 0;
5077 }
5078 c = mycommand[position];
5079 position++;
5080 return (c);
5081 } else
5082#endif
5083
5084 {
5085 i = safe_read(ap->afile, &c, sizeof(c));
5086 return (i == sizeof(c) ? (c & 0x7f) : (closef(ap->afile), 0));
5087 }
5088}
5089
5090/*
5091 * Return the characters from a here temp file.
5092 */
5093static int herechar(ap)
5094REGISTER struct ioarg *ap;
5095{
5096 char c;
5097
5098
5099 if (read(ap->afile, &c, sizeof(c)) != sizeof(c)) {
5100 close(ap->afile);
5101 c = 0;
5102 }
5103 return (c);
5104
5105}
5106
5107/*
5108 * Return the characters produced by a process (`...`).
5109 * Quote them if required, and remove any trailing newline characters.
5110 */
5111static int gravechar(ap, iop)
5112struct ioarg *ap;
5113struct io *iop;
5114{
5115 REGISTER int c;
5116
5117 if ((c = qgravechar(ap, iop) & ~QUOTE) == '\n')
5118 c = ' ';
5119 return (c);
5120}
5121
5122static int qgravechar(ap, iop)
5123REGISTER struct ioarg *ap;
5124struct io *iop;
5125{
5126 REGISTER int c;
5127
5128 DBGPRINTF3(("QGRAVECHAR: enter, ap=0x%x, iop=0x%x\n", ap, iop));
5129
5130 if (iop->xchar) {
5131 if (iop->nlcount) {
5132 iop->nlcount--;
5133 return ('\n' | QUOTE);
5134 }
5135 c = iop->xchar;
5136 iop->xchar = 0;
5137 } else if ((c = filechar(ap)) == '\n') {
5138 iop->nlcount = 1;
5139 while ((c = filechar(ap)) == '\n')
5140 iop->nlcount++;
5141 iop->xchar = c;
5142 if (c == 0)
5143 return (c);
5144 iop->nlcount--;
5145 c = '\n';
5146 }
5147 return (c != 0 ? c | QUOTE : 0);
5148}
5149
5150/*
5151 * Return a single command (usually the first line) from a file.
5152 */
5153static int linechar(ap)
5154REGISTER struct ioarg *ap;
5155{
5156 REGISTER int c;
5157
5158 if ((c = filechar(ap)) == '\n') {
5159 if (!multiline) {
5160 closef(ap->afile);
5161 ap->afile = -1; /* illegal value */
5162 }
5163 }
5164 return (c);
5165}
5166
5167static void prs(s)
5168REGISTER char *s;
5169{
5170 if (*s)
5171 write(2, s, strlen(s));
5172}
5173
5174static void prn(u)
5175unsigned u;
5176{
5177 prs(itoa(u));
5178}
5179
5180static void closef(i)
5181REGISTER int i;
5182{
5183 if (i > 2)
5184 close(i);
5185}
5186
5187static void closeall()
5188{
5189 REGISTER int u;
5190
5191 for (u = NUFILE; u < NOFILE;)
5192 close(u++);
5193}
5194
5195
5196/*
5197 * remap fd into Shell's fd space
5198 */
5199static int remap(fd)
5200REGISTER int fd;
5201{
5202 REGISTER int i;
5203 int map[NOFILE];
5204 int newfd;
5205
5206
5207 DBGPRINTF(("REMAP: fd=%d, e.iofd=%d\n", fd, e.iofd));
5208
5209 if (fd < e.iofd) {
5210 for (i = 0; i < NOFILE; i++)
5211 map[i] = 0;
5212
5213 do {
5214 map[fd] = 1;
5215 newfd = dup(fd);
5216 fd = newfd;
5217 } while (fd >= 0 && fd < e.iofd);
5218
5219 for (i = 0; i < NOFILE; i++)
5220 if (map[i])
5221 close(i);
5222
5223 if (fd < 0)
5224 err("too many files open in shell");
5225 }
5226
5227 return (fd);
5228}
5229
5230static int openpipe(pv)
5231REGISTER int *pv;
5232{
5233 REGISTER int i;
5234
5235 if ((i = pipe(pv)) < 0)
5236 err("can't create pipe - try again");
5237 return (i);
5238}
5239
5240static void closepipe(pv)
5241REGISTER int *pv;
5242{
5243 if (pv != NULL) {
5244 close(*pv++);
5245 close(*pv);
5246 }
5247}
5248
5249/* -------- here.c -------- */
5250
5251/*
5252 * here documents
5253 */
5254
5255static void markhere(s, iop)
5256REGISTER char *s;
5257struct ioword *iop;
5258{
5259 REGISTER struct here *h, *lh;
5260
5261 DBGPRINTF7(("MARKHERE: enter, s=0x%x\n", s));
5262
5263 h = (struct here *) space(sizeof(struct here));
5264 if (h == 0)
5265 return;
5266
5267 h->h_tag = evalstr(s, DOSUB);
5268 if (h->h_tag == 0)
5269 return;
5270
5271 h->h_iop = iop;
5272 iop->io_name = 0;
5273 h->h_next = NULL;
5274 if (inhere == 0)
5275 inhere = h;
5276 else
5277 for (lh = inhere; lh != NULL; lh = lh->h_next)
5278 if (lh->h_next == 0) {
5279 lh->h_next = h;
5280 break;
5281 }
5282 iop->io_flag |= IOHERE | IOXHERE;
5283 for (s = h->h_tag; *s; s++)
5284 if (*s & QUOTE) {
5285 iop->io_flag &= ~IOXHERE;
5286 *s &= ~QUOTE;
5287 }
5288 h->h_dosub = iop->io_flag & IOXHERE;
5289}
5290
5291static void gethere()
5292{
5293 REGISTER struct here *h, *hp;
5294
5295 DBGPRINTF7(("GETHERE: enter...\n"));
5296
5297 /* Scan here files first leaving inhere list in place */
5298 for (hp = h = inhere; h != NULL; hp = h, h = h->h_next)
5299 readhere(&h->h_iop->io_name, h->h_tag, h->h_dosub ? 0 : '\'');
5300
5301 /* Make inhere list active - keep list intact for scraphere */
5302 if (hp != NULL) {
5303 hp->h_next = acthere;
5304 acthere = inhere;
5305 inhere = NULL;
5306 }
5307}
5308
5309static void readhere(name, s, ec)
5310char **name;
5311REGISTER char *s;
5312int ec;
5313{
5314 int tf;
5315 char tname[30] = ".msh_XXXXXX";
5316 REGISTER int c;
5317 jmp_buf ev;
5318 char myline[LINELIM + 1];
5319 char *thenext;
5320
5321 DBGPRINTF7(("READHERE: enter, name=0x%x, s=0x%x\n", name, s));
5322
5323 tf = mkstemp(tname);
5324 if (tf < 0)
5325 return;
5326
5327 *name = strsave(tname, areanum);
5328 if (newenv(setjmp(errpt = ev)) != 0)
5329 unlink(tname);
5330 else {
5331 pushio(e.iop->argp, (int (*)(struct ioarg *)) e.iop->iofn);
5332 e.iobase = e.iop;
5333 for (;;) {
5334 if (interactive && e.iop <= iostack) {
5335#ifdef CONFIG_FEATURE_COMMAND_EDITING
5336 current_prompt = cprompt->value;
5337#else
5338 prs(cprompt->value);
5339#endif
5340 }
5341 thenext = myline;
5342 while ((c = my_getc(ec)) != '\n' && c) {
5343 if (ec == '\'')
5344 c &= ~QUOTE;
5345 if (thenext >= &myline[LINELIM]) {
5346 c = 0;
5347 break;
5348 }
5349 *thenext++ = c;
5350 }
5351 *thenext = 0;
5352 if (strcmp(s, myline) == 0 || c == 0)
5353 break;
5354 *thenext++ = '\n';
5355 write(tf, myline, (int) (thenext - myline));
5356 }
5357 if (c == 0) {
5358 prs("here document `");
5359 prs(s);
5360 err("' unclosed");
5361 }
5362 quitenv();
5363 }
5364 close(tf);
5365}
5366
5367/*
5368 * open here temp file.
5369 * if unquoted here, expand here temp file into second temp file.
5370 */
5371static int herein(hname, xdoll)
5372char *hname;
5373int xdoll;
5374{
5375 REGISTER int hf;
5376 int tf;
5377
5378#if __GNUC__
5379 /* Avoid longjmp clobbering */
5380 (void) &tf;
5381#endif
5382 if (hname == NULL)
5383 return (-1);
5384
5385 DBGPRINTF7(("HEREIN: hname is %s, xdoll=%d\n", hname, xdoll));
5386
5387 hf = open(hname, 0);
5388 if (hf < 0)
5389 return (-1);
5390
5391 if (xdoll) {
5392 char c;
5393 char tname[30] = ".msh_XXXXXX";
5394 jmp_buf ev;
5395
5396 tf = mkstemp(tname);
5397 if (tf < 0)
5398 return (-1);
5399 if (newenv(setjmp(errpt = ev)) == 0) {
5400 PUSHIO(afile, hf, herechar);
5401 setbase(e.iop);
5402 while ((c = subgetc(0, 0)) != 0) {
5403 c &= ~QUOTE;
5404 write(tf, &c, sizeof c);
5405 }
5406 quitenv();
5407 } else
5408 unlink(tname);
5409 close(tf);
5410 tf = open(tname, 0);
5411 unlink(tname);
5412 return (tf);
5413 } else
5414 return (hf);
5415}
5416
5417static void scraphere()
5418{
5419 REGISTER struct here *h;
5420
5421 DBGPRINTF7(("SCRAPHERE: enter...\n"));
5422
5423 for (h = inhere; h != NULL; h = h->h_next) {
5424 if (h->h_iop && h->h_iop->io_name)
5425 unlink(h->h_iop->io_name);
5426 }
5427 inhere = NULL;
5428}
5429
5430/* unlink here temp files before a freearea(area) */
5431static void freehere(area)
5432int area;
5433{
5434 REGISTER struct here *h, *hl;
5435
5436 DBGPRINTF6(("FREEHERE: enter, area=%d\n", area));
5437
5438 hl = NULL;
5439 for (h = acthere; h != NULL; h = h->h_next)
5440 if (getarea((char *) h) >= area) {
5441 if (h->h_iop->io_name != NULL)
5442 unlink(h->h_iop->io_name);
5443 if (hl == NULL)
5444 acthere = h->h_next;
5445 else
5446 hl->h_next = h->h_next;
5447 } else
5448 hl = h;
5449}
5450
5451
5452
5453/*
5454 * Copyright (c) 1987,1997, Prentice Hall
5455 * All rights reserved.
5456 *
5457 * Redistribution and use of the MINIX operating system in source and
5458 * binary forms, with or without modification, are permitted provided
5459 * that the following conditions are met:
5460 *
5461 * Redistributions of source code must retain the above copyright
5462 * notice, this list of conditions and the following disclaimer.
5463 *
5464 * Redistributions in binary form must reproduce the above
5465 * copyright notice, this list of conditions and the following
5466 * disclaimer in the documentation and/or other materials provided
5467 * with the distribution.
5468 *
5469 * Neither the name of Prentice Hall nor the names of the software
5470 * authors or contributors may be used to endorse or promote
5471 * products derived from this software without specific prior
5472 * written permission.
5473 *
5474 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, AUTHORS, AND
5475 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
5476 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
5477 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
5478 * IN NO EVENT SHALL PRENTICE HALL OR ANY AUTHORS OR CONTRIBUTORS BE
5479 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
5480 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
5481 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
5482 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
5483 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
5484 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
5485 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5486 *
5487 */
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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6mainmenu "BusyBox Configuration"
7
8config HAVE_DOT_CONFIG
9 bool
10 default y
11
12menu "General Configuration"
13
14choice
15 prompt "Buffer allocation policy"
16 default CONFIG_FEATURE_BUFFERS_USE_MALLOC
17 help
18 There are 3 ways BusyBox can handle buffer allocations:
19 - Use malloc. This costs code size for the call to xmalloc.
20 - Put them on stack. For some very small machines with limited stack
21 space, this can be deadly. For most folks, this works just fine.
22 - Put them in BSS. This works beautifully for computers with a real
23 MMU (and OS support), but wastes runtime RAM for uCLinux. This
24 behavior was the only one available for BusyBox versions 0.48 and
25 earlier.
26
27config CONFIG_FEATURE_BUFFERS_USE_MALLOC
28 bool "Allocate with Malloc"
29
30config CONFIG_FEATURE_BUFFERS_GO_ON_STACK
31 bool "Allocate on the Stack"
32
33config CONFIG_FEATURE_BUFFERS_GO_IN_BSS
34 bool "Allocate in the .bss section"
35
36endchoice
37
38config CONFIG_FEATURE_VERBOSE_USAGE
39 bool "Show verbose applet usage messages"
40 default n
41 help
42 All BusyBox applets will show more verbose help messages when
43 busybox is invoked with --help. This will add a lot of text to the
44 busybox binary. In the default configuration, this will add about
45 13k, but it can add much more depending on your configuration.
46
47config CONFIG_FEATURE_INSTALLER
48 bool "Support --install [-s] to install applet links at runtime"
49 default n
50 help
51 Enable 'busybox --install [-s]' support. This will allow you to use
52 busybox at runtime to create hard links or symlinks for all the
53 applets that are compiled into busybox. This feature requires the
54 /proc filesystem.
55
56config CONFIG_LOCALE_SUPPORT
57 bool "Enable locale support (system needs locale for this to work)"
58 default n
59 help
60 Enable this if your system has locale support and you would like
61 busybox to support locale settings.
62
63config CONFIG_FEATURE_DEVFS
64 bool "Support for devfs"
65 default n
66 help
67 Enable if you want BusyBox to work with devfs.
68
69config CONFIG_FEATURE_DEVPTS
70 bool "Use the devpts filesystem for Unix98 PTYs"
71 default y if CONFIG_FEATURE_DEVFS
72 help
73 Enable if you want BusyBox to use Unix98 PTY support. If enabled,
74 busybox will use /dev/ptmx for the master side of the pseudoterminal
75 and /dev/pts/<number> for the slave side. Otherwise, BSD style
76 /dev/ttyp<number> will be used. To use this option, you should have
77 devpts or devfs mounted.
78
79config CONFIG_FEATURE_CLEAN_UP
80 bool "Clean up all memory before exiting (usually not needed)"
81 default n
82 help
83 As a size optimization, busybox by default does not cleanup memory
84 that is dynamically allocated or close files before exiting. This
85 saves space and is usually not needed since the OS will clean up for
86 us. Don't enable this unless you have a really good reason to clean
87 things up manually.
88
89config CONFIG_FEATURE_SUID
90 bool "Support for SUID/SGID handling"
91 default n
92 help
93 Support SUID and SGID binaries.
94
95config CONFIG_FEATURE_SUID_CONFIG
96 bool "Runtime SUID/SGID configuration via /etc/busybox.conf"
97 default y if CONFIG_FEATURE_SUID
98 depends on CONFIG_FEATURE_SUID
99 help
100 Allow the SUID / SGID state of an applet to be determined runtime by
101 checking /etc/busybox.conf. The format of this file is as follows:
102
103 <applet> = [Ssx-][Ssx-][x-] (<username>|<uid>).(<groupname>|<gid>)
104
105 An example might help:
106
107 [SUID]
108 su = ssx root.0 # applet su can be run by anyone and runs with euid=0/egid=0
109 su = ssx # exactly the same
110
111 mount = sx- root.disk # applet mount can be run by root and members of group disk
112 # and runs with euid=0
113
114 cp = --- # disable applet cp for everyone
115
116 Robert 'sandman' Griebl has more information here:
117 <url: http://www.softforge.de/bb/suid.html >.
118
119config CONFIG_FEATURE_SUID_CONFIG_QUIET
120 bool "Suppress warning message if /etc/busybox.conf is not readable"
121 default n
122 depends on CONFIG_FEATURE_SUID_CONFIG
123 help
124 /etc/busybox.conf should be readable by the user needing the SUID, check
125 this option to avoid users to be notified about missing permissions.
126
127config CONFIG_SELINUX
128 bool "Support NSA Security Enhanced Linux"
129 default n
130 help
131 Enable support for SE Linux in applets ls, ps, and id. Also provide
132 the option of compiling in SE Linux applets.
133
134 If you do not have a complete SE Linux Full Userland installed, this
135 stuff will not compile. Go visit
136 http://www.nsa.gov/selinux/index.html
137 to download the necessary stuff to allow busybox to compile with this
138 option enabled.
139
140 Most people will leave this set to 'N'.
141
142endmenu
143
144menu 'Build Options'
145
146config CONFIG_STATIC
147 bool "Build BusyBox as a static binary (no shared libs)"
148 default n
149 help
150 If you want to build a static BusyBox binary, which does not
151 use or require any shared libraries, then enable this option.
152 This can cause BusyBox to be considerably larger, so you should
153 leave this option false unless you have a good reason (i.e.
154 your target platform does not support shared libraries, or
155 you are building an initrd which doesn't need anything but
156 BusyBox, etc).
157
158 Most people will leave this set to 'N'.
159
160config CONFIG_LFS
161 bool "Build with Large File Support (for accessing files > 2 GB)"
162 default n
163 select FDISK_SUPPORT_LARGE_DISKS
164 help
165 If you want to build BusyBox with large file support, then enable
166 this option. This will have no effect if your kernel or your C
167 library lacks large file support for large files. Some of the
168 programs that can benefit from large file support include dd, gzip,
169 cp, mount, tar, and many others. If you want to access files larger
170 than 2 Gigabytes, enable this option. Otherwise, leave it set to 'N'.
171
172config USING_CROSS_COMPILER
173 bool "Do you want to build BusyBox with a Cross Compiler?"
174 default n
175 help
176 Do you want to build BusyBox with a Cross Compiler? If so,
177 then enable this option. Otherwise leave it set to 'N'.
178
179config CROSS_COMPILER_PREFIX
180 string "Cross Compiler prefix"
181 default "/usr/i386-linux-uclibc/bin/i386-uclibc-"
182 depends on USING_CROSS_COMPILER
183 help
184 If you want to build BusyBox with a cross compiler, then you
185 will need to set this to the cross-compiler prefix. For example,
186 if my cross-compiler is /usr/i386-linux-uclibc/bin/i386-uclibc-gcc
187 then I would enter '/usr/i386-linux-uclibc/bin/i386-uclibc-' here,
188 which will ensure the correct compiler is used.
189
190config EXTRA_CFLAGS_OPTIONS
191 string "Any extra CFLAGS options for the compiler?"
192 default ""
193 help
194 Do you want to pass any extra CFLAGS options to the compiler as
195 you build BusyBox? If so, this is the option for you... For example,
196 if you want to add some simple compiler switches (like -march=i686),
197 or check for warnings using -Werror, just those options here.
198
199endmenu
200
201menu 'Installation Options'
202
203config CONFIG_INSTALL_NO_USR
204 bool "Don't use /usr"
205 default n
206 help
207 Disable use of /usr. Don't activate this option if you don't know
208 that you really want this behaviour.
209
210config PREFIX
211 string "BusyBox installation prefix"
212 default "./_install"
213 help
214 Define your directory to install BusyBox files/subdirs in.
215
216
217
218endmenu
219
220source archival/Config.in
221source coreutils/Config.in
222source console-tools/Config.in
223source debianutils/Config.in
224source editors/Config.in
225source findutils/Config.in
226source init/Config.in
227source loginutils/Config.in
228source miscutils/Config.in
229source modutils/Config.in
230source networking/Config.in
231source procps/Config.in
232source shell/Config.in
233source sysklogd/Config.in
234source util-linux/Config.in
235
236menu 'Debugging Options'
237
238config CONFIG_DEBUG
239 bool "Build BusyBox with Debugging symbols"
240 default n
241 help
242 Say Y here if you wish to compile BusyBox with debugging symbols.
243 This will allow you to use a debugger to examine BusyBox internals
244 while applets are running. This increases the size of the binary
245 considerably and should only be used when doing development.
246 If you are doing development and want to debug BusyBox, answer Y.
247
248 Most people should answer N.
249
250choice
251 prompt "Additional debugging library"
252 default CONFIG_NO_DEBUG_LIB
253 depends on CONFIG_DEBUG
254 help
255 Using an additional debugging library will make BusyBox become
256 considerable larger and will cause it to run more slowly. You
257 should always leave this option disabled for production use.
258
259 dmalloc support:
260 ----------------
261 This enables compiling with dmalloc ( http://dmalloc.com/ )
262 which is an excellent public domain mem leak and malloc problem
263 detector. To enable dmalloc, before running busybox you will
264 want to properly set your environment, for example:
265 export DMALLOC_OPTIONS=debug=0x34f47d83,inter=100,log=logfile
266 The 'debug=' value is generated using the following command
267 dmalloc -p log-stats -p log-non-free -p log-bad-space -p log-elapsed-time \
268 -p check-fence -p check-heap -p check-lists -p check-blank \
269 -p check-funcs -p realloc-copy -p allow-free-null
270
271 Electric-fence support:
272 -----------------------
273 This enables compiling with Electric-fence support. Electric
274 fence is another very useful malloc debugging library which uses
275 your computer's virtual memory hardware to detect illegal memory
276 accesses. This support will make BusyBox be considerable larger
277 and run slower, so you should leave this option disabled unless
278 you are hunting a hard to find memory problem.
279
280
281config CONFIG_NO_DEBUG_LIB
282 bool "None"
283
284config CONFIG_DMALLOC
285 bool "Dmalloc"
286
287config CONFIG_EFENCE
288 bool "Electric-fence"
289
290endchoice
291
292
293endmenu
294
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 @@
1#
2# Automatically generated make config: don't edit
3#
4HAVE_DOT_CONFIG=y
5
6#
7# General Configuration
8#
9# CONFIG_FEATURE_BUFFERS_USE_MALLOC is not set
10CONFIG_FEATURE_BUFFERS_GO_ON_STACK=y
11# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
12CONFIG_FEATURE_VERBOSE_USAGE=y
13# CONFIG_FEATURE_INSTALLER is not set
14# CONFIG_LOCALE_SUPPORT is not set
15# CONFIG_FEATURE_DEVFS is not set
16CONFIG_FEATURE_DEVPTS=y
17# CONFIG_FEATURE_CLEAN_UP is not set
18# CONFIG_FEATURE_SUID is not set
19# CONFIG_SELINUX is not set
20
21#
22# Build Options
23#
24# CONFIG_STATIC is not set
25# CONFIG_LFS is not set
26# USING_CROSS_COMPILER is not set
27EXTRA_CFLAGS_OPTIONS=""
28
29#
30# Installation Options
31#
32# CONFIG_INSTALL_NO_USR is not set
33PREFIX="./_install"
34
35#
36# Archival Utilities
37#
38# CONFIG_AR is not set
39CONFIG_BUNZIP2=y
40# CONFIG_CPIO is not set
41# CONFIG_DPKG is not set
42# CONFIG_DPKG_DEB is not set
43CONFIG_GUNZIP=y
44# CONFIG_FEATURE_GUNZIP_UNCOMPRESS is not set
45CONFIG_GZIP=y
46# CONFIG_RPM2CPIO is not set
47# CONFIG_RPM is not set
48CONFIG_TAR=y
49CONFIG_FEATURE_TAR_CREATE=y
50CONFIG_FEATURE_TAR_BZIP2=y
51# CONFIG_FEATURE_TAR_FROM is not set
52CONFIG_FEATURE_TAR_GZIP=y
53# CONFIG_FEATURE_TAR_COMPRESS is not set
54CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY=y
55CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
56# CONFIG_FEATURE_TAR_LONG_OPTIONS is not set
57# CONFIG_UNCOMPRESS is not set
58CONFIG_UNZIP=y
59
60#
61# Common options for cpio and tar
62#
63# CONFIG_FEATURE_UNARCHIVE_TAPE is not set
64
65#
66# Coreutils
67#
68CONFIG_BASENAME=y
69# CONFIG_CAL is not set
70CONFIG_CAT=y
71CONFIG_CHGRP=y
72CONFIG_CHMOD=y
73CONFIG_CHOWN=y
74CONFIG_CHROOT=y
75CONFIG_CMP=y
76CONFIG_CP=y
77CONFIG_CUT=y
78CONFIG_DATE=y
79CONFIG_FEATURE_DATE_ISOFMT=y
80CONFIG_DD=y
81CONFIG_DF=y
82CONFIG_DIRNAME=y
83# CONFIG_DOS2UNIX is not set
84CONFIG_DU=y
85CONFIG_FEATURE_DU_DEFALT_BLOCKSIZE_1K=y
86CONFIG_ECHO=y
87CONFIG_FEATURE_FANCY_ECHO=y
88CONFIG_ENV=y
89CONFIG_EXPR=y
90CONFIG_FALSE=y
91# CONFIG_FOLD is not set
92CONFIG_HEAD=y
93# CONFIG_FEATURE_FANCY_HEAD is not set
94# CONFIG_HOSTID is not set
95CONFIG_ID=y
96CONFIG_INSTALL=y
97# CONFIG_LENGTH is not set
98CONFIG_LN=y
99# CONFIG_LOGNAME is not set
100CONFIG_LS=y
101CONFIG_FEATURE_LS_FILETYPES=y
102CONFIG_FEATURE_LS_FOLLOWLINKS=y
103CONFIG_FEATURE_LS_RECURSIVE=y
104CONFIG_FEATURE_LS_SORTFILES=y
105CONFIG_FEATURE_LS_TIMESTAMPS=y
106CONFIG_FEATURE_LS_USERNAME=y
107CONFIG_FEATURE_LS_COLOR=y
108# CONFIG_MD5SUM is not set
109CONFIG_MKDIR=y
110# CONFIG_MKFIFO is not set
111CONFIG_MKNOD=y
112CONFIG_MV=y
113# CONFIG_OD is not set
114# CONFIG_PRINTF is not set
115CONFIG_PWD=y
116# CONFIG_REALPATH is not set
117CONFIG_RM=y
118CONFIG_RMDIR=y
119# CONFIG_SEQ is not set
120# CONFIG_SHA1SUM is not set
121CONFIG_SLEEP=y
122# CONFIG_FEATURE_FANCY_SLEEP is not set
123CONFIG_SORT=y
124# CONFIG_STTY is not set
125CONFIG_SYNC=y
126CONFIG_TAIL=y
127CONFIG_FEATURE_FANCY_TAIL=y
128CONFIG_TEE=y
129CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
130CONFIG_TEST=y
131
132#
133# test (forced enabled for use with shell)
134#
135CONFIG_TOUCH=y
136CONFIG_TR=y
137CONFIG_TRUE=y
138CONFIG_TTY=y
139CONFIG_UNAME=y
140CONFIG_UNIQ=y
141CONFIG_USLEEP=y
142# CONFIG_UUDECODE is not set
143# CONFIG_UUENCODE is not set
144# CONFIG_WATCH is not set
145CONFIG_WC=y
146# CONFIG_WHO is not set
147CONFIG_WHOAMI=y
148CONFIG_YES=y
149
150#
151# Common options for cp and mv
152#
153CONFIG_FEATURE_PRESERVE_HARDLINKS=y
154
155#
156# Common options for ls and more
157#
158CONFIG_FEATURE_AUTOWIDTH=y
159
160#
161# Common options for df, du, ls
162#
163CONFIG_FEATURE_HUMAN_READABLE=y
164
165#
166# Console Utilities
167#
168CONFIG_CHVT=y
169CONFIG_CLEAR=y
170CONFIG_DEALLOCVT=y
171# CONFIG_DUMPKMAP is not set
172# CONFIG_LOADFONT is not set
173# CONFIG_LOADKMAP is not set
174CONFIG_OPENVT=y
175CONFIG_RESET=y
176# CONFIG_SETKEYCODES is not set
177
178#
179# Debian Utilities
180#
181CONFIG_MKTEMP=y
182# CONFIG_PIPE_PROGRESS is not set
183CONFIG_READLINK=y
184# CONFIG_RUN_PARTS is not set
185# CONFIG_START_STOP_DAEMON is not set
186CONFIG_WHICH=y
187
188#
189# Editors
190#
191# CONFIG_AWK is not set
192# CONFIG_PATCH is not set
193CONFIG_SED=y
194CONFIG_VI=y
195CONFIG_FEATURE_VI_COLON=y
196CONFIG_FEATURE_VI_YANKMARK=y
197CONFIG_FEATURE_VI_SEARCH=y
198CONFIG_FEATURE_VI_USE_SIGNALS=y
199CONFIG_FEATURE_VI_DOT_CMD=y
200CONFIG_FEATURE_VI_READONLY=y
201CONFIG_FEATURE_VI_SETOPTS=y
202CONFIG_FEATURE_VI_SET=y
203CONFIG_FEATURE_VI_WIN_RESIZE=y
204CONFIG_FEATURE_VI_OPTIMIZE_CURSOR=y
205
206#
207# Finding Utilities
208#
209CONFIG_FIND=y
210CONFIG_FEATURE_FIND_MTIME=y
211CONFIG_FEATURE_FIND_PERM=y
212CONFIG_FEATURE_FIND_TYPE=y
213CONFIG_FEATURE_FIND_XDEV=y
214# CONFIG_FEATURE_FIND_NEWER is not set
215# CONFIG_FEATURE_FIND_INUM is not set
216CONFIG_GREP=y
217CONFIG_FEATURE_GREP_EGREP_ALIAS=y
218CONFIG_FEATURE_GREP_FGREP_ALIAS=y
219CONFIG_FEATURE_GREP_CONTEXT=y
220CONFIG_XARGS=y
221# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set
222CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
223CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
224CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
225
226#
227# Init Utilities
228#
229CONFIG_INIT=y
230CONFIG_FEATURE_USE_INITTAB=y
231CONFIG_FEATURE_INITRD=y
232# CONFIG_FEATURE_INIT_COREDUMPS is not set
233CONFIG_FEATURE_EXTRA_QUIET=y
234CONFIG_HALT=y
235CONFIG_POWEROFF=y
236CONFIG_REBOOT=y
237# CONFIG_MESG is not set
238
239#
240# Login/Password Management Utilities
241#
242# CONFIG_USE_BB_PWD_GRP is not set
243# CONFIG_ADDGROUP is not set
244# CONFIG_DELGROUP is not set
245# CONFIG_ADDUSER is not set
246# CONFIG_DELUSER is not set
247# CONFIG_GETTY is not set
248# CONFIG_LOGIN is not set
249# CONFIG_PASSWD is not set
250# CONFIG_SU is not set
251# CONFIG_SULOGIN is not set
252# CONFIG_VLOCK is not set
253
254#
255# Miscellaneous Utilities
256#
257# CONFIG_ADJTIMEX is not set
258# CONFIG_CROND is not set
259# CONFIG_CRONTAB is not set
260# CONFIG_DC is not set
261# CONFIG_DEVFSD is not set
262# CONFIG_LAST is not set
263# CONFIG_HDPARM is not set
264# CONFIG_MAKEDEVS is not set
265# CONFIG_MT is not set
266# CONFIG_RX is not set
267CONFIG_STRINGS=y
268CONFIG_TIME=y
269# CONFIG_WATCHDOG is not set
270
271#
272# Linux Module Utilities
273#
274# CONFIG_INSMOD is not set
275# CONFIG_LSMOD is not set
276# CONFIG_MODPROBE is not set
277# CONFIG_RMMOD is not set
278
279#
280# Networking Utilities
281#
282# CONFIG_FEATURE_IPV6 is not set
283# CONFIG_ARPING is not set
284# CONFIG_FTPGET is not set
285# CONFIG_FTPPUT is not set
286CONFIG_HOSTNAME=y
287# CONFIG_HTTPD is not set
288CONFIG_IFCONFIG=y
289CONFIG_FEATURE_IFCONFIG_STATUS=y
290# CONFIG_FEATURE_IFCONFIG_SLIP is not set
291# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
292# CONFIG_FEATURE_IFCONFIG_HW is not set
293# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
294# CONFIG_IFUPDOWN is not set
295# CONFIG_INETD is not set
296# CONFIG_IP is not set
297# CONFIG_IPCALC is not set
298# CONFIG_IPADDR is not set
299# CONFIG_IPLINK is not set
300# CONFIG_IPROUTE is not set
301# CONFIG_IPTUNNEL is not set
302# CONFIG_NAMEIF is not set
303# CONFIG_NC is not set
304# CONFIG_NETSTAT is not set
305# CONFIG_NSLOOKUP is not set
306CONFIG_PING=y
307CONFIG_FEATURE_FANCY_PING=y
308CONFIG_ROUTE=y
309# CONFIG_TELNET is not set
310# CONFIG_TELNETD is not set
311# CONFIG_TFTP is not set
312# CONFIG_TRACEROUTE is not set
313# CONFIG_VCONFIG is not set
314CONFIG_WGET=y
315CONFIG_FEATURE_WGET_STATUSBAR=y
316CONFIG_FEATURE_WGET_AUTHENTICATION=y
317# CONFIG_FEATURE_WGET_IP6_LITERAL is not set
318
319#
320# udhcp Server/Client
321#
322# CONFIG_UDHCPD is not set
323# CONFIG_UDHCPC is not set
324
325#
326# Process Utilities
327#
328CONFIG_FREE=y
329CONFIG_KILL=y
330CONFIG_KILLALL=y
331CONFIG_PIDOF=y
332CONFIG_PS=y
333# CONFIG_RENICE is not set
334# CONFIG_TOP is not set
335CONFIG_UPTIME=y
336# CONFIG_SYSCTL is not set
337
338#
339# Another Bourne-like Shell
340#
341CONFIG_FEATURE_SH_IS_ASH=y
342# CONFIG_FEATURE_SH_IS_HUSH is not set
343# CONFIG_FEATURE_SH_IS_LASH is not set
344# CONFIG_FEATURE_SH_IS_MSH is not set
345# CONFIG_FEATURE_SH_IS_NONE is not set
346CONFIG_ASH=y
347
348#
349# Ash Shell Options
350#
351CONFIG_ASH_JOB_CONTROL=y
352CONFIG_ASH_ALIAS=y
353CONFIG_ASH_MATH_SUPPORT=y
354CONFIG_ASH_MATH_SUPPORT_64=y
355# CONFIG_ASH_GETOPTS is not set
356# CONFIG_ASH_CMDCMD is not set
357# CONFIG_ASH_MAIL is not set
358CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
359# CONFIG_ASH_RANDOM_SUPPORT is not set
360# CONFIG_HUSH is not set
361# CONFIG_LASH is not set
362# CONFIG_MSH is not set
363
364#
365# Bourne Shell Options
366#
367# CONFIG_FEATURE_SH_EXTRA_QUIET is not set
368# CONFIG_FEATURE_SH_STANDALONE_SHELL is not set
369CONFIG_FEATURE_COMMAND_EDITING=y
370CONFIG_FEATURE_COMMAND_HISTORY=15
371CONFIG_FEATURE_COMMAND_SAVEHISTORY=y
372CONFIG_FEATURE_COMMAND_TAB_COMPLETION=y
373# CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION is not set
374CONFIG_FEATURE_SH_FANCY_PROMPT=y
375
376#
377# System Logging Utilities
378#
379CONFIG_SYSLOGD=y
380CONFIG_FEATURE_ROTATE_LOGFILE=y
381# CONFIG_FEATURE_REMOTE_LOG is not set
382# CONFIG_FEATURE_IPC_SYSLOG is not set
383CONFIG_KLOGD=y
384CONFIG_LOGGER=y
385
386#
387# Linux System Utilities
388#
389CONFIG_DMESG=y
390# CONFIG_FBSET is not set
391# CONFIG_FDFLUSH is not set
392# CONFIG_FDFORMAT is not set
393# CONFIG_FDISK is not set
394# CONFIG_FREERAMDISK is not set
395# CONFIG_FSCK_MINIX is not set
396# CONFIG_MKFS_MINIX is not set
397# CONFIG_GETOPT is not set
398CONFIG_HEXDUMP=y
399# CONFIG_HWCLOCK is not set
400# CONFIG_LOSETUP is not set
401# CONFIG_MKSWAP is not set
402CONFIG_MORE=y
403CONFIG_FEATURE_USE_TERMIOS=y
404CONFIG_PIVOT_ROOT=y
405# CONFIG_RDATE is not set
406CONFIG_SWAPONOFF=y
407CONFIG_MOUNT=y
408# CONFIG_NFSMOUNT is not set
409CONFIG_UMOUNT=y
410# CONFIG_FEATURE_MOUNT_FORCE is not set
411
412#
413# Common options for mount/umount
414#
415CONFIG_FEATURE_MOUNT_LOOP=y
416# CONFIG_FEATURE_MTAB_SUPPORT is not set
417
418#
419# Debugging Options
420#
421# 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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "System Logging Utilities"
7
8config CONFIG_SYSLOGD
9 bool "syslogd"
10 default n
11 help
12 The syslogd utility is used to record logs of all the
13 significant events that occur on a system. Every
14 message that is logged records the date and time of the
15 event, and will generally also record the name of the
16 application that generated the message. When used in
17 conjunction with klogd, messages from the Linux kernel
18 can also be recorded. This is terribly useful,
19 especially for finding what happened when something goes
20 wrong. And something almost always will go wrong if
21 you wait long enough....
22
23config CONFIG_FEATURE_ROTATE_LOGFILE
24 bool " Rotate message files"
25 default n
26 depends on CONFIG_SYSLOGD
27 help
28 This enables syslogd to rotate the message files
29 on his own. No need to use an external rotatescript.
30
31config CONFIG_FEATURE_REMOTE_LOG
32 bool " Remote Log support"
33 default n
34 depends on CONFIG_SYSLOGD
35 help
36 When you enable this feature, the syslogd utility can
37 be used to send system log messages to another system
38 connected via a network. This allows the remote
39 machine to log all the system messages, which can be
40 terribly useful for reducing the number of serial
41 cables you use. It can also be a very good security
42 measure to prevent system logs from being tampered with
43 by an intruder.
44
45config CONFIG_FEATURE_IPC_SYSLOG
46 bool " Circular Buffer support"
47 default n
48 depends on CONFIG_SYSLOGD
49 help
50 When you enable this feature, the syslogd utility will
51 use a circular buffer to record system log messages.
52 When the buffer is filled it will continue to overwrite
53 the oldest messages. This can be very useful for
54 systems with little or no permanent storage, since
55 otherwise system logs can eventually fill up your
56 entire filesystem, which may cause your system to
57 break badly.
58
59config CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
60 int " Circular buffer size in Kbytes (minimum 4KB)"
61 default 16
62 depends on CONFIG_FEATURE_IPC_SYSLOG
63 help
64 This option sets the size of the circular buffer
65 used to record system log messages.
66
67config CONFIG_LOGREAD
68 bool " logread"
69 default y
70 depends on CONFIG_FEATURE_IPC_SYSLOG
71 help
72 If you enabled Circular Buffer support, you almost
73 certainly want to enable this feature as well. This
74 utility will allow you to read the messages that are
75 stored in the syslogd circular buffer.
76
77config CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING
78 bool " logread double buffering"
79 default n
80 depends on CONFIG_LOGREAD
81 help
82 'logread' ouput to slow serial terminals can have
83 side effects on syslog because of the semaphore.
84 This option make logread to double buffer copy
85 from circular buffer, minimizing semaphore
86 contention at some minor memory expense.
87
88config CONFIG_KLOGD
89 bool "klogd"
90 default n
91 depends on CONFIG_SYSLOGD
92 help
93 klogd is a utility which intercepts and logs all
94 messages from the Linux kernel and sends the messages
95 out to the 'syslogd' utility so they can be logged. If
96 you wish to record the messages produced by the kernel,
97 you should enable this option.
98
99config CONFIG_LOGGER
100 bool "logger"
101 default n
102 help
103 The logger utility allows you to send arbitrary text
104 messages to the system log (i.e. the 'syslogd' utility) so
105 they can be logged. This is generally used to help locate
106 problems that occur within programs and scripts.
107
108endmenu
109
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_buildddir=..
22srcdir=$(top_srcdir)/sysklogd
23SYSKLOGD_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20SYSKLOGD_AR:=sysklogd.a
21ifndef $(SYSKLOGD_DIR)
22SYSKLOGD_DIR:=$(top_builddir)/sysklogd/
23endif
24srcdir=$(top_srcdir)/sysklogd
25
26SYSKLOGD-:=
27SYSKLOGD-$(CONFIG_KLOGD) += klogd.o
28SYSKLOGD-$(CONFIG_LOGGER) += logger.o
29SYSKLOGD-$(CONFIG_LOGREAD) += logread.o
30SYSKLOGD-$(CONFIG_SYSLOGD) += syslogd.o
31
32libraries-y+=$(SYSKLOGD_DIR)$(SYSKLOGD_AR)
33
34$(SYSKLOGD_DIR)$(SYSKLOGD_AR): $(patsubst %,$(SYSKLOGD_DIR)%, $(SYSKLOGD-y))
35 $(AR) -ro $@ $(patsubst %,$(SYSKLOGD_DIR)%, $(SYSKLOGD-y))
36
37$(SYSKLOGD_DIR)%.o: $(srcdir)/%.c
38 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
39
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini klogd implementation for busybox
4 *
5 * Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>.
6 * Changes: Made this a standalone busybox module which uses standalone
7 * syslog() client interface.
8 *
9 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
10 *
11 * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
12 *
13 * "circular buffer" Copyright (C) 2000 by Gennady Feldman <gfeldman@gena01.com>
14 *
15 * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 *
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <signal.h> /* for our signal() handlers */
36#include <string.h> /* strncpy() */
37#include <errno.h> /* errno and friends */
38#include <unistd.h>
39#include <ctype.h>
40#include <sys/syslog.h>
41#include <sys/klog.h>
42
43#include "busybox.h"
44
45static void klogd_signal(int sig)
46{
47 klogctl(7, NULL, 0);
48 klogctl(0, 0, 0);
49 /* logMessage(0, "Kernel log daemon exiting."); */
50 syslog(LOG_NOTICE, "Kernel log daemon exiting.");
51 exit(EXIT_SUCCESS);
52}
53
54static void doKlogd(const int console_log_level) __attribute__ ((noreturn));
55static void doKlogd(const int console_log_level)
56{
57 int priority = LOG_INFO;
58 char log_buffer[4096];
59 int i, n, lastc;
60 char *start;
61
62 openlog("kernel", 0, LOG_KERN);
63
64 /* Set up sig handlers */
65 signal(SIGINT, klogd_signal);
66 signal(SIGKILL, klogd_signal);
67 signal(SIGTERM, klogd_signal);
68 signal(SIGHUP, SIG_IGN);
69
70 /* "Open the log. Currently a NOP." */
71 klogctl(1, NULL, 0);
72
73 /* Set level of kernel console messaging.. */
74 if (console_log_level != -1)
75 klogctl(8, NULL, console_log_level);
76
77 syslog(LOG_NOTICE, "klogd started: " BB_BANNER);
78
79 while (1) {
80 /* Use kernel syscalls */
81 memset(log_buffer, '\0', sizeof(log_buffer));
82 n = klogctl(2, log_buffer, sizeof(log_buffer));
83 if (n < 0) {
84 if (errno == EINTR)
85 continue;
86 syslog(LOG_ERR, "klogd: Error return from sys_sycall: %d - %m.\n", errno);
87 exit(EXIT_FAILURE);
88 }
89
90 /* klogctl buffer parsing modelled after code in dmesg.c */
91 start = &log_buffer[0];
92 lastc = '\0';
93 for (i = 0; i < n; i++) {
94 if (lastc == '\0' && log_buffer[i] == '<') {
95 priority = 0;
96 i++;
97 while (isdigit(log_buffer[i])) {
98 priority = priority * 10 + (log_buffer[i] - '0');
99 i++;
100 }
101 if (log_buffer[i] == '>')
102 i++;
103 start = &log_buffer[i];
104 }
105 if (log_buffer[i] == '\n') {
106 log_buffer[i] = '\0'; /* zero terminate this message */
107 syslog(priority, "%s", start);
108 start = &log_buffer[i + 1];
109 priority = LOG_INFO;
110 }
111 lastc = log_buffer[i];
112 }
113 }
114}
115
116extern int klogd_main(int argc, char **argv)
117{
118 /* no options, no getopt */
119 int opt;
120 int doFork = TRUE;
121 unsigned char console_log_level = -1;
122
123 /* do normal option parsing */
124 while ((opt = getopt(argc, argv, "c:n")) > 0) {
125 switch (opt) {
126 case 'c':
127 if ((optarg == NULL) || (optarg[1] != '\0')) {
128 bb_show_usage();
129 }
130 /* Valid levels are between 1 and 8 */
131 console_log_level = *optarg - '1';
132 if (console_log_level > 7) {
133 bb_show_usage();
134 }
135 console_log_level++;
136
137 break;
138 case 'n':
139 doFork = FALSE;
140 break;
141 default:
142 bb_show_usage();
143 }
144 }
145
146 if (doFork) {
147#if defined(__uClinux__)
148 vfork_daemon_rexec(0, 1, argc, argv, "-n");
149#else /* __uClinux__ */
150 if (daemon(0, 1) < 0)
151 bb_perror_msg_and_die("daemon");
152#endif /* __uClinux__ */
153 }
154 doKlogd(console_log_level);
155
156 return EXIT_SUCCESS;
157}
158
159/*
160Local Variables
161c-file-style: "linux"
162c-basic-offset: 4
163tab-width: 4
164End:
165*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini logger implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <stdio.h>
24#include <unistd.h>
25#include <sys/types.h>
26#include <fcntl.h>
27#include <ctype.h>
28#include <string.h>
29#include <stdlib.h>
30
31#include "busybox.h"
32#if !defined CONFIG_SYSLOGD
33
34#define SYSLOG_NAMES
35#include <sys/syslog.h>
36
37#else
38#include <sys/syslog.h>
39# ifndef __dietlibc__
40 /* We have to do this since the header file defines static
41 * structures. Argh.... bad libc, bad, bad...
42 */
43 typedef struct _code {
44 char *c_name;
45 int c_val;
46 } CODE;
47 extern CODE prioritynames[];
48 extern CODE facilitynames[];
49# endif
50#endif
51
52/* Decode a symbolic name to a numeric value
53 * this function is based on code
54 * Copyright (c) 1983, 1993
55 * The Regents of the University of California. All rights reserved.
56 *
57 * Original copyright notice is retained at the end of this file.
58 */
59static int decode(char *name, CODE * codetab)
60{
61 CODE *c;
62
63 if (isdigit(*name))
64 return (atoi(name));
65 for (c = codetab; c->c_name; c++) {
66 if (!strcasecmp(name, c->c_name)) {
67 return (c->c_val);
68 }
69 }
70
71 return (-1);
72}
73
74/* Decode a symbolic name to a numeric value
75 * this function is based on code
76 * Copyright (c) 1983, 1993
77 * The Regents of the University of California. All rights reserved.
78 *
79 * Original copyright notice is retained at the end of this file.
80 */
81static int pencode(char *s)
82{
83 char *save;
84 int lev, fac = LOG_USER;
85
86 for (save = s; *s && *s != '.'; ++s);
87 if (*s) {
88 *s = '\0';
89 fac = decode(save, facilitynames);
90 if (fac < 0)
91 bb_error_msg_and_die("unknown facility name: %s", save);
92 *s++ = '.';
93 } else {
94 s = save;
95 }
96 lev = decode(s, prioritynames);
97 if (lev < 0)
98 bb_error_msg_and_die("unknown priority name: %s", save);
99 return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK));
100}
101
102
103extern int logger_main(int argc, char **argv)
104{
105 int pri = LOG_USER | LOG_NOTICE;
106 int option = 0;
107 int c, i, opt;
108 char buf[1024], name[128];
109
110 /* Fill out the name string early (may be overwritten later) */
111 my_getpwuid(name, geteuid(), sizeof(name));
112
113 /* Parse any options */
114 while ((opt = getopt(argc, argv, "p:st:")) > 0) {
115 switch (opt) {
116 case 's':
117 option |= LOG_PERROR;
118 break;
119 case 'p':
120 pri = pencode(optarg);
121 break;
122 case 't':
123 safe_strncpy(name, optarg, sizeof(name));
124 break;
125 default:
126 bb_show_usage();
127 }
128 }
129
130 openlog(name, option, (pri | LOG_FACMASK));
131 if (optind == argc) {
132 do {
133 /* read from stdin */
134 i = 0;
135 while ((c = getc(stdin)) != EOF && c != '\n' &&
136 i < (sizeof(buf)-1)) {
137 buf[i++] = c;
138 }
139 if (i > 0) {
140 buf[i++] = '\0';
141 syslog(pri, "%s", buf);
142 }
143 } while (c != EOF);
144 } else {
145 char *message = NULL;
146 int len = argc - optind; /* for the space between the args
147 and '\0' */
148 opt = len;
149 argv += optind;
150 for (i = 0; i < opt; i++) {
151 len += strlen(*argv);
152 message = xrealloc(message, len);
153 if(!i)
154 message[0] = 0;
155 else
156 strcat(message, " ");
157 strcat(message, *argv);
158 argv++;
159 }
160 syslog(pri, "%s", message);
161 }
162
163 closelog();
164 return EXIT_SUCCESS;
165}
166
167
168/*-
169 * Copyright (c) 1983, 1993
170 * The Regents of the University of California. All rights reserved.
171 *
172 * This is the original license statement for the decode and pencode functions.
173 *
174 * Redistribution and use in source and binary forms, with or without
175 * modification, are permitted provided that the following conditions
176 * are met:
177 * 1. Redistributions of source code must retain the above copyright
178 * notice, this list of conditions and the following disclaimer.
179 * 2. Redistributions in binary form must reproduce the above copyright
180 * notice, this list of conditions and the following disclaimer in the
181 * documentation and/or other materials provided with the distribution.
182 *
183 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
184 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
185 *
186 * 4. Neither the name of the University nor the names of its contributors
187 * may be used to endorse or promote products derived from this software
188 * without specific prior written permission.
189 *
190 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
191 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
192 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
193 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
194 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
195 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
196 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
197 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
198 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
199 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
200 * SUCH DAMAGE.
201 */
202
203
204
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * circular buffer syslog implementation for busybox
4 *
5 * Copyright (C) 2000 by Gennady Feldman <gfeldman@gena01.com>
6 *
7 * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
22 * 02111-1307 USA
23 *
24 */
25
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/ipc.h>
31#include <sys/types.h>
32#include <sys/sem.h>
33#include <sys/shm.h>
34#include <signal.h>
35#include <setjmp.h>
36#include <unistd.h>
37#include "busybox.h"
38
39static const long KEY_ID = 0x414e4547; /*"GENA"*/
40
41static struct shbuf_ds {
42 int size; // size of data written
43 int head; // start of message list
44 int tail; // end of message list
45 char data[1]; // data/messages
46} *buf = NULL; // shared memory pointer
47
48
49// Semaphore operation structures
50static struct sembuf SMrup[1] = {{0, -1, IPC_NOWAIT | SEM_UNDO}}; // set SMrup
51static struct sembuf SMrdn[2] = {{1, 0}, {0, +1, SEM_UNDO}}; // set SMrdn
52
53static int log_shmid = -1; // ipc shared memory id
54static int log_semid = -1; // ipc semaphore id
55static jmp_buf jmp_env;
56
57static void error_exit(const char *str);
58static void interrupted(int sig);
59
60/*
61 * sem_up - up()'s a semaphore.
62 */
63static inline void sem_up(int semid)
64{
65 if ( semop(semid, SMrup, 1) == -1 )
66 error_exit("semop[SMrup]");
67}
68
69/*
70 * sem_down - down()'s a semaphore
71 */
72static inline void sem_down(int semid)
73{
74 if ( semop(semid, SMrdn, 2) == -1 )
75 error_exit("semop[SMrdn]");
76}
77
78extern int logread_main(int argc, char **argv)
79{
80 int i;
81 int follow=0;
82
83 if (argc == 2 && strcmp(argv[1],"-f")==0) {
84 follow = 1;
85 } else {
86 /* no options, no getopt */
87 if (argc > 1)
88 bb_show_usage();
89 }
90
91 // handle intrrupt signal
92 if (setjmp(jmp_env)) goto output_end;
93
94 // attempt to redefine ^C signal
95 signal(SIGINT, interrupted);
96
97 if ( (log_shmid = shmget(KEY_ID, 0, 0)) == -1)
98 error_exit("Can't find circular buffer");
99
100 // Attach shared memory to our char*
101 if ( (buf = shmat(log_shmid, NULL, SHM_RDONLY)) == NULL)
102 error_exit("Can't get access to circular buffer from syslogd");
103
104 if ( (log_semid = semget(KEY_ID, 0, 0)) == -1)
105 error_exit("Can't get access to semaphone(s) for circular buffer from syslogd");
106
107 // Suppose atomic memory move
108 i = follow ? buf->tail : buf->head;
109
110 do {
111#ifdef CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING
112 char *buf_data;
113 int log_len,j;
114#endif
115
116 sem_down(log_semid);
117
118 //printf("head: %i tail: %i size: %i\n",buf->head,buf->tail,buf->size);
119 if (buf->head == buf->tail || i==buf->tail) {
120 if (follow) {
121 sem_up(log_semid);
122 sleep(1); /* TODO: replace me with a sleep_on */
123 continue;
124 } else {
125 printf("<empty syslog>\n");
126 }
127 }
128
129 // Read Memory
130#ifdef CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING
131 log_len = buf->tail - i;
132 if (log_len < 0)
133 log_len += buf->size;
134 buf_data = (char *)malloc(log_len);
135 if (!buf_data)
136 error_exit("malloc failed");
137
138 if (buf->tail < i) {
139 memcpy(buf_data, buf->data+i, buf->size-i);
140 memcpy(buf_data+buf->size-i, buf->data, buf->tail);
141 } else {
142 memcpy(buf_data, buf->data+i, buf->tail-i);
143 }
144 i = buf->tail;
145
146#else
147 while ( i != buf->tail) {
148 printf("%s", buf->data+i);
149 i+= strlen(buf->data+i) + 1;
150 if (i >= buf->size )
151 i=0;
152 }
153#endif
154 // release the lock on the log chain
155 sem_up(log_semid);
156
157#ifdef CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING
158 for (j=0; j < log_len; j+=strlen(buf_data+j)+1) {
159 printf("%s", buf_data+j);
160 if (follow)
161 fflush(stdout);
162 }
163 free(buf_data);
164#endif
165 } while (follow);
166
167output_end:
168 if (log_shmid != -1)
169 shmdt(buf);
170
171 return EXIT_SUCCESS;
172}
173
174static void interrupted(int sig){
175 signal(SIGINT, SIG_IGN);
176 longjmp(jmp_env, 1);
177}
178
179static void error_exit(const char *str){
180 perror(str);
181 //release all acquired resources
182 if (log_shmid != -1)
183 shmdt(buf);
184
185 exit(1);
186}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini syslogd implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
8 *
9 * "circular buffer" Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>
10 *
11 * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 *
27 */
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <ctype.h>
32#include <errno.h>
33#include <fcntl.h>
34#include <getopt.h>
35#include <netdb.h>
36#include <paths.h>
37#include <signal.h>
38#include <stdarg.h>
39#include <stdbool.h>
40#include <time.h>
41#include <string.h>
42#include <unistd.h>
43#include <sys/socket.h>
44#include <sys/types.h>
45#include <sys/un.h>
46#include <sys/param.h>
47
48#include "busybox.h"
49
50/* SYSLOG_NAMES defined to pull some extra junk from syslog.h */
51#define SYSLOG_NAMES
52#include <sys/syslog.h>
53#include <sys/uio.h>
54
55/* Path for the file where all log messages are written */
56#define __LOG_FILE "/var/log/messages"
57
58/* Path to the unix socket */
59static char lfile[MAXPATHLEN];
60
61static const char *logFilePath = __LOG_FILE;
62
63#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
64/* max size of message file before being rotated */
65static int logFileSize = 200 * 1024;
66
67/* number of rotated message files */
68static int logFileRotate = 1;
69#endif
70
71/* interval between marks in seconds */
72static int MarkInterval = 20 * 60;
73
74/* localhost's name */
75static char LocalHostName[64];
76
77#ifdef CONFIG_FEATURE_REMOTE_LOG
78#include <netinet/in.h>
79/* udp socket for logging to remote host */
80static int remotefd = -1;
81static struct sockaddr_in remoteaddr;
82
83/* where do we log? */
84static char *RemoteHost;
85
86/* what port to log to? */
87static int RemotePort = 514;
88
89/* To remote log or not to remote log, that is the question. */
90static int doRemoteLog = FALSE;
91static int local_logging = FALSE;
92#endif
93
94/* Make loging output smaller. */
95static bool small = false;
96
97
98#define MAXLINE 1024 /* maximum line length */
99
100
101/* circular buffer variables/structures */
102#ifdef CONFIG_FEATURE_IPC_SYSLOG
103
104#if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
105#error Sorry, you must set the syslogd buffer size to at least 4KB.
106#error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
107#endif
108
109#include <sys/ipc.h>
110#include <sys/sem.h>
111#include <sys/shm.h>
112
113/* our shared key */
114static const long KEY_ID = 0x414e4547; /*"GENA" */
115
116// Semaphore operation structures
117static struct shbuf_ds {
118 int size; // size of data written
119 int head; // start of message list
120 int tail; // end of message list
121 char data[1]; // data/messages
122} *buf = NULL; // shared memory pointer
123
124static struct sembuf SMwup[1] = { {1, -1, IPC_NOWAIT} }; // set SMwup
125static struct sembuf SMwdn[3] = { {0, 0}, {1, 0}, {1, +1} }; // set SMwdn
126
127static int shmid = -1; // ipc shared memory id
128static int s_semid = -1; // ipc semaphore id
129static int shm_size = ((CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE)*1024); // default shm size
130static int circular_logging = FALSE;
131
132/*
133 * sem_up - up()'s a semaphore.
134 */
135static inline void sem_up(int semid)
136{
137 if (semop(semid, SMwup, 1) == -1) {
138 bb_perror_msg_and_die("semop[SMwup]");
139 }
140}
141
142/*
143 * sem_down - down()'s a semaphore
144 */
145static inline void sem_down(int semid)
146{
147 if (semop(semid, SMwdn, 3) == -1) {
148 bb_perror_msg_and_die("semop[SMwdn]");
149 }
150}
151
152
153void ipcsyslog_cleanup(void)
154{
155 printf("Exiting Syslogd!\n");
156 if (shmid != -1) {
157 shmdt(buf);
158 }
159
160 if (shmid != -1) {
161 shmctl(shmid, IPC_RMID, NULL);
162 }
163 if (s_semid != -1) {
164 semctl(s_semid, 0, IPC_RMID, 0);
165 }
166}
167
168void ipcsyslog_init(void)
169{
170 if (buf == NULL) {
171 if ((shmid = shmget(KEY_ID, shm_size, IPC_CREAT | 1023)) == -1) {
172 bb_perror_msg_and_die("shmget");
173 }
174
175 if ((buf = shmat(shmid, NULL, 0)) == NULL) {
176 bb_perror_msg_and_die("shmat");
177 }
178
179 buf->size = shm_size - sizeof(*buf);
180 buf->head = buf->tail = 0;
181
182 // we'll trust the OS to set initial semval to 0 (let's hope)
183 if ((s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023)) == -1) {
184 if (errno == EEXIST) {
185 if ((s_semid = semget(KEY_ID, 2, 0)) == -1) {
186 bb_perror_msg_and_die("semget");
187 }
188 } else {
189 bb_perror_msg_and_die("semget");
190 }
191 }
192 } else {
193 printf("Buffer already allocated just grab the semaphore?");
194 }
195}
196
197/* write message to buffer */
198void circ_message(const char *msg)
199{
200 int l = strlen(msg) + 1; /* count the whole message w/ '\0' included */
201
202 sem_down(s_semid);
203
204 /*
205 * Circular Buffer Algorithm:
206 * --------------------------
207 *
208 * Start-off w/ empty buffer of specific size SHM_SIZ
209 * Start filling it up w/ messages. I use '\0' as separator to break up messages.
210 * This is also very handy since we can do printf on message.
211 *
212 * Once the buffer is full we need to get rid of the first message in buffer and
213 * insert the new message. (Note: if the message being added is >1 message then
214 * we will need to "remove" >1 old message from the buffer). The way this is done
215 * is the following:
216 * When we reach the end of the buffer we set a mark and start from the beginning.
217 * Now what about the beginning and end of the buffer? Well we have the "head"
218 * index/pointer which is the starting point for the messages and we have "tail"
219 * index/pointer which is the ending point for the messages. When we "display" the
220 * messages we start from the beginning and continue until we reach "tail". If we
221 * reach end of buffer, then we just start from the beginning (offset 0). "head" and
222 * "tail" are actually offsets from the beginning of the buffer.
223 *
224 * Note: This algorithm uses Linux IPC mechanism w/ shared memory and semaphores to provide
225 * a threasafe way of handling shared memory operations.
226 */
227 if ((buf->tail + l) < buf->size) {
228 /* before we append the message we need to check the HEAD so that we won't
229 overwrite any of the message that we still need and adjust HEAD to point
230 to the next message! */
231 if (buf->tail < buf->head) {
232 if ((buf->tail + l) >= buf->head) {
233 /* we need to move the HEAD to point to the next message
234 * Theoretically we have enough room to add the whole message to the
235 * buffer, because of the first outer IF statement, so we don't have
236 * to worry about overflows here!
237 */
238 int k = buf->tail + l - buf->head; /* we need to know how many bytes
239 we are overwriting to make
240 enough room */
241 char *c =
242 memchr(buf->data + buf->head + k, '\0',
243 buf->size - (buf->head + k));
244 if (c != NULL) { /* do a sanity check just in case! */
245 buf->head = c - buf->data + 1; /* we need to convert pointer to
246 offset + skip the '\0' since
247 we need to point to the beginning
248 of the next message */
249 /* Note: HEAD is only used to "retrieve" messages, it's not used
250 when writing messages into our buffer */
251 } else { /* show an error message to know we messed up? */
252 printf("Weird! Can't find the terminator token??? \n");
253 buf->head = 0;
254 }
255 }
256 }
257
258 /* in other cases no overflows have been done yet, so we don't care! */
259 /* we should be ok to append the message now */
260 strncpy(buf->data + buf->tail, msg, l); /* append our message */
261 buf->tail += l; /* count full message w/ '\0' terminating char */
262 } else {
263 /* we need to break up the message and "circle" it around */
264 char *c;
265 int k = buf->tail + l - buf->size; /* count # of bytes we don't fit */
266
267 /* We need to move HEAD! This is always the case since we are going
268 * to "circle" the message.
269 */
270 c = memchr(buf->data + k, '\0', buf->size - k);
271
272 if (c != NULL) { /* if we don't have '\0'??? weird!!! */
273 /* move head pointer */
274 buf->head = c - buf->data + 1;
275
276 /* now write the first part of the message */
277 strncpy(buf->data + buf->tail, msg, l - k - 1);
278
279 /* ALWAYS terminate end of buffer w/ '\0' */
280 buf->data[buf->size - 1] = '\0';
281
282 /* now write out the rest of the string to the beginning of the buffer */
283 strcpy(buf->data, &msg[l - k - 1]);
284
285 /* we need to place the TAIL at the end of the message */
286 buf->tail = k + 1;
287 } else {
288 printf
289 ("Weird! Can't find the terminator token from the beginning??? \n");
290 buf->head = buf->tail = 0; /* reset buffer, since it's probably corrupted */
291 }
292
293 }
294 sem_up(s_semid);
295}
296#endif /* CONFIG_FEATURE_IPC_SYSLOG */
297
298/* Note: There is also a function called "message()" in init.c */
299/* Print a message to the log file. */
300static void message(char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
301static void message(char *fmt, ...)
302{
303 int fd;
304 struct flock fl;
305 va_list arguments;
306
307 fl.l_whence = SEEK_SET;
308 fl.l_start = 0;
309 fl.l_len = 1;
310
311#ifdef CONFIG_FEATURE_IPC_SYSLOG
312 if ((circular_logging == TRUE) && (buf != NULL)) {
313 char b[1024];
314
315 va_start(arguments, fmt);
316 vsnprintf(b, sizeof(b) - 1, fmt, arguments);
317 va_end(arguments);
318 circ_message(b);
319
320 } else
321#endif
322 if ((fd =
323 device_open(logFilePath,
324 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
325 O_NONBLOCK)) >= 0) {
326 fl.l_type = F_WRLCK;
327 fcntl(fd, F_SETLKW, &fl);
328#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
329 if ( logFileSize > 0 ) {
330 struct stat statf;
331 int r = fstat(fd, &statf);
332 if( !r && (statf.st_mode & S_IFREG)
333 && (lseek(fd,0,SEEK_END) > logFileSize) ) {
334 if(logFileRotate > 0) {
335 int i;
336 char oldFile[(strlen(logFilePath)+3)], newFile[(strlen(logFilePath)+3)];
337 for(i=logFileRotate-1;i>0;i--) {
338 sprintf(oldFile, "%s.%d", logFilePath, i-1);
339 sprintf(newFile, "%s.%d", logFilePath, i);
340 rename(oldFile, newFile);
341 }
342 sprintf(newFile, "%s.%d", logFilePath, 0);
343 fl.l_type = F_UNLCK;
344 fcntl (fd, F_SETLKW, &fl);
345 close(fd);
346 rename(logFilePath, newFile);
347 fd = device_open (logFilePath,
348 O_WRONLY | O_CREAT | O_NOCTTY | O_APPEND |
349 O_NONBLOCK);
350 fl.l_type = F_WRLCK;
351 fcntl (fd, F_SETLKW, &fl);
352 } else {
353 ftruncate( fd, 0 );
354 }
355 }
356 }
357#endif
358 va_start(arguments, fmt);
359 vdprintf(fd, fmt, arguments);
360 va_end(arguments);
361 fl.l_type = F_UNLCK;
362 fcntl(fd, F_SETLKW, &fl);
363 close(fd);
364 } else {
365 /* Always send console messages to /dev/console so people will see them. */
366 if ((fd =
367 device_open(_PATH_CONSOLE,
368 O_WRONLY | O_NOCTTY | O_NONBLOCK)) >= 0) {
369 va_start(arguments, fmt);
370 vdprintf(fd, fmt, arguments);
371 va_end(arguments);
372 close(fd);
373 } else {
374 fprintf(stderr, "Bummer, can't print: ");
375 va_start(arguments, fmt);
376 vfprintf(stderr, fmt, arguments);
377 fflush(stderr);
378 va_end(arguments);
379 }
380 }
381}
382
383#ifdef CONFIG_FEATURE_REMOTE_LOG
384static void init_RemoteLog(void)
385{
386 memset(&remoteaddr, 0, sizeof(remoteaddr));
387 remotefd = socket(AF_INET, SOCK_DGRAM, 0);
388
389 if (remotefd < 0) {
390 bb_error_msg("cannot create socket");
391 }
392
393 remoteaddr.sin_family = AF_INET;
394 remoteaddr.sin_addr = *(struct in_addr *) *(xgethostbyname(RemoteHost))->h_addr_list;
395 remoteaddr.sin_port = htons(RemotePort);
396}
397#endif
398
399static void logMessage(int pri, char *msg)
400{
401 time_t now;
402 char *timestamp;
403 static char res[20] = "";
404#ifdef CONFIG_FEATURE_REMOTE_LOG
405 static char line[MAXLINE + 1];
406#endif
407 CODE *c_pri, *c_fac;
408
409 if (pri != 0) {
410 for (c_fac = facilitynames;
411 c_fac->c_name && !(c_fac->c_val == LOG_FAC(pri) << 3); c_fac++);
412 for (c_pri = prioritynames;
413 c_pri->c_name && !(c_pri->c_val == LOG_PRI(pri)); c_pri++);
414 if (c_fac->c_name == NULL || c_pri->c_name == NULL) {
415 snprintf(res, sizeof(res), "<%d>", pri);
416 } else {
417 snprintf(res, sizeof(res), "%s.%s", c_fac->c_name, c_pri->c_name);
418 }
419 }
420
421 if (strlen(msg) < 16 || msg[3] != ' ' || msg[6] != ' ' ||
422 msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') {
423 time(&now);
424 timestamp = ctime(&now) + 4;
425 timestamp[15] = '\0';
426 } else {
427 timestamp = msg;
428 timestamp[15] = '\0';
429 msg += 16;
430 }
431
432 /* todo: supress duplicates */
433
434#ifdef CONFIG_FEATURE_REMOTE_LOG
435 if (doRemoteLog == TRUE) {
436 /* trying connect the socket */
437 if (-1 == remotefd) {
438 init_RemoteLog();
439 }
440
441 /* if we have a valid socket, send the message */
442 if (-1 != remotefd) {
443 now = 1;
444 snprintf(line, sizeof(line), "<%d> %s", pri, msg);
445
446 retry:
447 /* send message to remote logger */
448 if(( -1 == sendto(remotefd, line, strlen(line), 0,
449 (struct sockaddr *) &remoteaddr,
450 sizeof(remoteaddr))) && (errno == EINTR)) {
451 /* sleep now seconds and retry (with now * 2) */
452 sleep(now);
453 now *= 2;
454 goto retry;
455 }
456 }
457 }
458
459 if (local_logging == TRUE)
460#endif
461 {
462 /* now spew out the message to wherever it is supposed to go */
463 if (small)
464 message("%s %s\n", timestamp, msg);
465 else
466 message("%s %s %s %s\n", timestamp, LocalHostName, res, msg);
467 }
468}
469
470static void quit_signal(int sig)
471{
472 logMessage(LOG_SYSLOG | LOG_INFO, "System log daemon exiting.");
473 unlink(lfile);
474#ifdef CONFIG_FEATURE_IPC_SYSLOG
475 ipcsyslog_cleanup();
476#endif
477
478 exit(TRUE);
479}
480
481static void domark(int sig)
482{
483 if (MarkInterval > 0) {
484 logMessage(LOG_SYSLOG | LOG_INFO, "-- MARK --");
485 alarm(MarkInterval);
486 }
487}
488
489/* This must be a #define, since when CONFIG_DEBUG and BUFFERS_GO_IN_BSS are
490 * enabled, we otherwise get a "storage size isn't constant error. */
491static int serveConnection(char *tmpbuf, int n_read)
492{
493 char *p = tmpbuf;
494
495 while (p < tmpbuf + n_read) {
496
497 int pri = (LOG_USER | LOG_NOTICE);
498 int num_lt = 0;
499 char line[MAXLINE + 1];
500 unsigned char c;
501 char *q = line;
502
503 while ((c = *p) && q < &line[sizeof(line) - 1]) {
504 if (c == '<' && num_lt == 0) {
505 /* Parse the magic priority number. */
506 num_lt++;
507 pri = 0;
508 while (isdigit(*(++p))) {
509 pri = 10 * pri + (*p - '0');
510 }
511 if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
512 pri = (LOG_USER | LOG_NOTICE);
513 }
514 } else if (c == '\n') {
515 *q++ = ' ';
516 } else if (iscntrl(c) && (c < 0177)) {
517 *q++ = '^';
518 *q++ = c ^ 0100;
519 } else {
520 *q++ = c;
521 }
522 p++;
523 }
524 *q = '\0';
525 p++;
526 /* Now log it */
527 logMessage(pri, line);
528 }
529 return n_read;
530}
531
532static void doSyslogd(void) __attribute__ ((noreturn));
533static void doSyslogd(void)
534{
535 struct sockaddr_un sunx;
536 socklen_t addrLength;
537
538 int sock_fd;
539 fd_set fds;
540
541 /* Set up signal handlers. */
542 signal(SIGINT, quit_signal);
543 signal(SIGTERM, quit_signal);
544 signal(SIGQUIT, quit_signal);
545 signal(SIGHUP, SIG_IGN);
546 signal(SIGCHLD, SIG_IGN);
547#ifdef SIGCLD
548 signal(SIGCLD, SIG_IGN);
549#endif
550 signal(SIGALRM, domark);
551 alarm(MarkInterval);
552
553 /* Create the syslog file so realpath() can work. */
554 if (realpath(_PATH_LOG, lfile) != NULL) {
555 unlink(lfile);
556 }
557
558 memset(&sunx, 0, sizeof(sunx));
559 sunx.sun_family = AF_UNIX;
560 strncpy(sunx.sun_path, lfile, sizeof(sunx.sun_path));
561 if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
562 bb_perror_msg_and_die("Couldn't get file descriptor for socket "
563 _PATH_LOG);
564 }
565
566 addrLength = sizeof(sunx.sun_family) + strlen(sunx.sun_path);
567 if (bind(sock_fd, (struct sockaddr *) &sunx, addrLength) < 0) {
568 bb_perror_msg_and_die("Could not connect to socket " _PATH_LOG);
569 }
570
571 if (chmod(lfile, 0666) < 0) {
572 bb_perror_msg_and_die("Could not set permission on " _PATH_LOG);
573 }
574#ifdef CONFIG_FEATURE_IPC_SYSLOG
575 if (circular_logging == TRUE) {
576 ipcsyslog_init();
577 }
578#endif
579
580#ifdef CONFIG_FEATURE_REMOTE_LOG
581 if (doRemoteLog == TRUE) {
582 init_RemoteLog();
583 }
584#endif
585
586 logMessage(LOG_SYSLOG | LOG_INFO, "syslogd started: " BB_BANNER);
587
588 for (;;) {
589
590 FD_ZERO(&fds);
591 FD_SET(sock_fd, &fds);
592
593 if (select(sock_fd + 1, &fds, NULL, NULL, NULL) < 0) {
594 if (errno == EINTR) {
595 /* alarm may have happened. */
596 continue;
597 }
598 bb_perror_msg_and_die("select error");
599 }
600
601 if (FD_ISSET(sock_fd, &fds)) {
602 int i;
603
604 RESERVE_CONFIG_BUFFER(tmpbuf, MAXLINE + 1);
605
606 memset(tmpbuf, '\0', MAXLINE + 1);
607 if ((i = recv(sock_fd, tmpbuf, MAXLINE, 0)) > 0) {
608 serveConnection(tmpbuf, i);
609 } else {
610 bb_perror_msg_and_die("UNIX socket error");
611 }
612 RELEASE_CONFIG_BUFFER(tmpbuf);
613 } /* FD_ISSET() */
614 } /* for main loop */
615}
616
617extern int syslogd_main(int argc, char **argv)
618{
619 int opt;
620
621 int doFork = TRUE;
622
623 char *p;
624
625 /* do normal option parsing */
626 while ((opt = getopt(argc, argv, "m:nO:s:Sb:R:LC::")) > 0) {
627 switch (opt) {
628 case 'm':
629 MarkInterval = atoi(optarg) * 60;
630 break;
631 case 'n':
632 doFork = FALSE;
633 break;
634 case 'O':
635 logFilePath = optarg;
636 break;
637#ifdef CONFIG_FEATURE_ROTATE_LOGFILE
638 case 's':
639 logFileSize = atoi(optarg) * 1024;
640 break;
641 case 'b':
642 logFileRotate = atoi(optarg);
643 if( logFileRotate > 99 ) logFileRotate = 99;
644 break;
645#endif
646#ifdef CONFIG_FEATURE_REMOTE_LOG
647 case 'R':
648 RemoteHost = bb_xstrdup(optarg);
649 if ((p = strchr(RemoteHost, ':'))) {
650 RemotePort = atoi(p + 1);
651 *p = '\0';
652 }
653 doRemoteLog = TRUE;
654 break;
655 case 'L':
656 local_logging = TRUE;
657 break;
658#endif
659#ifdef CONFIG_FEATURE_IPC_SYSLOG
660 case 'C':
661 if (optarg) {
662 int buf_size = atoi(optarg);
663 if (buf_size >= 4) {
664 shm_size = buf_size * 1024;
665 }
666 }
667 circular_logging = TRUE;
668 break;
669#endif
670 case 'S':
671 small = true;
672 break;
673 default:
674 bb_show_usage();
675 }
676 }
677
678#ifdef CONFIG_FEATURE_REMOTE_LOG
679 /* If they have not specified remote logging, then log locally */
680 if (doRemoteLog == FALSE)
681 local_logging = TRUE;
682#endif
683
684
685 /* Store away localhost's name before the fork */
686 gethostname(LocalHostName, sizeof(LocalHostName));
687 if ((p = strchr(LocalHostName, '.'))) {
688 *p = '\0';
689 }
690
691 umask(0);
692
693 if (doFork == TRUE) {
694#if defined(__uClinux__)
695 vfork_daemon_rexec(0, 1, argc, argv, "-n");
696#else /* __uClinux__ */
697 if(daemon(0, 1) < 0)
698 bb_perror_msg_and_die("daemon");
699#endif /* __uClinux__ */
700 }
701 doSyslogd();
702
703 return EXIT_SUCCESS;
704}
705
706/*
707Local Variables
708c-file-style: "linux"
709c-basic-offset: 4
710tab-width: 4
711End:
712*/
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 @@
1To run the test suite, change to this directory and run "./runtest". It will
2run all of the test cases, and list those with unexpected outcomes. Adding the
3-v option will cause it to show expected outcomes as well. To only run the test
4cases for particular applets, specify them as parameters to runtest.
5
6The test cases for an applet reside in the subdirectory of the applet name. The
7name of the test case should be the assertion that is tested. The test case
8should be a shell fragment that returns successfully if the test case passes,
9and unsuccessfully otherwise.
10
11If the test case relies on a certain feature, it should include the string
12"FEATURE: " followed by the name of the feature in a comment. If it is always
13expected to fail, it should include the string "XFAIL" in a comment.
14
15For the entire testsuite, the copyright is as follows:
16
17Copyright (C) 2001, 2002 Matt Kraai
18
19This program is free software; you can redistribute it and/or
20modify it under the terms of the GNU General Public License
21as published by the Free Software Foundation; either version 2
22of the License, or (at your option) any later version.
23
24This program is distributed in the hope that it will be useful,
25but WITHOUT ANY WARRANTY; without even the implied warranty of
26MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27GNU General Public License for more details.
28
29You should have received a copy of the GNU General Public License
30along with this program; if not, write to the Free Software
31Foundation, 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 @@
1This testsuite is quite obviously a work in progress. As such,
2there are a number of good extensions. If you are looking for
3something to do, feel free to tackle one or more of the following:
4
5Buildroot support
6 Erik has put together a handy package for constructing and
7 testing busybox called buildroot. Integrating this into
8 the testsuite would allow for greater test coverage (e.g.,
9 init, mount, and other privileged applications).
10
11libbb unit testing
12 Being able to test the functions of libbb individually may
13 help to prevent regressions.
14
15Standardization
16 This testsuite is totally bastardized. It would be better
17 to use an existing test framework, such as dejagnu, greg,
18 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 @@
1test x$(basename $(pwd)) = x$(busybox basename $(pwd))
2
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 @@
1echo foo | bzip2 | busybox bunzip2 > output
2echo 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 @@
1echo foo | bzip2 >foo.bz2
2busybox bunzip2 foo.bz2
3test ! -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 @@
1echo foo | bzip2 >foo.bz2
2busybox bzcat foo.bz2
3test -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 @@
1echo I WANT > foo
2busybox cat foo >bar
3cmp 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 @@
1echo I WANT > foo
2echo SOMETHING | busybox cat foo - >bar
3cat >baz <<EOF
4I WANT
5SOMETHING
6EOF
7cmp bar baz
diff --git a/busybox/testsuite/cmp/cmp-detects-difference b/busybox/testsuite/cmp/cmp-detects-difference
new file mode 100644
index 000000000..b9bb628f1
--- /dev/null
+++ b/busybox/testsuite/cmp/cmp-detects-difference
@@ -0,0 +1,9 @@
1echo foo >foo
2echo bar >bar
3set +e
4busybox cmp -s foo bar
5if [ $? != 0 ] ; then
6 exit 0;
7fi
8
9exit 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 @@
1echo file number one > file1
2echo file number two > file2
3ln -s file2 link1
4mkdir dir1
5touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3
6mkdir there
7busybox cp -a file1 file2 link1 dir1 there
8test -f there/file1
9test -f there/file2
10test ! -s there/dir1/file3
11test -L there/link1
12test xfile2 = x`readlink there/link1`
13test ! dir1/file3 -ot there/dir1/file3
14test ! 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 @@
1touch foo
2ln -s foo bar
3busybox cp -a bar baz
4test -L baz
5test 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 @@
1touch foo
2busybox cp foo bar
3cmp 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 @@
1dd if=/dev/zero of=foo seek=10k count=1 2>/dev/null
2busybox cp foo bar
3cmp 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 @@
1echo I WANT > foo
2busybox cp foo bar
3cmp 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 @@
1echo file number one > file1
2echo file number two > file2
3touch file3
4ln -s file2 link1
5mkdir there
6busybox cp -d file1 file2 file3 link1 there
7test -f there/file1
8test -f there/file2
9test ! -s there/file3
10test -L there/link1
11test 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 @@
1mkdir bar
2touch bar/baz
3busybox cp -R bar foo
4test -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 @@
1mkdir bar
2touch bar/baz
3mkdir foo
4busybox cp -R bar foo
5test -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 @@
1touch foo
2chmod a-r foo
3set +e
4busybox cp foo bar
5set -e
6test ! -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 @@
1echo file number one > file1
2echo file number two > file2
3touch file3
4ln -s file2 link1
5mkdir there
6busybox cp file1 file2 file3 link1 there
7test -f there/file1
8test -f there/file2
9test ! -s there/file3
10test -f there/link1
11cmp 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 @@
1touch foo
2ln -s foo bar
3busybox cp bar baz
4test -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 @@
1# FEATURE: CONFIG_FEATURE_PRESERVE_HARDLINKS
2touch foo
3ln foo bar
4mkdir baz
5busybox cp -d foo bar baz
6test 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 @@
1touch foo
2ln -s foo bar
3busybox cp -d bar baz
4test -L baz
5test 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 @@
1touch foo
2busybox cp foo bar
3test -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 @@
1test x"`date -R`" = x"`busybox date -R`"
2
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 @@
1test x"`date -u`" = x"`busybox date -u`"
2
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 @@
1test x"`date`" = x"`busybox date`"
2
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 @@
1echo I WANT >foo
2test "$(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 @@
1echo I WANT | busybox dd of=foo 2>/dev/null
2echo 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 @@
1echo I WANT | busybox dd of=foo >/dev/null 2>bar
2grep -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 @@
1test x$(dirname $(pwd)) = x$(busybox dirname $(pwd))
2
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 @@
1[ -n "$d" ] || d=..
2du -h "$d" > logfile.gnu
3busybox du -h "$d" > logfile.bb
4cmp 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 @@
1[ -n "$d" ] || d=..
2du -k "$d" > logfile.gnu
3busybox du -k "$d" > logfile.bb
4cmp 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 @@
1[ -n "$d" ] || d=..
2du -l "$d" > logfile.gnu
3busybox du -l "$d" > logfile.bb
4cmp 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 @@
1[ -n "$d" ] || d=..
2du -m "$d" > logfile.gnu
3busybox du -m "$d" > logfile.bb
4cmp 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 @@
1[ -n "$d" ] || d=..
2du -s "$d" > logfile.gnu
3busybox du -s "$d" > logfile.bb
4cmp 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 @@
1[ -n "$d" ] || d=..
2du "$d" > logfile.gnu
3busybox du "$d" > logfile.bb
4cmp 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 @@
1# busybox expr
2busybox expr 1 \| 1
3busybox expr 1 \| 0
4busybox expr 0 \| 1
5busybox expr 1 \& 1
6busybox expr 0 \< 1
7busybox expr 1 \> 0
8busybox expr 0 \<= 1
9busybox expr 1 \<= 1
10busybox expr 1 \>= 0
11busybox expr 1 \>= 1
12busybox expr 1 + 2
13busybox expr 2 - 1
14busybox expr 2 \* 3
15busybox expr 12 / 2
16busybox expr 12 % 5
17
18
19set +e
20busybox expr 0 \| 0
21if [ $? != 1 ] ; then
22 exit 1;
23fi;
24
25busybox expr 1 \& 0
26if [ $? != 1 ] ; then
27 exit 1;
28fi;
29
30busybox expr 0 \& 1
31if [ $? != 1 ] ; then
32 exit 1;
33fi;
34
35busybox expr 0 \& 0
36if [ $? != 1 ] ; then
37 exit 1;
38fi;
39
40busybox expr 1 \< 0
41if [ $? != 1 ] ; then
42 exit 1;
43fi;
44
45busybox expr 0 \> 1
46if [ $? != 1 ] ; then
47 exit 1;
48fi;
49
50busybox expr 1 \<= 0
51if [ $? != 1 ] ; then
52 exit 1;
53fi;
54
55busybox expr 0 \>= 1
56if [ $? != 1 ] ; then
57 exit 1;
58fi;
59
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 @@
1# FEATURE: CONFIG_FEATURE_GREP_EGREP_ALIAS
2test 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 @@
1# FEATURE: CONFIG_FEATURE_GREP_EGREP_ALIAS
2echo 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 @@
1# FEATURE: CONFIG_FEATURE_GREP_EGREP_ALIAS
2echo 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 @@
1set +e
2echo -e '\0' | busybox grep .
3if [ $? != 0 ] ; then
4 exit 0;
5fi
6
7exit 1;
8
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 @@
1echo foo | gzip | busybox gunzip > output
2echo 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 @@
1touch foo bar
2busybox gzip foo bar
3test -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 @@
1touch foo
2busybox gzip foo
3test ! -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 @@
1[ -n "$d" ] || d=..
2head -n 2 "$d/README" > logfile.gnu
3busybox head -n 2 "$d/README" > logfile.bb
4cmp 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 @@
1[ -n "$d" ] || d=..
2head "$d/README" > logfile.gnu
3busybox head "$d/README" > logfile.bb
4cmp 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 @@
1test x$(hostid) = x$(busybox hostid)
2
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 @@
1test x$(hostname -d) = x$(busybox hostname -d)
2
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 @@
1test x$(hostname -i) = x$(busybox hostname -i)
2
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 @@
1echo file number one > file1
2busybox ln file1 link1
3test -f file1
4test -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 @@
1echo file number one > file1
2busybox ln -s file1 link1
3test -L link1
4test 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 @@
1echo file number one > file1
2echo file number two > link1
3busybox ln -f file1 link1
4test -f file1
5test -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 @@
1echo file number one > file1
2echo file number two > link1
3busybox ln -f -s file1 link1
4test -L link1
5test 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 @@
1echo file number one > file1
2echo file number two > link1
3set +e
4busybox ln file1 link1
5if [ $? != 0 ] ; then
6 exit 0;
7fi
8exit 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 @@
1echo file number one > file1
2echo file number two > link1
3set +e
4busybox ln -s file1 link1
5if [ $? != 0 ] ; then
6 exit 0;
7fi
8exit 1;
9
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 @@
1[ -n "$d" ] || d=..
2ls -1 "$d" > logfile.gnu
3busybox ls -1 "$d" > logfile.bb
4cmp 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 @@
1[ -n "$d" ] || d=..
2ls -h "$d" > logfile.gnu
3busybox ls -h "$d" > logfile.bb
4cmp 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 @@
1[ -n "$d" ] || d=..
2ls -l "$d" > logfile.gnu
3busybox ls -l "$d" > logfile.bb
4cmp 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 @@
1[ -n "$d" ] || d=..
2ls -1s "$d" > logfile.gnu
3busybox ls -1s "$d" > logfile.bb
4cmp 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 @@
1touch foo
2md5sum foo > bar
3busybox 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 @@
1busybox mkdir foo
2test -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 @@
1busybox mkdir -p foo/bar
2test -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 @@
1echo file number one > file1
2echo file number two > file2
3ln -s file2 link1
4mkdir dir1
5touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3
6mkdir there
7busybox mv file1 file2 link1 dir1 there
8test -f there/file1
9test -f there/file2
10test -f there/dir1/file3
11test -L there/link1
12test xfile2 = x`readlink there/link1`
13test ! -e file1
14test ! -e file2
15test ! -e link1
16test ! -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 @@
1touch foo
2ln -s foo bar
3busybox mv bar baz
4test -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 @@
1touch foo
2busybox mv foo bar
3test ! -e foo
4test -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 @@
1touch foo
2busybox mv foo bar
3test ! -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 @@
1touch foo
2ln foo bar
3busybox mv bar baz
4test ! -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 @@
1dd if=/dev/zero of=foo seek=10k count=1 2>/dev/null
2busybox mv foo bar
3test ! -e foo
4test -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 @@
1echo I WANT > foo
2busybox mv foo bar
3test ! -e foo
4test -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 @@
1touch foo
2ln -s foo bar
3busybox mv bar baz
4test -f foo
5test ! -e bar
6test -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 @@
1touch foo
2chmod a-r foo
3busybox mv foo bar
4test ! -e foo
5test -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 @@
1# FEATURE: CONFIG_FEATURE_PRESERVE_HARDLINKS
2touch foo
3ln foo bar
4mkdir baz
5busybox mv foo bar baz
6test 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 @@
1touch foo
2ln -s foo bar
3busybox mv bar baz
4test -L baz
5test 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 @@
1echo file number one > file1
2echo file number two > file2
3ln -s file2 link1
4mkdir dir1
5touch --date='Sat Jan 29 21:24:08 PST 2000' dir1/file3
6mkdir there
7busybox mv file1 file2 link1 dir1 there
8test -f there/file1
9test -f there/file2
10test -f there/dir1/file3
11test -L there/link1
12test xfile2 = x`readlink there/link1`
13test ! -e file1
14test ! -e file2
15test ! -e link1
16test ! -e dir1/file3
17set +e
18busybox mv there there/dir1
19if [ $? != 0 ] ; then
20 exit 0;
21fi
22
23exit 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 @@
1touch foo
2busybox mv foo bar
3test ! -e foo
4test -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 @@
1touch foo
2busybox rm foo
3test ! -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 @@
1mkdir -p foo/bar
2busybox rmdir -p foo/bar
3test ! -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 @@
1#!/bin/sh
2
3[ -n "$srcdir" ] || srcdir=$(pwd)
4[ -n "$bindir" ] || bindir=$(dirname $(pwd))
5PATH=$bindir:$PATH
6
7run_applet_testcase ()
8{
9 local applet=$1
10 local testcase=$2
11
12 local status=0
13 local RES=
14
15 local uc_applet=$(echo $applet | tr a-z A-Z)
16 local testname=$(basename $testcase)
17
18 if grep -q "^# CONFIG_${uc_applet} is not set$" $bindir/.config; then
19 echo UNTESTED: $testname
20 return 0
21 fi
22
23 if grep -q "^# FEATURE: " $testcase; then
24 local feature=`sed -ne 's/^# FEATURE: //p' $testcase`
25
26 if grep -q "^# ${feature} is not set$" $bindir/.config; then
27 echo UNTESTED: $testname
28 return 0
29 fi
30 fi
31
32 rm -rf tmp
33 mkdir -p tmp
34 pushd tmp >/dev/null
35
36 d=$srcdir sh -x -e $testcase >.logfile.txt 2>&1
37
38 if [ $? != 0 ] ; then
39 echo FAIL: $testname
40 if [ "$verbose" = 1 ]; then
41 cat .logfile.txt
42 #exit 1;
43 fi;
44 status=$?
45 else
46 echo PASS: $testname
47 rm -f .logfile.txt
48 status=$?
49 fi
50
51 popd >/dev/null
52 rm -rf tmp
53
54 return $status
55}
56
57run_applet_tests ()
58{
59 local applet=$1
60
61 local status=0
62
63 for testcase in $srcdir/$applet/*; do
64 if [ "$testcase" = "$srcdir/$applet/CVS" ]; then
65 continue
66 fi
67
68 if run_applet_testcase $applet $testcase; then
69 :
70 else
71 status=1
72 fi
73 done
74
75 return $status
76}
77
78
79status=0
80
81if [ x"$1" = x"-v" ]; then
82 verbose=1
83 shift
84fi
85
86if [ $# -ne 0 ]; then
87 applets="$@"
88else
89 applets=$(ls $srcdir)
90fi
91
92for applet in $applets; do
93 if [ "$applet" != CVS -a -d "$srcdir/$applet" ]; then
94 if run_applet_tests $applet; then
95 :
96 else
97 status=1
98 fi
99 fi
100done
101
102exit $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' </dev/null
diff --git a/busybox/testsuite/sed/sed-aic-commands b/busybox/testsuite/sed/sed-aic-commands
new file mode 100644
index 000000000..b41c14ab8
--- /dev/null
+++ b/busybox/testsuite/sed/sed-aic-commands
@@ -0,0 +1,134 @@
1cat - >input <<EOF
22i\\
3before 2
45c\\
5Change 5
610a\\
7After 10
822i\\
9before 22\\
10Continued
1125c\\
12Change 25\\
13Continued
1420a\\
15After 20\\
16Continued
17 32i\\
18before 32\\
19Continued 1\\
20Continued 2\\
21Continued 3
22 35c\\
23Change 35\\
24Continued 1\\
25Continued 2\\
26Continued 3
27 30a\\
28After 30\\
29Continued 1\\
30Continued 2\\
31Continued 3
32EOF
33busybox sed -f input >output <<EOF
34 1 y
35 2 y
36 3 y
37 4 y
38 5 y
39 6 y
40 7 y
41 8 y
42 9 y
43 10 y
44 11 y
45 12 y
46 13 y
47 14 y
48 15 y
49 16 y
50 17 y
51 18 y
52 19 y
53 20 y
54 21 y
55 22 y
56 23 y
57 24 y
58 25 y
59 26 y
60 27 y
61 28 y
62 29 y
63 30 y
64 31 y
65 32 y
66 33 y
67 34 y
68 35 y
69 36 y
70 37 y
71 38 y
72 39 y
73 40 y
74EOF
75cmp -s output - <<EOF
76 1 y
77before 2
78 2 y
79 3 y
80 4 y
81Change 5
82 6 y
83 7 y
84 8 y
85 9 y
86 10 y
87After 10
88 11 y
89 12 y
90 13 y
91 14 y
92 15 y
93 16 y
94 17 y
95 18 y
96 19 y
97 20 y
98After 20
99Continued
100 21 y
101before 22
102Continued
103 22 y
104 23 y
105 24 y
106Change 25
107Continued
108 26 y
109 27 y
110 28 y
111 29 y
112 30 y
113After 30
114Continued 1
115Continued 2
116Continued 3
117 31 y
118before 32
119Continued 1
120Continued 2
121Continued 3
122 32 y
123 33 y
124 34 y
125Change 35
126Continued 1
127Continued 2
128Continued 3
129 36 y
130 37 y
131 38 y
132 39 y
133 40 y
134EOF
diff --git a/busybox/testsuite/sed/sed-append-hold-space-to-pattern-space b/busybox/testsuite/sed/sed-append-hold-space-to-pattern-space
new file mode 100644
index 000000000..6dda80fee
--- /dev/null
+++ b/busybox/testsuite/sed/sed-append-hold-space-to-pattern-space
@@ -0,0 +1,13 @@
1busybox sed 'G'>output <<EOF
2a
3b
4c
5EOF
6cmp -s output - <<EOF
7a
8
9b
10
11c
12
13EOF
diff --git a/busybox/testsuite/sed/sed-append-next-line b/busybox/testsuite/sed/sed-append-next-line
new file mode 100644
index 000000000..0621a319f
--- /dev/null
+++ b/busybox/testsuite/sed/sed-append-next-line
@@ -0,0 +1,19 @@
1# This will fail if CONFIG_FEATURE_SED_GNU_COMPATABILITY is defined
2busybox sed 'N;p'>output <<EOF
3a
4b
5c
6EOF
7
8set +e
9cmp -s output - <<EOF
10a
11b
12a
13b
14c
15EOF
16if [ $? != 0 ] ; then
17 exit 0;
18fi
19exit 1;
diff --git a/busybox/testsuite/sed/sed-branch b/busybox/testsuite/sed/sed-branch
new file mode 100644
index 000000000..4167569ad
--- /dev/null
+++ b/busybox/testsuite/sed/sed-branch
@@ -0,0 +1 @@
test "$(echo foo | busybox sed 'b one;p;: one')" = foo
diff --git a/busybox/testsuite/sed/sed-branch-conditional b/busybox/testsuite/sed/sed-branch-conditional
new file mode 100644
index 000000000..47d0a5ff2
--- /dev/null
+++ b/busybox/testsuite/sed/sed-branch-conditional
@@ -0,0 +1,15 @@
1busybox sed 's/a/1/;t one;p;: one;p'>output <<EOF
2a
3b
4c
5EOF
6cmp -s output - <<EOF
71
81
9b
10b
11b
12c
13c
14c
15EOF
diff --git a/busybox/testsuite/sed/sed-branch-conditional2 b/busybox/testsuite/sed/sed-branch-conditional2
new file mode 100644
index 000000000..f4b11f0f8
--- /dev/null
+++ b/busybox/testsuite/sed/sed-branch-conditional2
@@ -0,0 +1,11 @@
1#XFAIL
2busybox sed 's/a/b/;:loop;t loop'>output <<EOF
3a
4b
5c
6EOF
7cmp -s output - <<EOF
8b
9b
10c
11EOF
diff --git a/busybox/testsuite/sed/sed-branch-no-label b/busybox/testsuite/sed/sed-branch-no-label
new file mode 100644
index 000000000..446c1bcd9
--- /dev/null
+++ b/busybox/testsuite/sed/sed-branch-no-label
@@ -0,0 +1 @@
test "$(echo foo | busybox sed 'b;p')" = foo
diff --git a/busybox/testsuite/sed/sed-chains-substs b/busybox/testsuite/sed/sed-chains-substs
new file mode 100644
index 000000000..266936ac4
--- /dev/null
+++ b/busybox/testsuite/sed/sed-chains-substs
@@ -0,0 +1 @@
test "$(echo foo | busybox sed -e s/foo/bar/ -e s/bar/baz/)" = baz
diff --git a/busybox/testsuite/sed/sed-chains-substs2 b/busybox/testsuite/sed/sed-chains-substs2
new file mode 100644
index 000000000..90568f6e6
--- /dev/null
+++ b/busybox/testsuite/sed/sed-chains-substs2
@@ -0,0 +1 @@
test x"$(echo foo | busybox -n sed -e s/foo/bar/ -e s/foo/baz/)" = x
diff --git a/busybox/testsuite/sed/sed-does-not-substitute-in-deleted-line b/busybox/testsuite/sed/sed-does-not-substitute-in-deleted-line
new file mode 100644
index 000000000..6f106e104
--- /dev/null
+++ b/busybox/testsuite/sed/sed-does-not-substitute-in-deleted-line
@@ -0,0 +1,2 @@
1echo foo | busybox sed -e /foo/d -e s/foo/bar/ >foo
2cmp 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 @@
1busybox sed -e 's/.*root=/\1/' >output <<EOF
2BOOT_IMAGE=vmlinuz root=/dev/hda5 initrd=init1
3EOF
4cmp -s output - <<EOF
5/dev/hda5 initrd=init1
6EOF
diff --git a/busybox/testsuite/sed/sed-next-line b/busybox/testsuite/sed/sed-next-line
new file mode 100644
index 000000000..38fe20cf2
--- /dev/null
+++ b/busybox/testsuite/sed/sed-next-line
@@ -0,0 +1,12 @@
1busybox sed 'n;p'>output <<EOF
2a
3b
4c
5EOF
6cmp -s output - <<EOF
7a
8b
9b
10c
11c
12EOF
diff --git a/busybox/testsuite/sed/sed-prints-line-once-for-multiple-substs b/busybox/testsuite/sed/sed-prints-line-once-for-multiple-substs
new file mode 100644
index 000000000..ba8955d6e
--- /dev/null
+++ b/busybox/testsuite/sed/sed-prints-line-once-for-multiple-substs
@@ -0,0 +1,4 @@
1busybox sed -e s/1/2/g -e s/3/4/g >output <<EOF
21
3EOF
4echo 2 | cmp -s output -
diff --git a/busybox/testsuite/sed/sed-recurses-properly b/busybox/testsuite/sed/sed-recurses-properly
new file mode 100644
index 000000000..a02667b41
--- /dev/null
+++ b/busybox/testsuite/sed/sed-recurses-properly
@@ -0,0 +1 @@
test "`echo '12345' | busybox sed -e 's/[[:space:]]*/,/g')` = ',1,2,3,4,5,'"
diff --git a/busybox/testsuite/sed/sed-regex-match-newline b/busybox/testsuite/sed/sed-regex-match-newline
new file mode 100644
index 000000000..1057e1718
--- /dev/null
+++ b/busybox/testsuite/sed/sed-regex-match-newline
@@ -0,0 +1,10 @@
1# FEATURE: CONFIG_FEATURE_SED_EMBEDED_NEWLINE
2busybox sed -n 'N;/a\nb/p'>output <<EOF
3a
4b
5c
6EOF
7cmp -s output - <<EOF
8a
9b
10EOF
diff --git a/busybox/testsuite/sed/sed-splits-edit-commands-on-command-line b/busybox/testsuite/sed/sed-splits-edit-commands-on-command-line
new file mode 100644
index 000000000..6421fa552
--- /dev/null
+++ b/busybox/testsuite/sed/sed-splits-edit-commands-on-command-line
@@ -0,0 +1,9 @@
1echo 2 | busybox sed -e 'i\
21
3a\
43' > output
5cmp output - <<EOF
61
72
83
9EOF
diff --git a/busybox/testsuite/sed/sed-subst-subprint b/busybox/testsuite/sed/sed-subst-subprint
new file mode 100644
index 000000000..24f8bad7d
--- /dev/null
+++ b/busybox/testsuite/sed/sed-subst-subprint
@@ -0,0 +1,9 @@
1busybox sed 's/foo/bar/p'>output <<EOF
2foo
3bar
4EOF
5cmp -s output - <<EOF
6bar
7bar
8bar
9EOF
diff --git a/busybox/testsuite/sed/sed-write-to-stdout b/busybox/testsuite/sed/sed-write-to-stdout
new file mode 100644
index 000000000..95b4d724b
--- /dev/null
+++ b/busybox/testsuite/sed/sed-write-to-stdout
@@ -0,0 +1,10 @@
1busybox sed -n 'N;P;p'>output <<EOF
2a
3b
4c
5EOF
6cmp -s output - <<EOF
7a
8a
9b
10EOF
diff --git a/busybox/testsuite/sort/sort-n-works b/busybox/testsuite/sort/sort-n-works
new file mode 100644
index 000000000..878108ddd
--- /dev/null
+++ b/busybox/testsuite/sort/sort-n-works
@@ -0,0 +1,4 @@
1[ -n "$d" ] || d=..
2sort -n "$d/README" > logfile.gnu
3busybox sort -n "$d/README" > logfile.bb
4cmp 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 @@
1[ -n "$d" ] || d=..
2sort -r "$d/README" > logfile.gnu
3busybox sort -r "$d/README" > logfile.bb
4cmp 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 @@
1[ -n "$d" ] || d=..
2sort "$d/README" > logfile.gnu
3busybox sort "$d/README" > logfile.bb
4cmp 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 @@
1rm -f foo bar
2strings -af ../../busybox > foo
3busybox strings -af ../../busybox > bar
4set +e
5test ! -f foo -a -f bar
6if [ $? = 0 ] ; then
7 set -e
8 diff -q foo bar
9fi
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 @@
1[ -n "$d" ] || d=..
2tail -n 2 "$d/README" > logfile.gnu
3busybox tail -n 2 "$d/README" > logfile.bb
4cmp 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 @@
1[ -n "$d" ] || d=..
2tail -n 2 "$d/README" > logfile.gnu
3busybox tail -n 2 "$d/README" > logfile.bb
4cmp 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 @@
1# FEATURE: CONFIG_FEATURE_TAR_CREATE
2touch foo bar
3busybox tar cf foo.tar foo bar
4rm foo bar
5tar xf foo.tar
6test -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 @@
1touch foo
2tar cf foo.tar foo
3! 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 @@
1touch foo
2tar cf foo.tar foo
3rm foo
4busybox tar xf foo.tar
5test -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 @@
1touch foo
2tar cf foo.tar foo
3rm foo
4cat foo.tar | busybox tar x
5test -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 @@
1touch foo bar
2busybox tar cf foo.tar foo bar
3rm foo bar
4busybox tar -xf foo.tar
5test -f foo
6test -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 @@
1echo foo > foo
2tar cf foo.tar foo
3cat 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 @@
1# FEATURE: CONFIG_FEATURE_TAR_CREATE
2# FEATURE: CONFIG_FEATURE_TAR_GZIP
3touch foo
4busybox tar czf foo.tar.gz foo
5gzip -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 @@
1# FEATURE: CONFIG_FEATURE_TAR_FROM
2# FEATURE: CONFIG_FEATURE_TAR_CREATE
3touch foo
4tar cf foo.tar foo
5echo foo >foo.exclude
6busybox 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 @@
1# FEATURE: CONFIG_FEATURE_TAR_FROM
2# FEATURE: CONFIG_FEATURE_TAR_CREATE
3touch foo bar baz
4tar cf foo.tar foo bar baz
5echo foo >foo.exclude
6rm foo bar baz
7busybox tar xf foo.tar foo bar -X foo.exclude
8test ! -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 @@
1# FEATURE: CONFIG_FEATURE_TAR_FROM
2# FEATURE: CONFIG_FEATURE_TAR_CREATE
3touch foo
4touch bar
5tar cf foo.tar foo bar
6echo foo > foo.exclude
7echo bar > bar.exclude
8rm foo bar
9busybox tar xf foo.tar -X foo.exclude -X bar.exclude
10test ! -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 @@
1# FEATURE: CONFIG_FEATURE_TAR_FROM
2# FEATURE: CONFIG_FEATURE_TAR_CREATE
3mkdir foo
4touch foo/bar
5tar cf foo.tar foo
6rm -rf foo
7echo foo/bar >foobar.exclude
8busybox tar xf foo.tar foo -X foobar.exclude
9test -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 @@
1echo i\'m a little teapot >foo
2cp foo bar
3echo i\'m a little teapot >>foo
4echo i\'m a little teapot | busybox tee -a bar >/dev/null
5cmp 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 @@
1echo i\'m a little teapot >foo
2echo i\'m a little teapot | busybox tee bar >baz
3cmp 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 @@
1busybox touch foo
2test -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 @@
1busybox touch -c foo
2test ! -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 @@
1touch -t 198001010000 bar
2busybox touch -c foo bar
3test 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 @@
1echo testing | tr -d aeiou > logfile.gnu
2echo testing | busybox tr -d aeiou > logfile.bb
3
4cmp 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 @@
1echo "cbaab" | tr abc zyx > logfile.gnu
2echo "TESTING A B C" | tr [A-Z] [a-z] >> logfile.gnu
3echo abc[] | tr a[b AXB >> logfile.gnu
4
5echo "cbaab" | busybox tr abc zyx > logfile.bb
6echo "TESTING A B C" | busybox tr [A-Z] [a-z] >> logfile.bb
7echo abc[] | busybox tr a[b AXB >> logfile.bb
8
9cmp 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 @@
1busybox uptime
2
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 @@
1saved_umask=$(umask)
2umask 0
3busybox uuencode foo </dev/null | head -n 1 | grep -q 666
4umask $saved_umask
diff --git a/busybox/testsuite/wc/wc-counts-all b/busybox/testsuite/wc/wc-counts-all
new file mode 100644
index 000000000..5e2cb6e4a
--- /dev/null
+++ b/busybox/testsuite/wc/wc-counts-all
@@ -0,0 +1 @@
test "`echo i\'m a little teapot | busybox wc`" = ' 1 4 20'
diff --git a/busybox/testsuite/wc/wc-counts-characters b/busybox/testsuite/wc/wc-counts-characters
new file mode 100644
index 000000000..755864684
--- /dev/null
+++ b/busybox/testsuite/wc/wc-counts-characters
@@ -0,0 +1 @@
test `echo i\'m a little teapot | busybox wc -c` -eq 20
diff --git a/busybox/testsuite/wc/wc-counts-lines b/busybox/testsuite/wc/wc-counts-lines
new file mode 100644
index 000000000..5be6ed089
--- /dev/null
+++ b/busybox/testsuite/wc/wc-counts-lines
@@ -0,0 +1 @@
test `echo i\'m a little teapot | busybox wc -l` -eq 1
diff --git a/busybox/testsuite/wc/wc-counts-words b/busybox/testsuite/wc/wc-counts-words
new file mode 100644
index 000000000..331650e95
--- /dev/null
+++ b/busybox/testsuite/wc/wc-counts-words
@@ -0,0 +1 @@
test `echo i\'m a little teapot | busybox wc -w` -eq 4
diff --git a/busybox/testsuite/wc/wc-prints-longest-line-length b/busybox/testsuite/wc/wc-prints-longest-line-length
new file mode 100644
index 000000000..78831fc13
--- /dev/null
+++ b/busybox/testsuite/wc/wc-prints-longest-line-length
@@ -0,0 +1 @@
test `echo i\'m a little teapot | busybox wc -L` -eq 19
diff --git a/busybox/testsuite/wget/wget--O-overrides--P b/busybox/testsuite/wget/wget--O-overrides--P
new file mode 100644
index 000000000..fdb5d47c0
--- /dev/null
+++ b/busybox/testsuite/wget/wget--O-overrides--P
@@ -0,0 +1,3 @@
1mkdir foo
2busybox wget -q -O index.html -P foo http://www.google.com/
3test -s index.html
diff --git a/busybox/testsuite/wget/wget-handles-empty-path b/busybox/testsuite/wget/wget-handles-empty-path
new file mode 100644
index 000000000..5b591837a
--- /dev/null
+++ b/busybox/testsuite/wget/wget-handles-empty-path
@@ -0,0 +1 @@
busybox wget http://www.google.com
diff --git a/busybox/testsuite/wget/wget-retrieves-google-index b/busybox/testsuite/wget/wget-retrieves-google-index
new file mode 100644
index 000000000..7be9a8087
--- /dev/null
+++ b/busybox/testsuite/wget/wget-retrieves-google-index
@@ -0,0 +1,2 @@
1busybox wget -q -O foo http://www.google.com/
2test -s foo
diff --git a/busybox/testsuite/wget/wget-supports--P b/busybox/testsuite/wget/wget-supports--P
new file mode 100644
index 000000000..9b4d095e6
--- /dev/null
+++ b/busybox/testsuite/wget/wget-supports--P
@@ -0,0 +1,3 @@
1mkdir foo
2busybox wget -q -P foo http://www.google.com/
3test -s foo/index.html
diff --git a/busybox/testsuite/which/which-uses-default-path b/busybox/testsuite/which/which-uses-default-path
new file mode 100644
index 000000000..63ceb9f8f
--- /dev/null
+++ b/busybox/testsuite/which/which-uses-default-path
@@ -0,0 +1,4 @@
1BUSYBOX=$(type -p busybox)
2SAVED_PATH=$PATH
3unset PATH
4$BUSYBOX which ls
diff --git a/busybox/testsuite/xargs/xargs-works b/busybox/testsuite/xargs/xargs-works
new file mode 100644
index 000000000..c95869e89
--- /dev/null
+++ b/busybox/testsuite/xargs/xargs-works
@@ -0,0 +1,4 @@
1[ -n "$d" ] || d=..
2find "$d" -name \*works -type f | xargs md5sum > logfile.gnu
3find "$d" -name \*works -type f | busybox xargs md5sum > logfile.bb
4diff -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 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Linux System Utilities"
7
8
9config CONFIG_DMESG
10 bool "dmesg"
11 default n
12 help
13 dmesg is used to examine or control the kernel ring buffer. When the
14 Linux kernel prints messages to the system log, they are stored in
15 the kernel ring buffer. You can use dmesg to print the kernel's ring
16 buffer, clear the kernel ring buffer, change the size of the kernel
17 ring buffer, and change the priority level at which kernel messages
18 are also logged to the system console. Enable this option if you
19 wish to enable the 'dmesg' utility.
20
21config CONFIG_FBSET
22 bool "fbset"
23 default n
24 help
25 fbset is used to show or change the settings of a Linux frame buffer
26 device. The frame buffer device provides a simple and unique
27 interface to access a graphics display. Enable this option
28 if you wish to enable the 'fbset' utility.
29
30
31config CONFIG_FEATURE_FBSET_FANCY
32 bool " Turn on extra fbset options"
33 default n
34 depends on CONFIG_FBSET
35 help
36 This option enables extended fbset options, allowing one to set the
37 framebuffer size, color depth, etc. interface to access a graphics
38 display. Enable this option if you wish to enable extended fbset
39 options.
40
41config CONFIG_FEATURE_FBSET_READMODE
42 bool " Turn on fbset readmode support"
43 default n
44 depends on CONFIG_FBSET
45 help
46 This option allows fbset to read the video mode database stored by
47 default as /etc/fb.modes, which can be used to set frame buffer
48 device to pre-defined video modes.
49
50config CONFIG_FDFLUSH
51 bool "fdflush"
52 default n
53 help
54 fdflush is only needed when changing media on slightly-broken
55 removable media drives. It is used to make Linux believe that a
56 hardware disk-change switch has been actuated, which causes Linux to
57 forget anything it has cached from the previous media. If you have
58 such a slightly-broken drive, you will need to run fdflush every time
59 you change a disk. Most people have working hardware and can safely
60 leave this disabled.
61
62config CONFIG_FDFORMAT
63 bool "fdformat"
64 default n
65 help
66 fdformat is used to low-level format a floppy disk.
67
68config CONFIG_FDISK
69 bool "fdisk"
70 default n
71 help
72 The fdisk utility is used to divide hard disks into one or more
73 logical disks, which are generally called partitions. This utility
74 can be used to list and edit the set of partitions or BSD style
75 'disk slices' that are defined on a hard drive.
76
77config FDISK_SUPPORT_LARGE_DISKS
78 bool " support over 4GB disks"
79 default y
80 depends on CONFIG_FDISK
81 help
82 Enable this option to support large disks > 4GB.
83
84config CONFIG_FEATURE_FDISK_WRITABLE
85 bool " Write support"
86 default y
87 depends on CONFIG_FDISK
88 help
89 Enabling this option allows you to create or change a partition table
90 and write those changes out to disk. If you leave this option
91 disabled, you will only be able to view the partition table.
92
93config CONFIG_FEATURE_AIX_LABEL
94 bool " Support AIX disklabels"
95 default n
96 depends on CONFIG_FDISK && CONFIG_FEATURE_FDISK_WRITABLE
97 help
98 Enabling this option allows you to create or change AIX disklabels.
99 Most people can safely leave this option disabled.
100
101config CONFIG_FEATURE_SGI_LABEL
102 bool " Support SGI disklabels"
103 default n
104 depends on CONFIG_FDISK && CONFIG_FEATURE_FDISK_WRITABLE
105 help
106 Enabling this option allows you to create or change SGI disklabels.
107 Most people can safely leave this option disabled.
108
109config CONFIG_FEATURE_SUN_LABEL
110 bool " Support SUN disklabels"
111 default n
112 depends on CONFIG_FDISK && CONFIG_FEATURE_FDISK_WRITABLE
113 help
114 Enabling this option allows you to create or change SUN disklabels.
115 Most people can safely leave this option disabled.
116
117config CONFIG_FEATURE_OSF_LABEL
118 bool " Support BSD disklabels"
119 default n
120 depends on CONFIG_FDISK && CONFIG_FEATURE_FDISK_WRITABLE
121 help
122 Enabling this option allows you to create or change BSD disklabels
123 and define and edit BSD disk slices.
124
125config CONFIG_FEATURE_FDISK_ADVANCED
126 bool " Support expert mode"
127 default n
128 depends on CONFIG_FDISK && CONFIG_FEATURE_FDISK_WRITABLE
129 help
130 Enabling this option allows you to do terribly unsafe things like
131 define arbitrary drive geometry, move the beginning of data in a
132 partition, and similarly evil things. Unless you have a very good
133 reason you would be wise to leave this disabled.
134
135config CONFIG_FREERAMDISK
136 bool "freeramdisk"
137 default n
138 help
139 Linux allows you to create ramdisks. This utility allows you to
140 delete them and completely free all memory that was used for the
141 ramdisk. For example, if you boot Linux into a ramdisk and later
142 pivot_root, you may want to free the memory that is allocated to the
143 ramdisk. If you have no use for freeing memory from a ramdisk, leave
144 this disabled.
145
146config CONFIG_FSCK_MINIX
147 bool "fsck_minix"
148 default n
149 help
150 The minix filesystem is a nice, small, compact, read-write filesystem
151 with little overhead. It is not a journaling filesystem however and
152 can experience corruption if it is not properly unmounted or if the
153 power goes off in the middle of a write. This utility allows you to
154 check for and attempt to repair any corruption that occurs to a minix
155 filesystem.
156
157config CONFIG_MKFS_MINIX
158 bool "mkfs_minix"
159 default n
160 help
161 The minix filesystem is a nice, small, compact, read-write filesystem
162 with little overhead. If you wish to be able to create minix filesystems
163 this utility will do the job for you.
164
165comment "Minix filesystem support"
166 depends on CONFIG_FSCK_MINIX || CONFIG_MKFS_MINIX
167
168config CONFIG_FEATURE_MINIX2
169 bool " Support Minix fs v2 (fsck_minix/mkfs_minix)"
170 default y
171 depends on CONFIG_FSCK_MINIX || CONFIG_MKFS_MINIX
172 help
173 If you wish to be able to create version 2 minix filesystems, enable this.
174 If you enabled 'mkfs_minix' then you almost certainly want to be using the
175 version 2 filesystem support.
176
177config CONFIG_GETOPT
178 bool "getopt"
179 default n
180 help
181 The getopt utility is used to break up (parse) options in command
182 lines to make it easy to write complex shell scripts that also check
183 for legal (and illegal) options. If you want to write horribly
184 complex shell scripts, or use some horribly complex shell script
185 written by others, this utility may be for you. Most people will
186 wisely leave this disabled.
187
188config CONFIG_HEXDUMP
189 bool "hexdump"
190 default n
191 help
192 The hexdump utility is used to display binary data in a readable
193 way that is comparable to the output from most hex editors.
194
195config CONFIG_HWCLOCK
196 bool "hwclock"
197 default n
198 help
199 The hwclock utility is used to read and set the hardware clock
200 on a system. This is primarily used to set the current time on
201 shutdown in the hardware clock, so the hardware will keep the
202 correct time when Linux is _not_ running.
203
204config CONFIG_FEATURE_HWCLOCK_LONGOPTIONS
205 bool " Support long options (--hctosys,...)"
206 default n
207 depends on CONFIG_HWCLOCK
208 help
209 By default, the hwclock utility only uses short options. If you
210 are overly fond of its long options, such as --hctosys, --utc, etc)
211 then enable this option.
212
213config CONFIG_LOSETUP
214 bool "losetup"
215 default n
216 help
217 losetup is used to associate or detach a loop device with a regular
218 file or block device, and to query the status of a loop device. This
219 version does not currently support enabling data encryption.
220
221config CONFIG_MKSWAP
222 bool "mkswap"
223 default n
224 help
225 The mkswap utility is used to configure a file or disk partition as
226 Linux swap space. This allows Linux to use the entire file or
227 partition as if it were additional RAM, which can greatly increase
228 the capability of low-memory machines. This additional memory is
229 much slower than real RAM, but can be very helpful at preventing your
230 applications being killed by the Linux out of memory (OOM) killer.
231 Once you have created swap space using 'mkswap' you need to enable
232 the swap space using the 'swapon' utility.
233
234config CONFIG_MORE
235 bool "more"
236 default n
237 help
238 more is a simple utility which allows you to read text one screen
239 sized page at a time. If you want to read text that is larger than
240 the screen, and you are using anything faster than a 300 baud modem,
241 you will probably find this utility very helpful. If you don't have
242 any need to reading text files, you can leave this disabled.
243
244config CONFIG_FEATURE_USE_TERMIOS
245 bool " Use termios to manipulate the screen"
246 default y
247 depends on CONFIG_MORE
248 help
249 This option allows utilities such as 'more' and 'top' to determine
250 the size of the screen. If you leave this disabled, your utilities
251 that display things on the screen will be especially primitive and
252 will be unable to determine the current screen size, and will be
253 unable to move the cursor.
254
255config CONFIG_PIVOT_ROOT
256 bool "pivot_root"
257 default n
258 help
259 The pivot_root utility swaps the mount points for the root filesystem
260 with some other mounted filesystem. This allows you to do all sorts
261 of wild and crazy things with your Linux system and is far more
262 powerful than 'chroot'.
263
264config CONFIG_RDATE
265 bool "rdate"
266 default n
267 help
268 The rdate utility allows you to synchronize the date and time of your
269 system clock with the date and time of a remote networked system using
270 the RFC868 protocol, which is built into the inetd daemon on most
271 systems.
272
273config CONFIG_SWAPONOFF
274 bool "swaponoff"
275 default n
276 help
277 This option enables both the 'swapon' and the 'swapoff' utilities.
278 Once you have created some swap space using 'mkswap', you also need
279 to enable your swap space with the 'swapon' utility. The 'swapoff'
280 utility is used, typically at system shutdown, to disable any swap
281 space. If you are not using any swap space, you can leave this
282 option disabled.
283
284config CONFIG_MOUNT
285 bool "mount"
286 default n
287 help
288 All files and filesystems in Unix are arranged into one big directory
289 tree. The 'mount' utility is used to graft a filesystem onto a
290 particular part of the tree. A filesystem can either live on a block
291 device, or it can be accessible over the network, as is the case with
292 NFS filesystems. Most people using BusyBox will also want to enable
293 the 'mount' utility.
294
295config CONFIG_NFSMOUNT
296 bool " Support mounting NFS file systems"
297 default n
298 depends on CONFIG_MOUNT
299 help
300 Enable mounting of NFS file systems.
301
302config CONFIG_UMOUNT
303 bool "umount"
304 default n
305 help
306 When you want to remove a mounted filesystem from its current mount point,
307 for example when you are shutting down the system, the 'umount' utility is
308 the tool to use. If you enabled the 'mount' utility, you almost certainly
309 also want to enable 'umount'.
310
311config CONFIG_FEATURE_MOUNT_FORCE
312 bool " Support forced filesystem unmounting"
313 default n
314 depends on CONFIG_UMOUNT
315 help
316 This allows you to _force_ a filesystem to be umounted. This is generally
317 only useful when you want to get rid of an unreachable NFS system.
318
319comment "Common options for mount/umount"
320 depends on CONFIG_MOUNT || CONFIG_UMOUNT
321
322config CONFIG_FEATURE_MOUNT_LOOP
323 bool " Support for loop devices"
324 default n
325 depends on CONFIG_MOUNT || CONFIG_UMOUNT
326 help
327 Enabling this feature allows mount to use the '-o' loop options,
328 which lets you loop mount files. Mount will automagically setup and
329 free the necessary loop devices so you do not need to mess with the
330 'losetup' utility unless you really want to. This is really
331 only useful if you plan to loop mount files.
332
333config CONFIG_FEATURE_MTAB_SUPPORT
334 bool " Support for a real /etc/mtab (instead of /proc/mounts)"
335 default n
336 depends on CONFIG_MOUNT || CONFIG_UMOUNT
337 help
338 If your root filesystem is writable and you wish to have the 'mount'
339 utility create an mtab file listing the filesystems which have been
340 mounted then you should enable this option. Most people that use
341 BusyBox have a read-only root filesystem, so they will leave this
342 option disabled and BusyBox will use the /proc/mounts file.
343
344config CONFIG_FEATURE_MTAB_FILENAME
345 string " mtab file location"
346 default "/etc/mtab"
347 depends on CONFIG_FEATURE_MTAB_SUPPORT
348 help
349 Some people have a read only root filesystem, but they also wish to
350 have the 'mount' utility create an mtab file listing the filesystems
351 which have been mounted. This option allows you to specify an alternative
352 location for the mtab file, such as /var/mtab, or /tmp/mtab. The default
353 value is /etc/mtab, which is where this file is located on most desktop
354 Linux systems.
355
356endmenu
357
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_buildddir=..
22srcdir=$(top_srcdir)/util-linux
23UTILLINUX_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
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 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20UTILLINUX_AR:=util-linux.a
21ifndef $(UTILLINUX_DIR)
22UTILLINUX_DIR:=$(top_builddir)/util-linux/
23endif
24srcdir=$(top_srcdir)/util-linux
25
26UTILLINUX-:=
27UTILLINUX-$(CONFIG_DMESG) +=dmesg.o
28UTILLINUX-$(CONFIG_FBSET) +=fbset.o
29UTILLINUX-$(CONFIG_FDFLUSH) +=fdflush.o
30UTILLINUX-$(CONFIG_FDFORMAT) +=fdformat.o
31UTILLINUX-$(CONFIG_FDISK) +=fdisk.o
32UTILLINUX-$(CONFIG_FREERAMDISK) +=freeramdisk.o
33UTILLINUX-$(CONFIG_FSCK_MINIX) +=fsck_minix.o
34UTILLINUX-$(CONFIG_GETOPT) +=getopt.o
35UTILLINUX-$(CONFIG_HEXDUMP) +=hexdump.o
36UTILLINUX-$(CONFIG_HWCLOCK) +=hwclock.o
37UTILLINUX-$(CONFIG_LOSETUP) +=losetup.o
38UTILLINUX-$(CONFIG_MKFS_MINIX) +=mkfs_minix.o
39UTILLINUX-$(CONFIG_MKSWAP) +=mkswap.o
40UTILLINUX-$(CONFIG_MORE) +=more.o
41UTILLINUX-$(CONFIG_MOUNT) +=mount.o
42UTILLINUX-$(CONFIG_NFSMOUNT) +=nfsmount.o
43UTILLINUX-$(CONFIG_PIVOT_ROOT) +=pivot_root.o
44UTILLINUX-$(CONFIG_RDATE) +=rdate.o
45UTILLINUX-$(CONFIG_SWAPONOFF) +=swaponoff.o
46UTILLINUX-$(CONFIG_UMOUNT) +=umount.o
47
48libraries-y+=$(UTILLINUX_DIR)$(UTILLINUX_AR)
49
50$(UTILLINUX_DIR)$(UTILLINUX_AR): $(patsubst %,$(UTILLINUX_DIR)%, $(UTILLINUX-y))
51 $(AR) -ro $@ $(patsubst %,$(UTILLINUX_DIR)%, $(UTILLINUX-y))
52
53$(UTILLINUX_DIR)%.o: $(srcdir)/%.c
54 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
55
56ifneq ($(strip $(CONFIG_LFS)),y)
57ifeq ($(strip $(FDISK_SUPPORT_LARGE_DISKS)),y)
58
59$(UTILLINUX_DIR)fdisk.o: $(srcdir)/fdisk.c
60 $(CC) $(CFLAGS) \
61 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 \
62 $(EXTRA_CFLAGS) -c -o $@ $<
63
64endif
65endif
66
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 @@
1/* vi: set sw=4 ts=4: */
2/* dmesg.c -- Print out the contents of the kernel ring buffer
3 * Created: Sat Oct 9 16:19:47 1993
4 * Revised: Thu Oct 28 21:52:17 1993 by faith@cs.unc.edu
5 * Copyright 1993 Theodore Ts'o (tytso@athena.mit.edu)
6 * This program comes with ABSOLUTELY NO WARRANTY.
7 * Modifications by Rick Sladkey (jrs@world.std.com)
8 * Larger buffersize 3 June 1998 by Nicolai Langfeldt, based on a patch
9 * by Peeter Joot. This was also suggested by John Hudson.
10 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
11 * - added Native Language Support
12 *
13 * from util-linux -- adapted for busybox by
14 * Erik Andersen <andersen@codepoet.org>. I ripped out Native Language
15 * Support, replaced getopt, added some gotos for redundant stuff.
16 *
17 * Audited and cleaned up on 7 March 2003 to reduce size of
18 * check error handling by Erik Andersen <andersen@codepoet.org>
19 */
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <getopt.h>
24#include <errno.h>
25#include <sys/klog.h>
26
27#include "busybox.h"
28
29int dmesg_main(int argc, char **argv)
30{
31 char *buf
32#ifdef CONFIG_FEATURE_CLEAN_UP
33 = NULL
34#endif
35 ;
36 int bufsize = 8196;
37 int i, n;
38 int level = 0;
39 int lastc;
40 int cmd = 3;
41
42 while ((i = getopt(argc, argv, "cn:s:")) > 0) {
43 switch (i) {
44 case 'c':
45 cmd = 4;
46 break;
47 case 'n':
48 cmd = 8;
49 level = bb_xgetlarg(optarg, 10, 0, 10);
50 break;
51 case 's':
52 /* I think a 512k max kernel ring buffer is big enough for
53 * anybody, as the default is 16k... Could be wrong though.
54 * If so I'm sure I'll hear about it by the enraged masses*/
55 bufsize = bb_xgetlarg(optarg, 10, 4096, 512*1024);
56 break;
57 default:
58 bb_show_usage();
59 }
60 }
61
62 if (optind < argc) {
63 bb_show_usage();
64 }
65
66 if (cmd == 8) {
67 if (klogctl(cmd, NULL, level) < 0)
68 goto die_the_death;
69 goto all_done;
70 }
71
72 buf = xmalloc(bufsize);
73 if ((n = klogctl(cmd, buf, bufsize)) < 0)
74 goto die_the_death;
75
76 lastc = '\n';
77 for (i = 0; i < n; i++) {
78 if (lastc == '\n' && buf[i] == '<') {
79 i++;
80 while (buf[i] >= '0' && buf[i] <= '9')
81 i++;
82 if (buf[i] == '>')
83 i++;
84 }
85 lastc = buf[i];
86 putchar(lastc);
87 }
88 if (lastc != '\n')
89 putchar('\n');
90all_done:
91#ifdef CONFIG_FEATURE_CLEAN_UP
92 if (buf) {
93 free(buf);
94 }
95#endif
96 return EXIT_SUCCESS;
97die_the_death:
98 bb_perror_nomsg_and_die();
99}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini fbset implementation for busybox
4 *
5 * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 * This is a from-scratch implementation of fbset; but the de facto fbset
22 * implementation was a good reference. fbset (original) is released under
23 * the GPL, and is (c) 1995-1999 by:
24 * Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be)
25 */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <unistd.h>
30#include <fcntl.h>
31#include <errno.h>
32#include <ctype.h>
33#include <string.h>
34#include <stdint.h>
35#include <sys/ioctl.h>
36#include "busybox.h"
37
38#define DEFAULTFBDEV FB_0
39#define DEFAULTFBMODE "/etc/fb.modes"
40
41static const int OPT_CHANGE = (1 << 0);
42static const int OPT_INFO = (1 << 1);
43static const int OPT_READMODE = (1 << 2);
44
45enum {
46 CMD_FB = 1,
47 CMD_DB = 2,
48 CMD_GEOMETRY = 3,
49 CMD_TIMING = 4,
50 CMD_ACCEL = 5,
51 CMD_HSYNC = 6,
52 CMD_VSYNC = 7,
53 CMD_LACED = 8,
54 CMD_DOUBLE = 9,
55/* CMD_XCOMPAT = 10, */
56 CMD_ALL = 11,
57 CMD_INFO = 12,
58 CMD_CHANGE = 13,
59
60#ifdef CONFIG_FEATURE_FBSET_FANCY
61 CMD_XRES = 100,
62 CMD_YRES = 101,
63 CMD_VXRES = 102,
64 CMD_VYRES = 103,
65 CMD_DEPTH = 104,
66 CMD_MATCH = 105,
67 CMD_PIXCLOCK = 106,
68 CMD_LEFT = 107,
69 CMD_RIGHT = 108,
70 CMD_UPPER = 109,
71 CMD_LOWER = 110,
72 CMD_HSLEN = 111,
73 CMD_VSLEN = 112,
74 CMD_CSYNC = 113,
75 CMD_GSYNC = 114,
76 CMD_EXTSYNC = 115,
77 CMD_BCAST = 116,
78 CMD_RGBA = 117,
79 CMD_STEP = 118,
80 CMD_MOVE = 119,
81#endif
82};
83
84static unsigned int g_options = 0;
85
86/* Stuff stolen from the kernel's fb.h */
87static const int FBIOGET_VSCREENINFO = 0x4600;
88static const int FBIOPUT_VSCREENINFO = 0x4601;
89struct fb_bitfield {
90 uint32_t offset; /* beginning of bitfield */
91 uint32_t length; /* length of bitfield */
92 uint32_t msb_right; /* != 0 : Most significant bit is */
93 /* right */
94};
95struct fb_var_screeninfo {
96 uint32_t xres; /* visible resolution */
97 uint32_t yres;
98 uint32_t xres_virtual; /* virtual resolution */
99 uint32_t yres_virtual;
100 uint32_t xoffset; /* offset from virtual to visible */
101 uint32_t yoffset; /* resolution */
102
103 uint32_t bits_per_pixel; /* guess what */
104 uint32_t grayscale; /* != 0 Graylevels instead of colors */
105
106 struct fb_bitfield red; /* bitfield in fb mem if true color, */
107 struct fb_bitfield green; /* else only length is significant */
108 struct fb_bitfield blue;
109 struct fb_bitfield transp; /* transparency */
110
111 uint32_t nonstd; /* != 0 Non standard pixel format */
112
113 uint32_t activate; /* see FB_ACTIVATE_* */
114
115 uint32_t height; /* height of picture in mm */
116 uint32_t width; /* width of picture in mm */
117
118 uint32_t accel_flags; /* acceleration flags (hints) */
119
120 /* Timing: All values in pixclocks, except pixclock (of course) */
121 uint32_t pixclock; /* pixel clock in ps (pico seconds) */
122 uint32_t left_margin; /* time from sync to picture */
123 uint32_t right_margin; /* time from picture to sync */
124 uint32_t upper_margin; /* time from sync to picture */
125 uint32_t lower_margin;
126 uint32_t hsync_len; /* length of horizontal sync */
127 uint32_t vsync_len; /* length of vertical sync */
128 uint32_t sync; /* see FB_SYNC_* */
129 uint32_t vmode; /* see FB_VMODE_* */
130 uint32_t reserved[6]; /* Reserved for future compatibility */
131};
132
133
134const static struct cmdoptions_t {
135 const char *name;
136 const unsigned char param_count;
137 const unsigned char code;
138} g_cmdoptions[] = {
139 {
140 "-fb", 1, CMD_FB}, {
141 "-db", 1, CMD_DB}, {
142 "-a", 0, CMD_ALL}, {
143 "-i", 0, CMD_INFO}, {
144 "-g", 5, CMD_GEOMETRY}, {
145 "-t", 7, CMD_TIMING}, {
146 "-accel", 1, CMD_ACCEL}, {
147 "-hsync", 1, CMD_HSYNC}, {
148 "-vsync", 1, CMD_VSYNC}, {
149 "-laced", 1, CMD_LACED}, {
150 "-double", 1, CMD_DOUBLE}, {
151 "-n", 0, CMD_CHANGE}, {
152#ifdef CONFIG_FEATURE_FBSET_FANCY
153 "-all", 0, CMD_ALL}, {
154 "-xres", 1, CMD_XRES}, {
155 "-yres", 1, CMD_YRES}, {
156 "-vxres", 1, CMD_VXRES}, {
157 "-vyres", 1, CMD_VYRES}, {
158 "-depth", 1, CMD_DEPTH}, {
159 "-match", 0, CMD_MATCH}, {
160 "-geometry", 5, CMD_GEOMETRY}, {
161 "-pixclock", 1, CMD_PIXCLOCK}, {
162 "-left", 1, CMD_LEFT}, {
163 "-right", 1, CMD_RIGHT}, {
164 "-upper", 1, CMD_UPPER}, {
165 "-lower", 1, CMD_LOWER}, {
166 "-hslen", 1, CMD_HSLEN}, {
167 "-vslen", 1, CMD_VSLEN}, {
168 "-timings", 7, CMD_TIMING}, {
169 "-csync", 1, CMD_CSYNC}, {
170 "-gsync", 1, CMD_GSYNC}, {
171 "-extsync", 1, CMD_EXTSYNC}, {
172 "-bcast", 1, CMD_BCAST}, {
173 "-rgba", 1, CMD_RGBA}, {
174 "-step", 1, CMD_STEP}, {
175 "-move", 1, CMD_MOVE}, {
176#endif
177 0, 0, 0}
178};
179
180#ifdef CONFIG_FEATURE_FBSET_READMODE
181/* taken from linux/fb.h */
182static const int FB_VMODE_INTERLACED = 1; /* interlaced */
183static const int FB_VMODE_DOUBLE = 2; /* double scan */
184static const int FB_SYNC_HOR_HIGH_ACT = 1; /* horizontal sync high active */
185static const int FB_SYNC_VERT_HIGH_ACT = 2; /* vertical sync high active */
186static const int FB_SYNC_EXT = 4; /* external sync */
187static const int FB_SYNC_COMP_HIGH_ACT = 8; /* composite sync high active */
188#endif
189static int readmode(struct fb_var_screeninfo *base, const char *fn,
190 const char *mode)
191{
192#ifdef CONFIG_FEATURE_FBSET_READMODE
193 FILE *f;
194 char buf[256];
195 char *p = buf;
196
197 f = bb_xfopen(fn, "r");
198 while (!feof(f)) {
199 fgets(buf, sizeof(buf), f);
200 if ((p = strstr(buf, "mode ")) || (p = strstr(buf, "mode\t"))) {
201 p += 5;
202 if ((p = strstr(buf, mode))) {
203 p += strlen(mode);
204 if (!isspace(*p) && (*p != 0) && (*p != '"')
205 && (*p != '\r') && (*p != '\n'))
206 continue; /* almost, but not quite */
207 while (!feof(f)) {
208 fgets(buf, sizeof(buf), f);
209
210 if ((p = strstr(buf, "geometry "))) {
211 p += 9;
212
213 sscanf(p, "%d %d %d %d %d",
214 &(base->xres), &(base->yres),
215 &(base->xres_virtual), &(base->yres_virtual),
216 &(base->bits_per_pixel));
217 } else if ((p = strstr(buf, "timings "))) {
218 p += 8;
219
220 sscanf(p, "%d %d %d %d %d %d %d",
221 &(base->pixclock),
222 &(base->left_margin), &(base->right_margin),
223 &(base->upper_margin), &(base->lower_margin),
224 &(base->hsync_len), &(base->vsync_len));
225 } else if ((p = strstr(buf, "laced "))) {
226 p += 6;
227
228 if (strstr(buf, "false")) {
229 base->vmode &= ~FB_VMODE_INTERLACED;
230 } else {
231 base->vmode |= FB_VMODE_INTERLACED;
232 }
233 } else if ((p = strstr(buf, "double "))) {
234 p += 7;
235
236 if (strstr(buf, "false")) {
237 base->vmode &= ~FB_VMODE_DOUBLE;
238 } else {
239 base->vmode |= FB_VMODE_DOUBLE;
240 }
241 } else if ((p = strstr(buf, "vsync "))) {
242 p += 6;
243
244 if (strstr(buf, "low")) {
245 base->sync &= ~FB_SYNC_VERT_HIGH_ACT;
246 } else {
247 base->sync |= FB_SYNC_VERT_HIGH_ACT;
248 }
249 } else if ((p = strstr(buf, "hsync "))) {
250 p += 6;
251
252 if (strstr(buf, "low")) {
253 base->sync &= ~FB_SYNC_HOR_HIGH_ACT;
254 } else {
255 base->sync |= FB_SYNC_HOR_HIGH_ACT;
256 }
257 } else if ((p = strstr(buf, "csync "))) {
258 p += 6;
259
260 if (strstr(buf, "low")) {
261 base->sync &= ~FB_SYNC_COMP_HIGH_ACT;
262 } else {
263 base->sync |= FB_SYNC_COMP_HIGH_ACT;
264 }
265 } else if ((p = strstr(buf, "extsync "))) {
266 p += 8;
267
268 if (strstr(buf, "false")) {
269 base->sync &= ~FB_SYNC_EXT;
270 } else {
271 base->sync |= FB_SYNC_EXT;
272 }
273 }
274
275 if (strstr(buf, "endmode"))
276 return 1;
277 }
278 }
279 }
280 }
281#else
282 bb_error_msg( "mode reading not compiled in");
283#endif
284 return 0;
285}
286
287static inline void setmode(struct fb_var_screeninfo *base,
288 struct fb_var_screeninfo *set)
289{
290 if ((int) set->xres > 0)
291 base->xres = set->xres;
292 if ((int) set->yres > 0)
293 base->yres = set->yres;
294 if ((int) set->xres_virtual > 0)
295 base->xres_virtual = set->xres_virtual;
296 if ((int) set->yres_virtual > 0)
297 base->yres_virtual = set->yres_virtual;
298 if ((int) set->bits_per_pixel > 0)
299 base->bits_per_pixel = set->bits_per_pixel;
300}
301
302static inline void showmode(struct fb_var_screeninfo *v)
303{
304 double drate = 0, hrate = 0, vrate = 0;
305
306 if (v->pixclock) {
307 drate = 1e12 / v->pixclock;
308 hrate =
309 drate / (v->left_margin + v->xres + v->right_margin +
310 v->hsync_len);
311 vrate =
312 hrate / (v->upper_margin + v->yres + v->lower_margin +
313 v->vsync_len);
314 }
315 printf("\nmode \"%ux%u-%u\"\n"
316#ifdef CONFIG_FEATURE_FBSET_FANCY
317 "\t# D: %.3f MHz, H: %.3f kHz, V: %.3f Hz\n"
318#endif
319 "\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",
320 v->xres, v->yres, (int) (vrate + 0.5),
321#ifdef CONFIG_FEATURE_FBSET_FANCY
322 drate / 1e6, hrate / 1e3, vrate,
323#endif
324 v->xres, v->yres, v->xres_virtual, v->yres_virtual,
325 v->bits_per_pixel, v->pixclock, v->left_margin,
326 v->right_margin, v->upper_margin, v->lower_margin, v->hsync_len,
327 v->vsync_len, (v->accel_flags > 0 ? "true" : "false"), v->red.length,
328 v->red.offset, v->green.length, v->green.offset, v->blue.length,
329 v->blue.offset, v->transp.length, v->transp.offset);
330}
331
332#ifdef STANDALONE
333int main(int argc, char **argv)
334#else
335extern int fbset_main(int argc, char **argv)
336#endif
337{
338 struct fb_var_screeninfo var, varset;
339 int fh, i;
340 char *fbdev = DEFAULTFBDEV;
341 char *modefile = DEFAULTFBMODE;
342 char *thisarg, *mode = NULL;
343
344 memset(&varset, 0xFF, sizeof(varset));
345
346 /* parse cmd args.... why do they have to make things so difficult? */
347 argv++;
348 argc--;
349 for (; argc > 0 && (thisarg = *argv); argc--, argv++) {
350 for (i = 0; g_cmdoptions[i].name; i++) {
351 if (!strcmp(thisarg, g_cmdoptions[i].name)) {
352 if (argc - 1 < g_cmdoptions[i].param_count)
353 bb_show_usage();
354 switch (g_cmdoptions[i].code) {
355 case CMD_FB:
356 fbdev = argv[1];
357 break;
358 case CMD_DB:
359 modefile = argv[1];
360 break;
361 case CMD_GEOMETRY:
362 varset.xres = strtoul(argv[1], 0, 0);
363 varset.yres = strtoul(argv[2], 0, 0);
364 varset.xres_virtual = strtoul(argv[3], 0, 0);
365 varset.yres_virtual = strtoul(argv[4], 0, 0);
366 varset.bits_per_pixel = strtoul(argv[5], 0, 0);
367 break;
368 case CMD_TIMING:
369 varset.pixclock = strtoul(argv[1], 0, 0);
370 varset.left_margin = strtoul(argv[2], 0, 0);
371 varset.right_margin = strtoul(argv[3], 0, 0);
372 varset.upper_margin = strtoul(argv[4], 0, 0);
373 varset.lower_margin = strtoul(argv[5], 0, 0);
374 varset.hsync_len = strtoul(argv[6], 0, 0);
375 varset.vsync_len = strtoul(argv[7], 0, 0);
376 break;
377 case CMD_CHANGE:
378 g_options |= OPT_CHANGE;
379 break;
380#ifdef CONFIG_FEATURE_FBSET_FANCY
381 case CMD_XRES:
382 varset.xres = strtoul(argv[1], 0, 0);
383 break;
384 case CMD_YRES:
385 varset.yres = strtoul(argv[1], 0, 0);
386 break;
387 case CMD_DEPTH:
388 varset.bits_per_pixel = strtoul(argv[1], 0, 0);
389 break;
390#endif
391 }
392 argc -= g_cmdoptions[i].param_count;
393 argv += g_cmdoptions[i].param_count;
394 break;
395 }
396 }
397 if (!g_cmdoptions[i].name) {
398 if (argc == 1) {
399 mode = *argv;
400 g_options |= OPT_READMODE;
401 } else {
402 bb_show_usage();
403 }
404 }
405 }
406
407 if ((fh = open(fbdev, O_RDONLY)) < 0)
408 bb_perror_msg_and_die("fbset(open)");
409 if (ioctl(fh, FBIOGET_VSCREENINFO, &var))
410 bb_perror_msg_and_die("fbset(ioctl)");
411 if (g_options & OPT_READMODE) {
412 if (!readmode(&var, modefile, mode)) {
413 bb_error_msg("Unknown video mode `%s'", mode);
414 return EXIT_FAILURE;
415 }
416 }
417
418 setmode(&var, &varset);
419 if (g_options & OPT_CHANGE)
420 if (ioctl(fh, FBIOPUT_VSCREENINFO, &var))
421 bb_perror_msg_and_die("fbset(ioctl)");
422 showmode(&var);
423 /* Don't close the file, as exiting will take care of that */
424 /* close(fh); */
425
426 return EXIT_SUCCESS;
427}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini fdflush implementation for busybox
4 *
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@perens.com>.
6 * Copyright (C) 2003 by Erik Andersen <andersen@codeoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <stdio.h>
25#include <sys/ioctl.h>
26#include <fcntl.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include "busybox.h"
30
31/* From <linux/fd.h> */
32#define FDFLUSH _IO(2,0x4b)
33
34extern int fdflush_main(int argc, char **argv)
35{
36 int fd, result;
37
38 if (argc <= 1)
39 bb_show_usage();
40
41 fd = bb_xopen(argv[1], 0);
42
43 result = ioctl(fd, FDFLUSH, 0);
44#ifdef CONFIG_FEATURE_CLEAN_UP
45 close(fd);
46#endif
47 if (result) {
48 bb_perror_nomsg_and_die();
49 }
50
51 /* Don't bother closing. Exit does
52 * that, so we can save a few bytes */
53 return EXIT_SUCCESS;
54}
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 @@
1/* fdformat.c - Low-level formats a floppy disk - Werner Almesberger */
2
3/* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
4 * - added Native Language Support
5 * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
6 * - more i18n/nls translatable strings marked
7 *
8 * 5 July 2003 -- modified for Busybox by Erik Andersen
9 */
10
11#include <stdio.h>
12#include <string.h>
13#include <fcntl.h>
14#include <errno.h>
15#include <unistd.h>
16#include <stdlib.h>
17#include <sys/stat.h>
18#include <sys/ioctl.h>
19#include "busybox.h"
20
21
22/* Stuff extracted from linux/fd.h */
23struct floppy_struct {
24 unsigned int size, /* nr of sectors total */
25 sect, /* sectors per track */
26 head, /* nr of heads */
27 track, /* nr of tracks */
28 stretch; /* !=0 means double track steps */
29#define FD_STRETCH 1
30#define FD_SWAPSIDES 2
31
32 unsigned char gap, /* gap1 size */
33
34 rate, /* data rate. |= 0x40 for perpendicular */
35#define FD_2M 0x4
36#define FD_SIZECODEMASK 0x38
37#define FD_SIZECODE(floppy) (((((floppy)->rate&FD_SIZECODEMASK)>> 3)+ 2) %8)
38#define FD_SECTSIZE(floppy) ( (floppy)->rate & FD_2M ? \
39 512 : 128 << FD_SIZECODE(floppy) )
40#define FD_PERP 0x40
41
42 spec1, /* stepping rate, head unload time */
43 fmt_gap; /* gap2 size */
44 const char * name; /* used only for predefined formats */
45};
46struct format_descr {
47 unsigned int device,head,track;
48};
49#define FDFMTBEG _IO(2,0x47)
50#define FDFMTTRK _IOW(2,0x48, struct format_descr)
51#define FDFMTEND _IO(2,0x49)
52#define FDGETPRM _IOR(2, 0x04, struct floppy_struct)
53#define FD_FILL_BYTE 0xF6 /* format fill byte. */
54
55static void print_and_flush(const char * __restrict format, ...)
56{
57 va_list arg;
58
59 va_start(arg, format);
60 bb_vfprintf(stdout, format, arg);
61 va_end(arg);
62 bb_xfflush_stdout();
63}
64
65static void bb_xioctl(int fd, int request, void *argp, const char *string)
66{
67 if (ioctl (fd, request, argp) < 0) {
68 bb_perror_msg_and_die(string);
69 }
70}
71
72int fdformat_main(int argc,char **argv)
73{
74 int fd, n, cyl, read_bytes, verify;
75 unsigned char *data;
76 struct stat st;
77 struct floppy_struct param;
78 struct format_descr descr;
79
80 if (argc < 2) {
81 bb_show_usage();
82 }
83 verify = !bb_getopt_ulflags(argc, argv, "n");
84 argv += optind;
85
86 /* R_OK is needed for verifying */
87 if (stat(*argv,&st) < 0 || access(*argv,W_OK | R_OK ) < 0) {
88 bb_perror_msg_and_die(*argv);
89 }
90 if (!S_ISBLK(st.st_mode)) {
91 bb_error_msg_and_die("%s: not a block device",*argv);
92 /* do not test major - perhaps this was an USB floppy */
93 }
94
95
96 /* O_RDWR for formatting and verifying */
97 fd = bb_xopen(*argv,O_RDWR );
98
99 bb_xioctl(fd, FDGETPRM, &param, "FDGETPRM");/*original message was: "Could not determine current format type" */
100
101 print_and_flush("%s-sided, %d tracks, %d sec/track. Total capacity %d kB.\n",
102 (param.head == 2) ? "Double" : "Single",
103 param.track, param.sect, param.size >> 1);
104
105 /* FORMAT */
106 print_and_flush("Formatting ... ", NULL);
107 bb_xioctl(fd, FDFMTBEG,NULL,"FDFMTBEG");
108
109 /* n == track */
110 for (n = 0; n < param.track; n++)
111 {
112 descr.head = 0;
113 descr.track = n;
114 bb_xioctl(fd, FDFMTTRK,&descr,"FDFMTTRK");
115 print_and_flush("%3d\b\b\b", n);
116 if (param.head == 2) {
117 descr.head = 1;
118 bb_xioctl(fd, FDFMTTRK,&descr,"FDFMTTRK");
119 }
120 }
121
122 bb_xioctl(fd,FDFMTEND,NULL,"FDFMTEND");
123 print_and_flush("done\n", NULL);
124
125 /* VERIFY */
126 if(verify) {
127 /* n == cyl_size */
128 n = param.sect*param.head*512;
129
130 data = xmalloc(n);
131 print_and_flush("Verifying ... ", NULL);
132 for (cyl = 0; cyl < param.track; cyl++) {
133 print_and_flush("%3d\b\b\b", cyl);
134 if((read_bytes = safe_read(fd,data,n))!= n ) {
135 if(read_bytes < 0) {
136 bb_perror_msg("Read: ");
137 }
138 bb_error_msg_and_die("Problem reading cylinder %d, expected %d, read %d", cyl, n, read_bytes);
139 }
140 /* Check backwards so we don't need a counter */
141 while(--read_bytes>=0) {
142 if( data[read_bytes] != FD_FILL_BYTE) {
143 print_and_flush("bad data in cyl %d\nContinuing ... ",cyl);
144 }
145 }
146 }
147 /* There is no point in freeing blocks at the end of a program, because
148 all of the program's space is given back to the system when the process
149 terminates.*/
150#ifdef CONFIG_FEATURE_CLEAN_UP
151 free(data);
152#endif
153 print_and_flush("done\n", NULL);
154 }
155#ifdef CONFIG_FEATURE_CLEAN_UP
156 close(fd);
157#endif
158 /* Don't bother closing. Exit does
159 * that, so we can save a few bytes */
160 return EXIT_SUCCESS;
161}
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 @@
1/* fdisk.c -- Partition table manipulator for Linux.
2 *
3 * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk)
4 *
5 * This program is free software. You can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation: either version 1 or
8 * (at your option) any later version.
9 *
10 * Vladimir Oleynik <dzo@simtreas.ru> 2001,2002 Busybox port
11 */
12
13#define UTIL_LINUX_VERSION "2.12"
14
15#define PROC_PARTITIONS "/proc/partitions"
16
17#include <features.h>
18#include <sys/types.h>
19#include <sys/stat.h> /* stat */
20#include <ctype.h>
21#include <stdlib.h>
22#include <stdio.h>
23#include <string.h>
24#include <errno.h>
25#include <unistd.h>
26#include <fcntl.h>
27#include <setjmp.h>
28#include <assert.h> /* assert */
29#include <getopt.h>
30#include <endian.h>
31#include <sys/ioctl.h>
32#include <sys/param.h>
33#include <sys/sysmacros.h> /* major */
34
35#include <stdint.h> /* for uint32_t, uint16_t, uint8_t, int16_t, etc */
36
37/* Copied from linux/major.h */
38#define FLOPPY_MAJOR 2
39
40#include <sys/utsname.h>
41
42#include "busybox.h"
43
44#define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r))
45
46#define DKTYPENAMES
47
48#define _(Text) Text
49
50#define BLKRRPART _IO(0x12,95) /* re-read partition table */
51#define BLKGETSIZE _IO(0x12,96) /* return device size */
52#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */
53#define BLKSSZGET _IO(0x12,104) /* get block device sector size */
54
55/* Avoid conflicts with the 2.6 kernel headers, which define
56 * _IOR rather differently */
57#undef _IOR
58#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
59#define BLKGETSIZE64 _IOR(0x12,114,uint64_t)
60
61/*
62 fdisk.h
63*/
64
65#define DEFAULT_SECTOR_SIZE 512
66#define MAX_SECTOR_SIZE 2048
67#define SECTOR_SIZE 512 /* still used in BSD code */
68#define MAXIMUM_PARTS 60
69
70#define ACTIVE_FLAG 0x80
71
72#define EXTENDED 0x05
73#define WIN98_EXTENDED 0x0f
74#define LINUX_PARTITION 0x81
75#define LINUX_SWAP 0x82
76#define LINUX_NATIVE 0x83
77#define LINUX_EXTENDED 0x85
78#define LINUX_LVM 0x8e
79#define LINUX_RAID 0xfd
80
81#define SUNOS_SWAP 3
82#define WHOLE_DISK 5
83
84#define IS_EXTENDED(i) \
85 ((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED)
86
87#define SIZE(a) (sizeof(a)/sizeof((a)[0]))
88
89#define cround(n) (display_in_cyl_units ? ((n)/units_per_sector)+1 : (n))
90#define scround(x) (((x)+units_per_sector-1)/units_per_sector)
91
92#ifdef CONFIG_FEATURE_SUN_LABEL
93#define SCSI_IOCTL_GET_IDLUN 0x5382
94#endif
95
96
97/* including <linux/hdreg.h> also fails */
98struct hd_geometry {
99 unsigned char heads;
100 unsigned char sectors;
101 unsigned short cylinders;
102 unsigned long start;
103};
104
105#define HDIO_GETGEO 0x0301 /* get device geometry */
106
107
108struct systypes {
109 const unsigned char *name;
110};
111
112static uint sector_size = DEFAULT_SECTOR_SIZE,
113 user_set_sector_size,
114 sector_offset = 1;
115
116/*
117 * Raw disk label. For DOS-type partition tables the MBR,
118 * with descriptions of the primary partitions.
119 */
120static char MBRbuffer[MAX_SECTOR_SIZE];
121
122#ifdef CONFIG_FEATURE_SUN_LABEL
123static int sun_label; /* looking at sun disklabel */
124#else
125#define sun_label 0
126#endif
127#ifdef CONFIG_FEATURE_SGI_LABEL
128static int sgi_label; /* looking at sgi disklabel */
129#else
130#define sgi_label 0
131#endif
132#ifdef CONFIG_FEATURE_AIX_LABEL
133static int aix_label; /* looking at aix disklabel */
134#else
135#define aix_label 0
136#endif
137#ifdef CONFIG_FEATURE_OSF_LABEL
138static int osf_label; /* looking at OSF/1 disklabel */
139static int possibly_osf_label;
140#else
141#define osf_label 0
142#endif
143
144#define dos_label (!sun_label && !sgi_label && !aix_label && !osf_label)
145
146static uint heads, sectors, cylinders;
147static void update_units(void);
148
149
150/*
151 * return partition name - uses static storage unless buf is supplied
152 */
153static const char *
154partname(const char *dev, int pno, int lth) {
155 static char buffer[80];
156 const char *p;
157 int w, wp;
158 int bufsiz;
159 char *bufp;
160
161 bufp = buffer;
162 bufsiz = sizeof(buffer);
163
164 w = strlen(dev);
165 p = "";
166
167 if (isdigit(dev[w-1]))
168 p = "p";
169
170 /* devfs kludge - note: fdisk partition names are not supposed
171 to equal kernel names, so there is no reason to do this */
172 if (strcmp (dev + w - 4, "disc") == 0) {
173 w -= 4;
174 p = "part";
175 }
176
177 wp = strlen(p);
178
179 if (lth) {
180 snprintf(bufp, bufsiz, "%*.*s%s%-2u",
181 lth-wp-2, w, dev, p, pno);
182 } else {
183 snprintf(bufp, bufsiz, "%.*s%s%-2u", w, dev, p, pno);
184 }
185 return bufp;
186}
187
188struct partition {
189 unsigned char boot_ind; /* 0x80 - active */
190 unsigned char head; /* starting head */
191 unsigned char sector; /* starting sector */
192 unsigned char cyl; /* starting cylinder */
193 unsigned char sys_ind; /* What partition type */
194 unsigned char end_head; /* end head */
195 unsigned char end_sector; /* end sector */
196 unsigned char end_cyl; /* end cylinder */
197 unsigned char start4[4]; /* starting sector counting from 0 */
198 unsigned char size4[4]; /* nr of sectors in partition */
199} __attribute__((__packed__));
200
201enum failure {
202 ioctl_error, unable_to_open, unable_to_read, unable_to_seek,
203 unable_to_write
204};
205
206enum action {fdisk, require, try_only, create_empty_dos, create_empty_sun};
207
208static const char *disk_device;
209static int fd; /* the disk */
210static int partitions = 4; /* maximum partition + 1 */
211static uint display_in_cyl_units = 1;
212static uint units_per_sector = 1;
213#ifdef CONFIG_FEATURE_FDISK_WRITABLE
214static char *line_ptr;
215static void change_units(void);
216static void reread_partition_table(int leave);
217static void delete_partition(int i);
218static int get_partition(int warn, int max);
219static void list_types(const struct systypes *sys);
220static uint read_int(uint low, uint dflt, uint high, uint base, char *mesg);
221#endif
222static const char *partition_type(unsigned char type);
223static void fdisk_fatal(enum failure why) __attribute__ ((noreturn));
224static void get_geometry(void);
225static int get_boot(enum action what);
226
227#define PLURAL 0
228#define SINGULAR 1
229
230#define hex_val(c) ({ \
231 char _c = (c); \
232 isdigit(_c) ? _c - '0' : \
233 tolower(_c) + 10 - 'a'; \
234 })
235
236
237#define LINE_LENGTH 800
238#define pt_offset(b, n) ((struct partition *)((b) + 0x1be + \
239 (n) * sizeof(struct partition)))
240#define sector(s) ((s) & 0x3f)
241#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
242
243#define hsc2sector(h,s,c) (sector(s) - 1 + sectors * \
244 ((h) + heads * cylinder(s,c)))
245#define set_hsc(h,s,c,sector) { \
246 s = sector % sectors + 1; \
247 sector /= sectors; \
248 h = sector % heads; \
249 sector /= heads; \
250 c = sector & 0xff; \
251 s |= (sector >> 2) & 0xc0; \
252 }
253
254
255static int32_t get_start_sect(const struct partition *p);
256static int32_t get_nr_sects(const struct partition *p);
257
258/*
259 * per partition table entry data
260 *
261 * The four primary partitions have the same sectorbuffer (MBRbuffer)
262 * and have NULL ext_pointer.
263 * Each logical partition table entry has two pointers, one for the
264 * partition and one link to the next one.
265 */
266static struct pte {
267 struct partition *part_table; /* points into sectorbuffer */
268 struct partition *ext_pointer; /* points into sectorbuffer */
269#ifdef CONFIG_FEATURE_FDISK_WRITABLE
270 char changed; /* boolean */
271#endif
272 off_t offset; /* disk sector number */
273 char *sectorbuffer; /* disk sector contents */
274} ptes[MAXIMUM_PARTS];
275
276
277#ifdef CONFIG_FEATURE_FDISK_WRITABLE
278static void
279set_all_unchanged(void) {
280 int i;
281
282 for (i = 0; i < MAXIMUM_PARTS; i++)
283 ptes[i].changed = 0;
284}
285
286static void
287set_changed(int i) {
288 ptes[i].changed = 1;
289}
290#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
291
292#if defined(CONFIG_FEATURE_SGI_LABEL) || defined(CONFIG_FEATURE_OSF_LABEL)
293static struct partition *
294get_part_table(int i) {
295 return ptes[i].part_table;
296}
297#endif
298
299static const char *
300str_units(int n) { /* n==1: use singular */
301 if (n == 1)
302 return display_in_cyl_units ? _("cylinder") : _("sector");
303 else
304 return display_in_cyl_units ? _("cylinders") : _("sectors");
305}
306
307static int
308valid_part_table_flag(const unsigned char *b) {
309 return (b[510] == 0x55 && b[511] == 0xaa);
310}
311
312#ifdef CONFIG_FEATURE_FDISK_WRITABLE
313static char line_buffer[LINE_LENGTH];
314
315/* read line; return 0 or first char */
316static int
317read_line(void)
318{
319 static int got_eof = 0;
320
321 fflush (stdout); /* requested by niles@scyld.com */
322 line_ptr = line_buffer;
323 if (!fgets(line_buffer, LINE_LENGTH, stdin)) {
324 if (feof(stdin))
325 got_eof++; /* user typed ^D ? */
326 if (got_eof >= 3) {
327 fprintf(stderr, _("\ngot EOF thrice - exiting..\n"));
328 exit(1);
329 }
330 return 0;
331 }
332 while (*line_ptr && !isgraph(*line_ptr))
333 line_ptr++;
334 return *line_ptr;
335}
336
337static char
338read_char(const char *mesg)
339{
340 do {
341 fputs(mesg, stdout);
342 } while (!read_line());
343 return *line_ptr;
344}
345
346static char
347read_chars(const char *mesg)
348{
349 fputs(mesg, stdout);
350 if (!read_line()) {
351 *line_ptr = '\n';
352 line_ptr[1] = 0;
353 }
354 return *line_ptr;
355}
356
357static int
358read_hex(const struct systypes *sys)
359{
360 int hex;
361
362 while (1)
363 {
364 read_char(_("Hex code (type L to list codes): "));
365 if (*line_ptr == 'l' || *line_ptr == 'L')
366 list_types(sys);
367 else if (isxdigit (*line_ptr))
368 {
369 hex = 0;
370 do
371 hex = hex << 4 | hex_val(*line_ptr++);
372 while (isxdigit(*line_ptr));
373 return hex;
374 }
375 }
376}
377#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
378
379#ifdef CONFIG_FEATURE_AIX_LABEL
380/*
381 * Copyright (C) Andreas Neuper, Sep 1998.
382 * This file may be redistributed under
383 * the terms of the GNU Public License.
384 */
385
386typedef struct {
387 unsigned int magic; /* expect AIX_LABEL_MAGIC */
388 unsigned int fillbytes1[124];
389 unsigned int physical_volume_id;
390 unsigned int fillbytes2[124];
391} aix_partition;
392
393#define AIX_LABEL_MAGIC 0xc9c2d4c1
394#define AIX_LABEL_MAGIC_SWAPPED 0xc1d4c2c9
395#define AIX_INFO_MAGIC 0x00072959
396#define AIX_INFO_MAGIC_SWAPPED 0x59290700
397
398#define aixlabel ((aix_partition *)MBRbuffer)
399
400
401/*
402 Changes:
403 * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
404 * Internationalization
405 *
406 * 2003-03-20 Phillip Kesling <pkesling@sgi.com>
407 * Some fixes
408*/
409
410static int aix_other_endian;
411static short aix_volumes=1;
412
413/*
414 * only dealing with free blocks here
415 */
416
417static void
418aix_info( void ) {
419 puts(
420 _("\n\tThere is a valid AIX label on this disk.\n"
421 "\tUnfortunately Linux cannot handle these\n"
422 "\tdisks at the moment. Nevertheless some\n"
423 "\tadvice:\n"
424 "\t1. fdisk will destroy its contents on write.\n"
425 "\t2. Be sure that this disk is NOT a still vital\n"
426 "\t part of a volume group. (Otherwise you may\n"
427 "\t erase the other disks as well, if unmirrored.)\n"
428 "\t3. Before deleting this physical volume be sure\n"
429 "\t to remove the disk logically from your AIX\n"
430 "\t machine. (Otherwise you become an AIXpert).")
431 );
432}
433
434static void
435aix_nolabel( void )
436{
437 aixlabel->magic = 0;
438 aix_label = 0;
439 partitions = 4;
440 memset( MBRbuffer, 0, sizeof(MBRbuffer) ); /* avoid fdisk cores */
441 return;
442}
443
444static int
445check_aix_label( void )
446{
447 if (aixlabel->magic != AIX_LABEL_MAGIC &&
448 aixlabel->magic != AIX_LABEL_MAGIC_SWAPPED) {
449 aix_label = 0;
450 aix_other_endian = 0;
451 return 0;
452 }
453 aix_other_endian = (aixlabel->magic == AIX_LABEL_MAGIC_SWAPPED);
454 update_units();
455 aix_label = 1;
456 partitions= 1016;
457 aix_volumes = 15;
458 aix_info();
459 aix_nolabel(); /* %% */
460 aix_label = 1; /* %% */
461 return 1;
462}
463#endif /* AIX_LABEL */
464
465#ifdef CONFIG_FEATURE_OSF_LABEL
466/*
467 * Copyright (c) 1987, 1988 Regents of the University of California.
468 * All rights reserved.
469 *
470 * Redistribution and use in source and binary forms, with or without
471 * modification, are permitted provided that the following conditions
472 * are met:
473 * 1. Redistributions of source code must retain the above copyright
474 * notice, this list of conditions and the following disclaimer.
475 * 2. Redistributions in binary form must reproduce the above copyright
476 * notice, this list of conditions and the following disclaimer in the
477 * documentation and/or other materials provided with the distribution.
478 * 3. All advertising materials mentioning features or use of this software
479 * must display the following acknowledgment:
480 * This product includes software developed by the University of
481 * California, Berkeley and its contributors.
482 * 4. Neither the name of the University nor the names of its contributors
483 * may be used to endorse or promote products derived from this software
484 * without specific prior written permission.
485 *
486 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
487 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
488 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
489 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
490 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
491 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
492 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
493 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
494 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
495 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
496 * SUCH DAMAGE.
497 */
498
499
500#ifndef BSD_DISKMAGIC
501#define BSD_DISKMAGIC ((uint32_t) 0x82564557)
502#endif
503
504#ifndef BSD_MAXPARTITIONS
505#define BSD_MAXPARTITIONS 16
506#endif
507
508#define BSD_LINUX_BOOTDIR "/usr/ucb/mdec"
509
510#if defined (i386) || defined (__sparc__) || defined (__arm__) || defined (__mips__) || defined (__s390__) || defined (__sh__) || defined(__x86_64__)
511#define BSD_LABELSECTOR 1
512#define BSD_LABELOFFSET 0
513#elif defined (__alpha__) || defined (__powerpc__) || defined (__ia64__) || defined (__hppa__)
514#define BSD_LABELSECTOR 0
515#define BSD_LABELOFFSET 64
516#elif defined (__s390__) || defined (__s390x__)
517#define BSD_LABELSECTOR 1
518#define BSD_LABELOFFSET 0
519#else
520#error unknown architecture
521#endif
522
523#define BSD_BBSIZE 8192 /* size of boot area, with label */
524#define BSD_SBSIZE 8192 /* max size of fs superblock */
525
526struct xbsd_disklabel {
527 uint32_t d_magic; /* the magic number */
528 int16_t d_type; /* drive type */
529 int16_t d_subtype; /* controller/d_type specific */
530 char d_typename[16]; /* type name, e.g. "eagle" */
531 char d_packname[16]; /* pack identifier */
532 /* disk geometry: */
533 uint32_t d_secsize; /* # of bytes per sector */
534 uint32_t d_nsectors; /* # of data sectors per track */
535 uint32_t d_ntracks; /* # of tracks per cylinder */
536 uint32_t d_ncylinders; /* # of data cylinders per unit */
537 uint32_t d_secpercyl; /* # of data sectors per cylinder */
538 uint32_t d_secperunit; /* # of data sectors per unit */
539 /*
540 * Spares (bad sector replacements) below
541 * are not counted in d_nsectors or d_secpercyl.
542 * Spare sectors are assumed to be physical sectors
543 * which occupy space at the end of each track and/or cylinder.
544 */
545 uint16_t d_sparespertrack; /* # of spare sectors per track */
546 uint16_t d_sparespercyl; /* # of spare sectors per cylinder */
547 /*
548 * Alternate cylinders include maintenance, replacement,
549 * configuration description areas, etc.
550 */
551 uint32_t d_acylinders; /* # of alt. cylinders per unit */
552
553 /* hardware characteristics: */
554 /*
555 * d_interleave, d_trackskew and d_cylskew describe perturbations
556 * in the media format used to compensate for a slow controller.
557 * Interleave is physical sector interleave, set up by the formatter
558 * or controller when formatting. When interleaving is in use,
559 * logically adjacent sectors are not physically contiguous,
560 * but instead are separated by some number of sectors.
561 * It is specified as the ratio of physical sectors traversed
562 * per logical sector. Thus an interleave of 1:1 implies contiguous
563 * layout, while 2:1 implies that logical sector 0 is separated
564 * by one sector from logical sector 1.
565 * d_trackskew is the offset of sector 0 on track N
566 * relative to sector 0 on track N-1 on the same cylinder.
567 * Finally, d_cylskew is the offset of sector 0 on cylinder N
568 * relative to sector 0 on cylinder N-1.
569 */
570 uint16_t d_rpm; /* rotational speed */
571 uint16_t d_interleave; /* hardware sector interleave */
572 uint16_t d_trackskew; /* sector 0 skew, per track */
573 uint16_t d_cylskew; /* sector 0 skew, per cylinder */
574 uint32_t d_headswitch; /* head switch time, usec */
575 uint32_t d_trkseek; /* track-to-track seek, usec */
576 uint32_t d_flags; /* generic flags */
577#define NDDATA 5
578 uint32_t d_drivedata[NDDATA]; /* drive-type specific information */
579#define NSPARE 5
580 uint32_t d_spare[NSPARE]; /* reserved for future use */
581 uint32_t d_magic2; /* the magic number (again) */
582 uint16_t d_checksum; /* xor of data incl. partitions */
583 /* filesystem and partition information: */
584 uint16_t d_npartitions; /* number of partitions in following */
585 uint32_t d_bbsize; /* size of boot area at sn0, bytes */
586 uint32_t d_sbsize; /* max size of fs superblock, bytes */
587 struct xbsd_partition { /* the partition table */
588 uint32_t p_size; /* number of sectors in partition */
589 uint32_t p_offset; /* starting sector */
590 uint32_t p_fsize; /* filesystem basic fragment size */
591 uint8_t p_fstype; /* filesystem type, see below */
592 uint8_t p_frag; /* filesystem fragments per block */
593 uint16_t p_cpg; /* filesystem cylinders per group */
594 } d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */
595};
596
597/* d_type values: */
598#define BSD_DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */
599#define BSD_DTYPE_MSCP 2 /* MSCP */
600#define BSD_DTYPE_DEC 3 /* other DEC (rk, rl) */
601#define BSD_DTYPE_SCSI 4 /* SCSI */
602#define BSD_DTYPE_ESDI 5 /* ESDI interface */
603#define BSD_DTYPE_ST506 6 /* ST506 etc. */
604#define BSD_DTYPE_HPIB 7 /* CS/80 on HP-IB */
605#define BSD_DTYPE_HPFL 8 /* HP Fiber-link */
606#define BSD_DTYPE_FLOPPY 10 /* floppy */
607
608/* d_subtype values: */
609#define BSD_DSTYPE_INDOSPART 0x8 /* is inside dos partition */
610#define BSD_DSTYPE_DOSPART(s) ((s) & 3) /* dos partition number */
611#define BSD_DSTYPE_GEOMETRY 0x10 /* drive params in label */
612
613#ifdef DKTYPENAMES
614static const char * const xbsd_dktypenames[] = {
615 "unknown",
616 "SMD",
617 "MSCP",
618 "old DEC",
619 "SCSI",
620 "ESDI",
621 "ST506",
622 "HP-IB",
623 "HP-FL",
624 "type 9",
625 "floppy",
626 0
627};
628#define BSD_DKMAXTYPES (sizeof(xbsd_dktypenames) / sizeof(xbsd_dktypenames[0]) - 1)
629#endif
630
631/*
632 * Filesystem type and version.
633 * Used to interpret other filesystem-specific
634 * per-partition information.
635 */
636#define BSD_FS_UNUSED 0 /* unused */
637#define BSD_FS_SWAP 1 /* swap */
638#define BSD_FS_V6 2 /* Sixth Edition */
639#define BSD_FS_V7 3 /* Seventh Edition */
640#define BSD_FS_SYSV 4 /* System V */
641#define BSD_FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */
642#define BSD_FS_V8 6 /* Eighth Edition, 4K blocks */
643#define BSD_FS_BSDFFS 7 /* 4.2BSD fast file system */
644#define BSD_FS_BSDLFS 9 /* 4.4BSD log-structured file system */
645#define BSD_FS_OTHER 10 /* in use, but unknown/unsupported */
646#define BSD_FS_HPFS 11 /* OS/2 high-performance file system */
647#define BSD_FS_ISO9660 12 /* ISO-9660 filesystem (cdrom) */
648#define BSD_FS_ISOFS BSD_FS_ISO9660
649#define BSD_FS_BOOT 13 /* partition contains bootstrap */
650#define BSD_FS_ADOS 14 /* AmigaDOS fast file system */
651#define BSD_FS_HFS 15 /* Macintosh HFS */
652#define BSD_FS_ADVFS 16 /* Digital Unix AdvFS */
653
654/* this is annoying, but it's also the way it is :-( */
655#ifdef __alpha__
656#define BSD_FS_EXT2 8 /* ext2 file system */
657#else
658#define BSD_FS_MSDOS 8 /* MS-DOS file system */
659#endif
660
661#ifdef DKTYPENAMES
662static const struct systypes xbsd_fstypes[] = {
663/* BSD_FS_UNUSED */ {"\x00" "unused"},
664/* BSD_FS_SWAP */ {"\x01" "swap"},
665/* BSD_FS_V6 */ {"\x02" "Version 6"},
666/* BSD_FS_V7 */ {"\x03" "Version 7"},
667/* BSD_FS_SYSV */ {"\x04" "System V"},
668/* BSD_FS_V71K */ {"\x05" "4.1BSD"},
669/* BSD_FS_V8 */ {"\x06" "Eighth Edition"},
670/* BSD_FS_BSDFFS */ {"\x07" "4.2BSD"},
671#ifdef __alpha__
672/* BSD_FS_EXT2 */ {"\x08" "ext2"},
673#else
674/* BSD_FS_MSDOS */ {"\x08" "MS-DOS"},
675#endif
676/* BSD_FS_BSDLFS */ {"\x09" "4.4LFS"},
677/* BSD_FS_OTHER */ {"\x0a" "unknown"},
678/* BSD_FS_HPFS */ {"\x0b" "HPFS"},
679/* BSD_FS_ISO9660 */ {"\x0c" "ISO-9660"},
680/* BSD_FS_BOOT */ {"\x0d" "boot"},
681/* BSD_FS_ADOS */ {"\x0e" "ADOS"},
682/* BSD_FS_HFS */ {"\x0f" "HFS"},
683/* BSD_FS_ADVFS */ {"\x10" "AdvFS"},
684 { NULL }
685};
686#define BSD_FSMAXTYPES (SIZE(xbsd_fstypes)-1)
687
688#endif
689
690/*
691 * flags shared by various drives:
692 */
693#define BSD_D_REMOVABLE 0x01 /* removable media */
694#define BSD_D_ECC 0x02 /* supports ECC */
695#define BSD_D_BADSECT 0x04 /* supports bad sector forw. */
696#define BSD_D_RAMDISK 0x08 /* disk emulator */
697#define BSD_D_CHAIN 0x10 /* can do back-back transfers */
698#define BSD_D_DOSPART 0x20 /* within MSDOS partition */
699
700#endif /* OSF_LABEL */
701
702/*
703 * Copyright (C) Andreas Neuper, Sep 1998.
704 * This file may be modified and redistributed under
705 * the terms of the GNU Public License.
706 */
707
708struct device_parameter { /* 48 bytes */
709 unsigned char skew;
710 unsigned char gap1;
711 unsigned char gap2;
712 unsigned char sparecyl;
713 unsigned short pcylcount;
714 unsigned short head_vol0;
715 unsigned short ntrks; /* tracks in cyl 0 or vol 0 */
716 unsigned char cmd_tag_queue_depth;
717 unsigned char unused0;
718 unsigned short unused1;
719 unsigned short nsect; /* sectors/tracks in cyl 0 or vol 0 */
720 unsigned short bytes;
721 unsigned short ilfact;
722 unsigned int flags; /* controller flags */
723 unsigned int datarate;
724 unsigned int retries_on_error;
725 unsigned int ms_per_word;
726 unsigned short xylogics_gap1;
727 unsigned short xylogics_syncdelay;
728 unsigned short xylogics_readdelay;
729 unsigned short xylogics_gap2;
730 unsigned short xylogics_readgate;
731 unsigned short xylogics_writecont;
732};
733
734#define SGI_VOLHDR 0x00
735/* 1 and 2 were used for drive types no longer supported by SGI */
736#define SGI_SWAP 0x03
737/* 4 and 5 were for filesystem types SGI haven't ever supported on MIPS CPUs */
738#define SGI_VOLUME 0x06
739#define SGI_EFS 0x07
740#define SGI_LVOL 0x08
741#define SGI_RLVOL 0x09
742#define SGI_XFS 0x0a
743#define SGI_XFSLOG 0x0b
744#define SGI_XLV 0x0c
745#define SGI_XVM 0x0d
746#define ENTIRE_DISK SGI_VOLUME
747/*
748 * controller flags
749 */
750#define SECTOR_SLIP 0x01
751#define SECTOR_FWD 0x02
752#define TRACK_FWD 0x04
753#define TRACK_MULTIVOL 0x08
754#define IGNORE_ERRORS 0x10
755#define RESEEK 0x20
756#define ENABLE_CMDTAGQ 0x40
757
758typedef struct {
759 unsigned int magic; /* expect SGI_LABEL_MAGIC */
760 unsigned short boot_part; /* active boot partition */
761 unsigned short swap_part; /* active swap partition */
762 unsigned char boot_file[16]; /* name of the bootfile */
763 struct device_parameter devparam; /* 1 * 48 bytes */
764 struct volume_directory { /* 15 * 16 bytes */
765 unsigned char vol_file_name[8]; /* a character array */
766 unsigned int vol_file_start; /* number of logical block */
767 unsigned int vol_file_size; /* number of bytes */
768 } directory[15];
769 struct sgi_partition { /* 16 * 12 bytes */
770 unsigned int num_sectors; /* number of blocks */
771 unsigned int start_sector; /* must be cylinder aligned */
772 unsigned int id;
773 } partitions[16];
774 unsigned int csum;
775 unsigned int fillbytes;
776} sgi_partition;
777
778typedef struct {
779 unsigned int magic; /* looks like a magic number */
780 unsigned int a2;
781 unsigned int a3;
782 unsigned int a4;
783 unsigned int b1;
784 unsigned short b2;
785 unsigned short b3;
786 unsigned int c[16];
787 unsigned short d[3];
788 unsigned char scsi_string[50];
789 unsigned char serial[137];
790 unsigned short check1816;
791 unsigned char installer[225];
792} sgiinfo;
793
794#define SGI_LABEL_MAGIC 0x0be5a941
795#define SGI_LABEL_MAGIC_SWAPPED 0x41a9e50b
796#define SGI_INFO_MAGIC 0x00072959
797#define SGI_INFO_MAGIC_SWAPPED 0x59290700
798#define SGI_SSWAP16(x) (sgi_other_endian ? __swap16(x) \
799 : (uint16_t)(x))
800#define SGI_SSWAP32(x) (sgi_other_endian ? __swap32(x) \
801 : (uint32_t)(x))
802
803#define sgilabel ((sgi_partition *)MBRbuffer)
804#define sgiparam (sgilabel->devparam)
805
806typedef struct {
807 unsigned char info[128]; /* Informative text string */
808 unsigned char spare0[14];
809 struct sun_info {
810 unsigned char spare1;
811 unsigned char id;
812 unsigned char spare2;
813 unsigned char flags;
814 } infos[8];
815 unsigned char spare1[246]; /* Boot information etc. */
816 unsigned short rspeed; /* Disk rotational speed */
817 unsigned short pcylcount; /* Physical cylinder count */
818 unsigned short sparecyl; /* extra sects per cylinder */
819 unsigned char spare2[4]; /* More magic... */
820 unsigned short ilfact; /* Interleave factor */
821 unsigned short ncyl; /* Data cylinder count */
822 unsigned short nacyl; /* Alt. cylinder count */
823 unsigned short ntrks; /* Tracks per cylinder */
824 unsigned short nsect; /* Sectors per track */
825 unsigned char spare3[4]; /* Even more magic... */
826 struct sun_partition {
827 uint32_t start_cylinder;
828 uint32_t num_sectors;
829 } partitions[8];
830 unsigned short magic; /* Magic number */
831 unsigned short csum; /* Label xor'd checksum */
832} sun_partition;
833
834
835#define SUN_LABEL_MAGIC 0xDABE
836#define SUN_LABEL_MAGIC_SWAPPED 0xBEDA
837#define sunlabel ((sun_partition *)MBRbuffer)
838#define SUN_SSWAP16(x) (sun_other_endian ? __swap16(x) \
839 : (uint16_t)(x))
840#define SUN_SSWAP32(x) (sun_other_endian ? __swap32(x) \
841 : (uint32_t)(x))
842
843
844#ifdef CONFIG_FEATURE_OSF_LABEL
845/*
846 Changes:
847 19990319 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> - i18n/nls
848
849 20000101 - David Huggins-Daines <dhuggins@linuxcare.com> - Better
850 support for OSF/1 disklabels on Alpha.
851 Also fixed unaligned accesses in alpha_bootblock_checksum()
852*/
853
854#define FREEBSD_PARTITION 0xa5
855#define NETBSD_PARTITION 0xa9
856
857static void xbsd_delete_part (void);
858static void xbsd_new_part (void);
859static void xbsd_write_disklabel (void);
860static int xbsd_create_disklabel (void);
861static void xbsd_edit_disklabel (void);
862static void xbsd_write_bootstrap (void);
863static void xbsd_change_fstype (void);
864static int xbsd_get_part_index (int max);
865static int xbsd_check_new_partition (int *i);
866static void xbsd_list_types (void);
867static u_short xbsd_dkcksum (struct xbsd_disklabel *lp);
868static int xbsd_initlabel (struct partition *p, struct xbsd_disklabel *d,
869 int pindex);
870static int xbsd_readlabel (struct partition *p, struct xbsd_disklabel *d);
871static int xbsd_writelabel (struct partition *p, struct xbsd_disklabel *d);
872
873#if defined (__alpha__)
874static void alpha_bootblock_checksum (char *boot);
875#endif
876
877#if !defined (__alpha__)
878static int xbsd_translate_fstype (int linux_type);
879static void xbsd_link_part (void);
880static struct partition *xbsd_part;
881static int xbsd_part_index;
882#endif
883
884#if defined (__alpha__)
885/* We access this through a uint64_t * when checksumming */
886static char disklabelbuffer[BSD_BBSIZE] __attribute__((aligned(8)));
887#else
888static char disklabelbuffer[BSD_BBSIZE];
889#endif
890
891static struct xbsd_disklabel xbsd_dlabel;
892
893#define bsd_cround(n) \
894 (display_in_cyl_units ? ((n)/xbsd_dlabel.d_secpercyl) + 1 : (n))
895
896/*
897 * Test whether the whole disk has BSD disk label magic.
898 *
899 * Note: often reformatting with DOS-type label leaves the BSD magic,
900 * so this does not mean that there is a BSD disk label.
901 */
902static int
903check_osf_label(void) {
904 if (xbsd_readlabel (NULL, &xbsd_dlabel) == 0)
905 return 0;
906 return 1;
907}
908
909static void xbsd_print_disklabel(int);
910
911static int
912btrydev (const char * dev) {
913 if (xbsd_readlabel (NULL, &xbsd_dlabel) == 0)
914 return -1;
915 printf(_("\nBSD label for device: %s\n"), dev);
916 xbsd_print_disklabel (0);
917 return 0;
918}
919
920static void
921bmenu (void) {
922 puts (_("Command action"));
923 puts (_("\td\tdelete a BSD partition"));
924 puts (_("\te\tedit drive data"));
925 puts (_("\ti\tinstall bootstrap"));
926 puts (_("\tl\tlist known filesystem types"));
927 puts (_("\tm\tprint this menu"));
928 puts (_("\tn\tadd a new BSD partition"));
929 puts (_("\tp\tprint BSD partition table"));
930 puts (_("\tq\tquit without saving changes"));
931 puts (_("\tr\treturn to main menu"));
932 puts (_("\ts\tshow complete disklabel"));
933 puts (_("\tt\tchange a partition's filesystem id"));
934 puts (_("\tu\tchange units (cylinders/sectors)"));
935 puts (_("\tw\twrite disklabel to disk"));
936#if !defined (__alpha__)
937 puts (_("\tx\tlink BSD partition to non-BSD partition"));
938#endif
939}
940
941#if !defined (__alpha__)
942static int
943hidden(int type) {
944 return type ^ 0x10;
945}
946
947static int
948is_bsd_partition_type(int type) {
949 return (type == FREEBSD_PARTITION ||
950 type == hidden(FREEBSD_PARTITION) ||
951 type == NETBSD_PARTITION ||
952 type == hidden(NETBSD_PARTITION));
953}
954#endif
955
956static void
957bselect (void) {
958#if !defined (__alpha__)
959 int t, ss;
960 struct partition *p;
961
962 for (t=0; t<4; t++) {
963 p = get_part_table(t);
964 if (p && is_bsd_partition_type(p->sys_ind)) {
965 xbsd_part = p;
966 xbsd_part_index = t;
967 ss = get_start_sect(xbsd_part);
968 if (ss == 0) {
969 fprintf (stderr, _("Partition %s has invalid starting sector 0.\n"),
970 partname(disk_device, t+1, 0));
971 return;
972 }
973 printf (_("Reading disklabel of %s at sector %d.\n"),
974 partname(disk_device, t+1, 0), ss + BSD_LABELSECTOR);
975 if (xbsd_readlabel (xbsd_part, &xbsd_dlabel) == 0)
976 if (xbsd_create_disklabel () == 0)
977 return;
978 break;
979 }
980 }
981
982 if (t == 4) {
983 printf (_("There is no *BSD partition on %s.\n"), disk_device);
984 return;
985 }
986
987#elif defined (__alpha__)
988
989 if (xbsd_readlabel (NULL, &xbsd_dlabel) == 0)
990 if (xbsd_create_disklabel () == 0)
991 exit ( EXIT_SUCCESS );
992
993#endif
994
995 while (1) {
996 putchar ('\n');
997 switch (tolower (read_char (_("BSD disklabel command (m for help): ")))) {
998 case 'd':
999 xbsd_delete_part ();
1000 break;
1001 case 'e':
1002 xbsd_edit_disklabel ();
1003 break;
1004 case 'i':
1005 xbsd_write_bootstrap ();
1006 break;
1007 case 'l':
1008 xbsd_list_types ();
1009 break;
1010 case 'n':
1011 xbsd_new_part ();
1012 break;
1013 case 'p':
1014 xbsd_print_disklabel (0);
1015 break;
1016 case 'q':
1017 close (fd);
1018 exit ( EXIT_SUCCESS );
1019 case 'r':
1020 return;
1021 case 's':
1022 xbsd_print_disklabel (1);
1023 break;
1024 case 't':
1025 xbsd_change_fstype ();
1026 break;
1027 case 'u':
1028 change_units();
1029 break;
1030 case 'w':
1031 xbsd_write_disklabel ();
1032 break;
1033#if !defined (__alpha__)
1034 case 'x':
1035 xbsd_link_part ();
1036 break;
1037#endif
1038 default:
1039 bmenu ();
1040 break;
1041 }
1042 }
1043}
1044
1045static void
1046xbsd_delete_part (void)
1047{
1048 int i;
1049
1050 i = xbsd_get_part_index (xbsd_dlabel.d_npartitions);
1051 xbsd_dlabel.d_partitions[i].p_size = 0;
1052 xbsd_dlabel.d_partitions[i].p_offset = 0;
1053 xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED;
1054 if (xbsd_dlabel.d_npartitions == i + 1)
1055 while (xbsd_dlabel.d_partitions[xbsd_dlabel.d_npartitions-1].p_size == 0)
1056 xbsd_dlabel.d_npartitions--;
1057}
1058
1059static void
1060xbsd_new_part (void)
1061{
1062 off_t begin, end;
1063 char mesg[256];
1064 int i;
1065
1066 if (!xbsd_check_new_partition (&i))
1067 return;
1068
1069#if !defined (__alpha__) && !defined (__powerpc__) && !defined (__hppa__)
1070 begin = get_start_sect(xbsd_part);
1071 end = begin + get_nr_sects(xbsd_part) - 1;
1072#else
1073 begin = 0;
1074 end = xbsd_dlabel.d_secperunit - 1;
1075#endif
1076
1077 snprintf (mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR));
1078 begin = read_int (bsd_cround (begin), bsd_cround (begin), bsd_cround (end),
1079 0, mesg);
1080
1081 if (display_in_cyl_units)
1082 begin = (begin - 1) * xbsd_dlabel.d_secpercyl;
1083
1084 snprintf (mesg, sizeof(mesg), _("Last %s or +size or +sizeM or +sizeK"),
1085 str_units(SINGULAR));
1086 end = read_int (bsd_cround (begin), bsd_cround (end), bsd_cround (end),
1087 bsd_cround (begin), mesg);
1088
1089 if (display_in_cyl_units)
1090 end = end * xbsd_dlabel.d_secpercyl - 1;
1091
1092 xbsd_dlabel.d_partitions[i].p_size = end - begin + 1;
1093 xbsd_dlabel.d_partitions[i].p_offset = begin;
1094 xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED;
1095}
1096
1097static void
1098xbsd_print_disklabel (int show_all) {
1099 struct xbsd_disklabel *lp = &xbsd_dlabel;
1100 struct xbsd_partition *pp;
1101 int i, j;
1102
1103 if (show_all) {
1104#if defined (__alpha__)
1105 printf("# %s:\n", disk_device);
1106#else
1107 printf("# %s:\n", partname(disk_device, xbsd_part_index+1, 0));
1108#endif
1109 if ((unsigned) lp->d_type < BSD_DKMAXTYPES)
1110 printf(_("type: %s\n"), xbsd_dktypenames[lp->d_type]);
1111 else
1112 printf(_("type: %d\n"), lp->d_type);
1113 printf(_("disk: %.*s\n"), (int) sizeof(lp->d_typename), lp->d_typename);
1114 printf(_("label: %.*s\n"), (int) sizeof(lp->d_packname), lp->d_packname);
1115 printf(_("flags:"));
1116 if (lp->d_flags & BSD_D_REMOVABLE)
1117 printf(_(" removable"));
1118 if (lp->d_flags & BSD_D_ECC)
1119 printf(_(" ecc"));
1120 if (lp->d_flags & BSD_D_BADSECT)
1121 printf(_(" badsect"));
1122 printf("\n");
1123 /* On various machines the fields of *lp are short/int/long */
1124 /* In order to avoid problems, we cast them all to long. */
1125 printf(_("bytes/sector: %ld\n"), (long) lp->d_secsize);
1126 printf(_("sectors/track: %ld\n"), (long) lp->d_nsectors);
1127 printf(_("tracks/cylinder: %ld\n"), (long) lp->d_ntracks);
1128 printf(_("sectors/cylinder: %ld\n"), (long) lp->d_secpercyl);
1129 printf(_("cylinders: %ld\n"), (long) lp->d_ncylinders);
1130 printf(_("rpm: %d\n"), lp->d_rpm);
1131 printf(_("interleave: %d\n"), lp->d_interleave);
1132 printf(_("trackskew: %d\n"), lp->d_trackskew);
1133 printf(_("cylinderskew: %d\n"), lp->d_cylskew);
1134 printf(_("headswitch: %ld\t\t# milliseconds\n"),
1135 (long) lp->d_headswitch);
1136 printf(_("track-to-track seek: %ld\t# milliseconds\n"),
1137 (long) lp->d_trkseek);
1138 printf(_("drivedata: "));
1139 for (i = NDDATA - 1; i >= 0; i--)
1140 if (lp->d_drivedata[i])
1141 break;
1142 if (i < 0)
1143 i = 0;
1144 for (j = 0; j <= i; j++)
1145 printf("%ld ", (long) lp->d_drivedata[j]);
1146 }
1147 printf(_("\n%d partitions:\n"), lp->d_npartitions);
1148 printf(_("# start end size fstype [fsize bsize cpg]\n"));
1149 pp = lp->d_partitions;
1150 for (i = 0; i < lp->d_npartitions; i++, pp++) {
1151 if (pp->p_size) {
1152 if (display_in_cyl_units && lp->d_secpercyl) {
1153 printf(" %c: %8ld%c %8ld%c %8ld%c ",
1154 'a' + i,
1155 (long) pp->p_offset / lp->d_secpercyl + 1,
1156 (pp->p_offset % lp->d_secpercyl) ? '*' : ' ',
1157 (long) (pp->p_offset + pp->p_size + lp->d_secpercyl - 1)
1158 / lp->d_secpercyl,
1159 ((pp->p_offset + pp->p_size) % lp->d_secpercyl) ? '*' : ' ',
1160 (long) pp->p_size / lp->d_secpercyl,
1161 (pp->p_size % lp->d_secpercyl) ? '*' : ' ');
1162 } else {
1163 printf(" %c: %8ld %8ld %8ld ",
1164 'a' + i,
1165 (long) pp->p_offset,
1166 (long) pp->p_offset + pp->p_size - 1,
1167 (long) pp->p_size);
1168 }
1169 if ((unsigned) pp->p_fstype < BSD_FSMAXTYPES)
1170 printf("%8.8s", xbsd_fstypes[pp->p_fstype].name);
1171 else
1172 printf("%8x", pp->p_fstype);
1173 switch (pp->p_fstype) {
1174 case BSD_FS_UNUSED:
1175 printf(" %5ld %5ld %5.5s ",
1176 (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, "");
1177 break;
1178
1179 case BSD_FS_BSDFFS:
1180 printf(" %5ld %5ld %5d ",
1181 (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag,
1182 pp->p_cpg);
1183 break;
1184
1185 default:
1186 printf("%22.22s", "");
1187 break;
1188 }
1189 printf("\n");
1190 }
1191 }
1192}
1193
1194static void
1195xbsd_write_disklabel (void) {
1196#if defined (__alpha__)
1197 printf (_("Writing disklabel to %s.\n"), disk_device);
1198 xbsd_writelabel (NULL, &xbsd_dlabel);
1199#else
1200 printf (_("Writing disklabel to %s.\n"),
1201 partname(disk_device, xbsd_part_index+1, 0));
1202 xbsd_writelabel (xbsd_part, &xbsd_dlabel);
1203#endif
1204 reread_partition_table(0); /* no exit yet */
1205}
1206
1207static int
1208xbsd_create_disklabel (void) {
1209 char c;
1210
1211#if defined (__alpha__)
1212 fprintf (stderr, _("%s contains no disklabel.\n"), disk_device);
1213#else
1214 fprintf (stderr, _("%s contains no disklabel.\n"),
1215 partname(disk_device, xbsd_part_index+1, 0));
1216#endif
1217
1218 while (1) {
1219 c = read_char (_("Do you want to create a disklabel? (y/n) "));
1220 if (c == 'y' || c == 'Y') {
1221 if (xbsd_initlabel (
1222#if defined (__alpha__) || defined (__powerpc__) || defined (__hppa__) || \
1223 defined (__s390__) || defined (__s390x__)
1224 NULL, &xbsd_dlabel, 0
1225#else
1226 xbsd_part, &xbsd_dlabel, xbsd_part_index
1227#endif
1228 ) == 1) {
1229 xbsd_print_disklabel (1);
1230 return 1;
1231 } else
1232 return 0;
1233 } else if (c == 'n')
1234 return 0;
1235 }
1236}
1237
1238static int
1239edit_int (int def, char *mesg)
1240{
1241 do {
1242 fputs (mesg, stdout);
1243 printf (" (%d): ", def);
1244 if (!read_line ())
1245 return def;
1246 }
1247 while (!isdigit (*line_ptr));
1248 return atoi (line_ptr);
1249}
1250
1251static void
1252xbsd_edit_disklabel (void)
1253{
1254 struct xbsd_disklabel *d;
1255
1256 d = &xbsd_dlabel;
1257
1258#if defined (__alpha__) || defined (__ia64__)
1259 d -> d_secsize = (u_long) edit_int ((u_long) d -> d_secsize ,_("bytes/sector"));
1260 d -> d_nsectors = (u_long) edit_int ((u_long) d -> d_nsectors ,_("sectors/track"));
1261 d -> d_ntracks = (u_long) edit_int ((u_long) d -> d_ntracks ,_("tracks/cylinder"));
1262 d -> d_ncylinders = (u_long) edit_int ((u_long) d -> d_ncylinders ,_("cylinders"));
1263#endif
1264
1265 /* d -> d_secpercyl can be != d -> d_nsectors * d -> d_ntracks */
1266 while (1)
1267 {
1268 d -> d_secpercyl = (u_long) edit_int ((u_long) d -> d_nsectors * d -> d_ntracks,
1269 _("sectors/cylinder"));
1270 if (d -> d_secpercyl <= d -> d_nsectors * d -> d_ntracks)
1271 break;
1272
1273 printf (_("Must be <= sectors/track * tracks/cylinder (default).\n"));
1274 }
1275 d -> d_rpm = (u_short) edit_int ((u_short) d -> d_rpm ,_("rpm"));
1276 d -> d_interleave = (u_short) edit_int ((u_short) d -> d_interleave,_("interleave"));
1277 d -> d_trackskew = (u_short) edit_int ((u_short) d -> d_trackskew ,_("trackskew"));
1278 d -> d_cylskew = (u_short) edit_int ((u_short) d -> d_cylskew ,_("cylinderskew"));
1279 d -> d_headswitch = (u_long) edit_int ((u_long) d -> d_headswitch ,_("headswitch"));
1280 d -> d_trkseek = (u_long) edit_int ((u_long) d -> d_trkseek ,_("track-to-track seek"));
1281
1282 d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders;
1283}
1284
1285static int
1286xbsd_get_bootstrap (char *path, void *ptr, int size)
1287{
1288 int fdb;
1289
1290 if ((fdb = open (path, O_RDONLY)) < 0)
1291 {
1292 perror (path);
1293 return 0;
1294 }
1295 if (read (fdb, ptr, size) < 0)
1296 {
1297 perror (path);
1298 close (fdb);
1299 return 0;
1300 }
1301 printf (" ... %s\n", path);
1302 close (fdb);
1303 return 1;
1304}
1305
1306static void
1307sync_disks (void)
1308{
1309 printf (_("\nSyncing disks.\n"));
1310 sync ();
1311 sleep (4);
1312}
1313
1314static void
1315xbsd_write_bootstrap (void)
1316{
1317 char *bootdir = BSD_LINUX_BOOTDIR;
1318 char path[MAXPATHLEN];
1319 char *dkbasename;
1320 struct xbsd_disklabel dl;
1321 char *d, *p, *e;
1322 int sector;
1323
1324 if (xbsd_dlabel.d_type == BSD_DTYPE_SCSI)
1325 dkbasename = "sd";
1326 else
1327 dkbasename = "wd";
1328
1329 printf (_("Bootstrap: %sboot -> boot%s (%s): "),
1330 dkbasename, dkbasename, dkbasename);
1331 if (read_line ()) {
1332 line_ptr[strlen (line_ptr)-1] = '\0';
1333 dkbasename = line_ptr;
1334 }
1335 snprintf (path, sizeof(path), "%s/%sboot", bootdir, dkbasename);
1336 if (!xbsd_get_bootstrap (path, disklabelbuffer, (int) xbsd_dlabel.d_secsize))
1337 return;
1338
1339 /* We need a backup of the disklabel (xbsd_dlabel might have changed). */
1340 d = &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE];
1341 bcopy (d, &dl, sizeof (struct xbsd_disklabel));
1342
1343 /* The disklabel will be overwritten by 0's from bootxx anyway */
1344 bzero (d, sizeof (struct xbsd_disklabel));
1345
1346 snprintf (path, sizeof(path), "%s/boot%s", bootdir, dkbasename);
1347 if (!xbsd_get_bootstrap (path, &disklabelbuffer[xbsd_dlabel.d_secsize],
1348 (int) xbsd_dlabel.d_bbsize - xbsd_dlabel.d_secsize))
1349 return;
1350
1351 e = d + sizeof (struct xbsd_disklabel);
1352 for (p=d; p < e; p++)
1353 if (*p) {
1354 fprintf (stderr, _("Bootstrap overlaps with disk label!\n"));
1355 exit ( EXIT_FAILURE );
1356 }
1357
1358 bcopy (&dl, d, sizeof (struct xbsd_disklabel));
1359
1360#if defined (__powerpc__) || defined (__hppa__)
1361 sector = 0;
1362#elif defined (__alpha__)
1363 sector = 0;
1364 alpha_bootblock_checksum (disklabelbuffer);
1365#else
1366 sector = get_start_sect(xbsd_part);
1367#endif
1368
1369 if (lseek (fd, sector * SECTOR_SIZE, SEEK_SET) == -1)
1370 fdisk_fatal (unable_to_seek);
1371 if (BSD_BBSIZE != write (fd, disklabelbuffer, BSD_BBSIZE))
1372 fdisk_fatal (unable_to_write);
1373
1374#if defined (__alpha__)
1375 printf (_("Bootstrap installed on %s.\n"), disk_device);
1376#else
1377 printf (_("Bootstrap installed on %s.\n"),
1378 partname (disk_device, xbsd_part_index+1, 0));
1379#endif
1380
1381 sync_disks ();
1382}
1383
1384static void
1385xbsd_change_fstype (void)
1386{
1387 int i;
1388
1389 i = xbsd_get_part_index (xbsd_dlabel.d_npartitions);
1390 xbsd_dlabel.d_partitions[i].p_fstype = read_hex (xbsd_fstypes);
1391}
1392
1393static int
1394xbsd_get_part_index (int max)
1395{
1396 char prompt[256];
1397 char l;
1398
1399 snprintf (prompt, sizeof(prompt), _("Partition (a-%c): "), 'a' + max - 1);
1400 do
1401 l = tolower (read_char (prompt));
1402 while (l < 'a' || l > 'a' + max - 1);
1403 return l - 'a';
1404}
1405
1406static int
1407xbsd_check_new_partition (int *i) {
1408
1409 /* room for more? various BSD flavours have different maxima */
1410 if (xbsd_dlabel.d_npartitions == BSD_MAXPARTITIONS) {
1411 int t;
1412
1413 for (t = 0; t < BSD_MAXPARTITIONS; t++)
1414 if (xbsd_dlabel.d_partitions[t].p_size == 0)
1415 break;
1416
1417 if (t == BSD_MAXPARTITIONS) {
1418 fprintf (stderr, _("The maximum number of partitions "
1419 "has been created\n"));
1420 return 0;
1421 }
1422 }
1423
1424 *i = xbsd_get_part_index (BSD_MAXPARTITIONS);
1425
1426 if (*i >= xbsd_dlabel.d_npartitions)
1427 xbsd_dlabel.d_npartitions = (*i) + 1;
1428
1429 if (xbsd_dlabel.d_partitions[*i].p_size != 0) {
1430 fprintf (stderr, _("This partition already exists.\n"));
1431 return 0;
1432 }
1433
1434 return 1;
1435}
1436
1437static void
1438xbsd_list_types (void) {
1439 list_types (xbsd_fstypes);
1440}
1441
1442static u_short
1443xbsd_dkcksum (struct xbsd_disklabel *lp) {
1444 u_short *start, *end;
1445 u_short sum = 0;
1446
1447 start = (u_short *) lp;
1448 end = (u_short *) &lp->d_partitions[lp->d_npartitions];
1449 while (start < end)
1450 sum ^= *start++;
1451 return sum;
1452}
1453
1454static int
1455xbsd_initlabel (struct partition *p, struct xbsd_disklabel *d, int pindex) {
1456 struct xbsd_partition *pp;
1457
1458 get_geometry ();
1459 bzero (d, sizeof (struct xbsd_disklabel));
1460
1461 d -> d_magic = BSD_DISKMAGIC;
1462
1463 if (strncmp (disk_device, "/dev/sd", 7) == 0)
1464 d -> d_type = BSD_DTYPE_SCSI;
1465 else
1466 d -> d_type = BSD_DTYPE_ST506;
1467
1468#if 0 /* not used (at least not written to disk) by NetBSD/i386 1.0 */
1469 d -> d_subtype = BSD_DSTYPE_INDOSPART & pindex;
1470#endif
1471
1472#if !defined (__alpha__)
1473 d -> d_flags = BSD_D_DOSPART;
1474#else
1475 d -> d_flags = 0;
1476#endif
1477 d -> d_secsize = SECTOR_SIZE; /* bytes/sector */
1478 d -> d_nsectors = sectors; /* sectors/track */
1479 d -> d_ntracks = heads; /* tracks/cylinder (heads) */
1480 d -> d_ncylinders = cylinders;
1481 d -> d_secpercyl = sectors * heads;/* sectors/cylinder */
1482 if (d -> d_secpercyl == 0)
1483 d -> d_secpercyl = 1; /* avoid segfaults */
1484 d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders;
1485
1486 d -> d_rpm = 3600;
1487 d -> d_interleave = 1;
1488 d -> d_trackskew = 0;
1489 d -> d_cylskew = 0;
1490 d -> d_headswitch = 0;
1491 d -> d_trkseek = 0;
1492
1493 d -> d_magic2 = BSD_DISKMAGIC;
1494 d -> d_bbsize = BSD_BBSIZE;
1495 d -> d_sbsize = BSD_SBSIZE;
1496
1497#if !defined (__alpha__)
1498 d -> d_npartitions = 4;
1499 pp = &d -> d_partitions[2]; /* Partition C should be
1500 the NetBSD partition */
1501 pp -> p_offset = get_start_sect(p);
1502 pp -> p_size = get_nr_sects(p);
1503 pp -> p_fstype = BSD_FS_UNUSED;
1504 pp = &d -> d_partitions[3]; /* Partition D should be
1505 the whole disk */
1506 pp -> p_offset = 0;
1507 pp -> p_size = d -> d_secperunit;
1508 pp -> p_fstype = BSD_FS_UNUSED;
1509#elif defined (__alpha__)
1510 d -> d_npartitions = 3;
1511 pp = &d -> d_partitions[2]; /* Partition C should be
1512 the whole disk */
1513 pp -> p_offset = 0;
1514 pp -> p_size = d -> d_secperunit;
1515 pp -> p_fstype = BSD_FS_UNUSED;
1516#endif
1517
1518 return 1;
1519}
1520
1521/*
1522 * Read a xbsd_disklabel from sector 0 or from the starting sector of p.
1523 * If it has the right magic, return 1.
1524 */
1525static int
1526xbsd_readlabel (struct partition *p, struct xbsd_disklabel *d)
1527{
1528 int t, sector;
1529
1530 /* p is used only to get the starting sector */
1531#if !defined (__alpha__)
1532 sector = (p ? get_start_sect(p) : 0);
1533#elif defined (__alpha__)
1534 sector = 0;
1535#endif
1536
1537 if (lseek (fd, sector * SECTOR_SIZE, SEEK_SET) == -1)
1538 fdisk_fatal (unable_to_seek);
1539 if (BSD_BBSIZE != read (fd, disklabelbuffer, BSD_BBSIZE))
1540 fdisk_fatal (unable_to_read);
1541
1542 bcopy (&disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET],
1543 d, sizeof (struct xbsd_disklabel));
1544
1545 if (d -> d_magic != BSD_DISKMAGIC || d -> d_magic2 != BSD_DISKMAGIC)
1546 return 0;
1547
1548 for (t = d -> d_npartitions; t < BSD_MAXPARTITIONS; t++) {
1549 d -> d_partitions[t].p_size = 0;
1550 d -> d_partitions[t].p_offset = 0;
1551 d -> d_partitions[t].p_fstype = BSD_FS_UNUSED;
1552 }
1553
1554 if (d -> d_npartitions > BSD_MAXPARTITIONS)
1555 fprintf (stderr, _("Warning: too many partitions "
1556 "(%d, maximum is %d).\n"),
1557 d -> d_npartitions, BSD_MAXPARTITIONS);
1558 return 1;
1559}
1560
1561static int
1562xbsd_writelabel (struct partition *p, struct xbsd_disklabel *d)
1563{
1564 unsigned int sector;
1565
1566#if !defined (__alpha__) && !defined (__powerpc__) && !defined (__hppa__)
1567 sector = get_start_sect(p) + BSD_LABELSECTOR;
1568#else
1569 sector = BSD_LABELSECTOR;
1570#endif
1571
1572 d -> d_checksum = 0;
1573 d -> d_checksum = xbsd_dkcksum (d);
1574
1575 /* This is necessary if we want to write the bootstrap later,
1576 otherwise we'd write the old disklabel with the bootstrap.
1577 */
1578 bcopy (d, &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET],
1579 sizeof (struct xbsd_disklabel));
1580
1581#if defined (__alpha__) && BSD_LABELSECTOR == 0
1582 alpha_bootblock_checksum (disklabelbuffer);
1583 if (lseek (fd, 0, SEEK_SET) == -1)
1584 fdisk_fatal (unable_to_seek);
1585 if (BSD_BBSIZE != write (fd, disklabelbuffer, BSD_BBSIZE))
1586 fdisk_fatal (unable_to_write);
1587#else
1588 if (lseek (fd, sector * SECTOR_SIZE + BSD_LABELOFFSET,
1589 SEEK_SET) == -1)
1590 fdisk_fatal (unable_to_seek);
1591 if (sizeof (struct xbsd_disklabel) != write (fd, d, sizeof (struct xbsd_disklabel)))
1592 fdisk_fatal (unable_to_write);
1593#endif
1594
1595 sync_disks ();
1596
1597 return 1;
1598}
1599
1600
1601#if !defined (__alpha__)
1602static int
1603xbsd_translate_fstype (int linux_type)
1604{
1605 switch (linux_type)
1606 {
1607 case 0x01: /* DOS 12-bit FAT */
1608 case 0x04: /* DOS 16-bit <32M */
1609 case 0x06: /* DOS 16-bit >=32M */
1610 case 0xe1: /* DOS access */
1611 case 0xe3: /* DOS R/O */
1612 case 0xf2: /* DOS secondary */
1613 return BSD_FS_MSDOS;
1614 case 0x07: /* OS/2 HPFS */
1615 return BSD_FS_HPFS;
1616 default:
1617 return BSD_FS_OTHER;
1618 }
1619}
1620
1621static void
1622xbsd_link_part (void)
1623{
1624 int k, i;
1625 struct partition *p;
1626
1627 k = get_partition (1, partitions);
1628
1629 if (!xbsd_check_new_partition (&i))
1630 return;
1631
1632 p = get_part_table(k);
1633
1634 xbsd_dlabel.d_partitions[i].p_size = get_nr_sects(p);
1635 xbsd_dlabel.d_partitions[i].p_offset = get_start_sect(p);
1636 xbsd_dlabel.d_partitions[i].p_fstype = xbsd_translate_fstype(p->sys_ind);
1637}
1638#endif
1639
1640#if defined (__alpha__)
1641
1642#if !defined(__GLIBC__)
1643typedef unsigned long long uint64_t;
1644#endif
1645
1646static void
1647alpha_bootblock_checksum (char *boot)
1648{
1649 uint64_t *dp, sum;
1650 int i;
1651
1652 dp = (uint64_t *)boot;
1653 sum = 0;
1654 for (i = 0; i < 63; i++)
1655 sum += dp[i];
1656 dp[63] = sum;
1657}
1658#endif /* __alpha__ */
1659
1660#endif /* OSF_LABEL */
1661
1662#if defined(CONFIG_FEATURE_SGI_LABEL) || defined(CONFIG_FEATURE_SUN_LABEL)
1663static inline unsigned short
1664__swap16(unsigned short x) {
1665 return (((uint16_t)(x) & 0xFF) << 8) | (((uint16_t)(x) & 0xFF00) >> 8);
1666}
1667
1668static inline uint32_t
1669__swap32(uint32_t x) {
1670 return (((x & 0xFF) << 24) |
1671 ((x & 0xFF00) << 8) |
1672 ((x & 0xFF0000) >> 8) |
1673 ((x & 0xFF000000) >> 24));
1674}
1675#endif
1676
1677#ifdef CONFIG_FEATURE_SGI_LABEL
1678/*
1679 *
1680 * fdisksgilabel.c
1681 *
1682 * Copyright (C) Andreas Neuper, Sep 1998.
1683 * This file may be modified and redistributed under
1684 * the terms of the GNU Public License.
1685 *
1686 * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
1687 * Internationalization
1688 */
1689
1690
1691static int sgi_other_endian;
1692static int debug;
1693static short sgi_volumes=1;
1694
1695/*
1696 * only dealing with free blocks here
1697 */
1698
1699typedef struct { unsigned int first; unsigned int last; } freeblocks;
1700static freeblocks freelist[17]; /* 16 partitions can produce 17 vacant slots */
1701
1702static void
1703setfreelist(int i, unsigned int f, unsigned int l) {
1704 freelist[i].first = f;
1705 freelist[i].last = l;
1706}
1707
1708static void
1709add2freelist(unsigned int f, unsigned int l) {
1710 int i = 0;
1711 for ( ; i < 17 ; i++)
1712 if (freelist[i].last == 0)
1713 break;
1714 setfreelist(i, f, l);
1715}
1716
1717static void
1718clearfreelist(void) {
1719 int i;
1720
1721 for (i = 0; i < 17 ; i++)
1722 setfreelist(i, 0, 0);
1723}
1724
1725static unsigned int
1726isinfreelist(unsigned int b) {
1727 int i;
1728
1729 for (i = 0; i < 17 ; i++)
1730 if (freelist[i].first <= b && freelist[i].last >= b)
1731 return freelist[i].last;
1732 return 0;
1733}
1734 /* return last vacant block of this stride (never 0). */
1735 /* the '>=' is not quite correct, but simplifies the code */
1736/*
1737 * end of free blocks section
1738 */
1739
1740static const struct systypes sgi_sys_types[] = {
1741/* SGI_VOLHDR */ {"\x00" "SGI volhdr" },
1742/* 0x01 */ {"\x01" "SGI trkrepl" },
1743/* 0x02 */ {"\x02" "SGI secrepl" },
1744/* SGI_SWAP */ {"\x03" "SGI raw" },
1745/* 0x04 */ {"\x04" "SGI bsd" },
1746/* 0x05 */ {"\x05" "SGI sysv" },
1747/* ENTIRE_DISK */ {"\x06" "SGI volume" },
1748/* SGI_EFS */ {"\x07" "SGI efs" },
1749/* 0x08 */ {"\x08" "SGI lvol" },
1750/* 0x09 */ {"\x09" "SGI rlvol" },
1751/* SGI_XFS */ {"\x0a" "SGI xfs" },
1752/* SGI_XFSLOG */ {"\x0b" "SGI xfslog" },
1753/* SGI_XLV */ {"\x0c" "SGI xlv" },
1754/* SGI_XVM */ {"\x0d" "SGI xvm" },
1755/* LINUX_SWAP */ {"\x82" "Linux swap" },
1756/* LINUX_NATIVE */ {"\x83" "Linux native" },
1757/* LINUX_LVM */ {"\x8d" "Linux LVM" },
1758/* LINUX_RAID */ {"\xfd" "Linux RAID" },
1759 { NULL }
1760};
1761
1762
1763static int
1764sgi_get_nsect(void) {
1765 return SGI_SSWAP16(sgilabel->devparam.nsect);
1766}
1767
1768static int
1769sgi_get_ntrks(void) {
1770 return SGI_SSWAP16(sgilabel->devparam.ntrks);
1771}
1772
1773static void
1774sgi_nolabel(void) {
1775 sgilabel->magic = 0;
1776 sgi_label = 0;
1777 partitions = 4;
1778}
1779
1780static unsigned int
1781two_s_complement_32bit_sum(unsigned int* base, int size /* in bytes */) {
1782 int i=0;
1783 unsigned int sum=0;
1784
1785 size /= sizeof(unsigned int);
1786 for (i = 0; i < size; i++)
1787 sum -= SGI_SSWAP32(base[i]);
1788 return sum;
1789}
1790
1791static int
1792check_sgi_label(void) {
1793 if (sizeof(sgilabel) > 512) {
1794 fprintf(stderr,
1795 _("According to MIPS Computer Systems, Inc the "
1796 "Label must not contain more than 512 bytes\n"));
1797 exit(1);
1798 }
1799
1800 if (sgilabel->magic != SGI_LABEL_MAGIC &&
1801 sgilabel->magic != SGI_LABEL_MAGIC_SWAPPED) {
1802 sgi_label = 0;
1803 sgi_other_endian = 0;
1804 return 0;
1805 }
1806
1807 sgi_other_endian = (sgilabel->magic == SGI_LABEL_MAGIC_SWAPPED);
1808 /*
1809 * test for correct checksum
1810 */
1811 if (two_s_complement_32bit_sum((unsigned int*)sgilabel,
1812 sizeof(*sgilabel))) {
1813 fprintf(stderr,
1814 _("Detected sgi disklabel with wrong checksum.\n"));
1815 }
1816 update_units();
1817 sgi_label = 1;
1818 partitions= 16;
1819 sgi_volumes = 15;
1820 return 1;
1821}
1822
1823static unsigned int
1824sgi_get_start_sector(int i) {
1825 return SGI_SSWAP32(sgilabel->partitions[i].start_sector);
1826}
1827
1828static unsigned int
1829sgi_get_num_sectors(int i) {
1830 return SGI_SSWAP32(sgilabel->partitions[i].num_sectors);
1831}
1832
1833static int
1834sgi_get_sysid(int i)
1835{
1836 return SGI_SSWAP32(sgilabel->partitions[i].id);
1837}
1838
1839static int
1840sgi_get_bootpartition(void)
1841{
1842 return SGI_SSWAP16(sgilabel->boot_part);
1843}
1844
1845static int
1846sgi_get_swappartition(void)
1847{
1848 return SGI_SSWAP16(sgilabel->swap_part);
1849}
1850
1851static void
1852sgi_list_table(int xtra) {
1853 int i, w, wd;
1854 int kpi = 0; /* kernel partition ID */
1855
1856 if(xtra) {
1857 printf(_("\nDisk %s (SGI disk label): %d heads, %d sectors\n"
1858 "%d cylinders, %d physical cylinders\n"
1859 "%d extra sects/cyl, interleave %d:1\n"
1860 "%s\n"
1861 "Units = %s of %d * 512 bytes\n\n"),
1862 disk_device, heads, sectors, cylinders,
1863 SGI_SSWAP16(sgiparam.pcylcount),
1864 SGI_SSWAP16(sgiparam.sparecyl),
1865 SGI_SSWAP16(sgiparam.ilfact),
1866 (char *)sgilabel,
1867 str_units(PLURAL), units_per_sector);
1868 } else {
1869 printf( _("\nDisk %s (SGI disk label): "
1870 "%d heads, %d sectors, %d cylinders\n"
1871 "Units = %s of %d * 512 bytes\n\n"),
1872 disk_device, heads, sectors, cylinders,
1873 str_units(PLURAL), units_per_sector );
1874 }
1875
1876 w = strlen(disk_device);
1877 wd = strlen(_("Device"));
1878 if (w < wd)
1879 w = wd;
1880
1881 printf(_("----- partitions -----\n"
1882 "Pt# %*s Info Start End Sectors Id System\n"),
1883 w + 2, _("Device"));
1884 for (i = 0 ; i < partitions; i++) {
1885 if( sgi_get_num_sectors(i) || debug ) {
1886 uint32_t start = sgi_get_start_sector(i);
1887 uint32_t len = sgi_get_num_sectors(i);
1888 kpi++; /* only count nonempty partitions */
1889 printf(
1890 "%2d: %s %4s %9ld %9ld %9ld %2x %s\n",
1891/* fdisk part number */ i+1,
1892/* device */ partname(disk_device, kpi, w+3),
1893/* flags */ (sgi_get_swappartition() == i) ? "swap" :
1894/* flags */ (sgi_get_bootpartition() == i) ? "boot" : " ",
1895/* start */ (long) scround(start),
1896/* end */ (long) scround(start+len)-1,
1897/* no odd flag on end */ (long) len,
1898/* type id */ sgi_get_sysid(i),
1899/* type name */ partition_type(sgi_get_sysid(i)));
1900 }
1901 }
1902 printf(_("----- Bootinfo -----\nBootfile: %s\n"
1903 "----- Directory Entries -----\n"),
1904 sgilabel->boot_file);
1905 for (i = 0 ; i < sgi_volumes; i++) {
1906 if (sgilabel->directory[i].vol_file_size) {
1907 uint32_t start = SGI_SSWAP32(sgilabel->directory[i].vol_file_start);
1908 uint32_t len = SGI_SSWAP32(sgilabel->directory[i].vol_file_size);
1909 char*name = sgilabel->directory[i].vol_file_name;
1910
1911 printf(_("%2d: %-10s sector%5u size%8u\n"),
1912 i, name, (unsigned int) start, (unsigned int) len);
1913 }
1914 }
1915}
1916
1917static void
1918sgi_set_bootpartition( int i )
1919{
1920 sgilabel->boot_part = SGI_SSWAP16(((short)i));
1921}
1922
1923static unsigned int
1924sgi_get_lastblock(void) {
1925 return heads * sectors * cylinders;
1926}
1927
1928static void
1929sgi_set_swappartition( int i ) {
1930 sgilabel->swap_part = SGI_SSWAP16(((short)i));
1931}
1932
1933static int
1934sgi_check_bootfile(const char* aFile) {
1935
1936 if (strlen(aFile) < 3) /* "/a\n" is minimum */ {
1937 printf(_("\nInvalid Bootfile!\n"
1938 "\tThe bootfile must be an absolute non-zero pathname,\n"
1939 "\te.g. \"/unix\" or \"/unix.save\".\n"));
1940 return 0;
1941 } else {
1942 if (strlen(aFile) > 16) {
1943 printf(_("\n\tName of Bootfile too long: "
1944 "16 bytes maximum.\n"));
1945 return 0;
1946 } else {
1947 if (aFile[0] != '/') {
1948 printf(_("\n\tBootfile must have a "
1949 "fully qualified pathname.\n"));
1950 return 0;
1951 }
1952 }
1953 }
1954 if (strncmp(aFile, sgilabel->boot_file, 16)) {
1955 printf(_("\n\tBe aware, that the bootfile is not checked for existence.\n\t"
1956 "SGI's default is \"/unix\" and for backup \"/unix.save\".\n"));
1957 /* filename is correct and did change */
1958 return 1;
1959 }
1960 return 0; /* filename did not change */
1961}
1962
1963static const char *
1964sgi_get_bootfile(void) {
1965 return sgilabel->boot_file;
1966}
1967
1968static void
1969sgi_set_bootfile(const char* aFile) {
1970 int i = 0;
1971
1972 if (sgi_check_bootfile(aFile)) {
1973 while (i < 16) {
1974 if ((aFile[i] != '\n') /* in principle caught again by next line */
1975 && (strlen(aFile) > i))
1976 sgilabel->boot_file[i] = aFile[i];
1977 else
1978 sgilabel->boot_file[i] = 0;
1979 i++;
1980 }
1981 printf(_("\n\tBootfile is changed to \"%s\".\n"), sgilabel->boot_file);
1982 }
1983}
1984
1985static void
1986create_sgiinfo(void)
1987{
1988 /* I keep SGI's habit to write the sgilabel to the second block */
1989 sgilabel->directory[0].vol_file_start = SGI_SSWAP32(2);
1990 sgilabel->directory[0].vol_file_size = SGI_SSWAP32(sizeof(sgiinfo));
1991 strncpy(sgilabel->directory[0].vol_file_name, "sgilabel", 8);
1992}
1993
1994static sgiinfo *fill_sgiinfo(void);
1995
1996static void
1997sgi_write_table(void) {
1998 sgilabel->csum = 0;
1999 sgilabel->csum = SGI_SSWAP32(two_s_complement_32bit_sum(
2000 (unsigned int*)sgilabel,
2001 sizeof(*sgilabel)));
2002 assert(two_s_complement_32bit_sum(
2003 (unsigned int*)sgilabel, sizeof(*sgilabel)) == 0);
2004 if (lseek(fd, 0, SEEK_SET) < 0)
2005 fdisk_fatal(unable_to_seek);
2006 if (write(fd, sgilabel, SECTOR_SIZE) != SECTOR_SIZE)
2007 fdisk_fatal(unable_to_write);
2008 if (! strncmp(sgilabel->directory[0].vol_file_name, "sgilabel", 8)) {
2009 /*
2010 * keep this habit of first writing the "sgilabel".
2011 * I never tested whether it works without (AN 981002).
2012 */
2013 sgiinfo *info = fill_sgiinfo();
2014 int infostartblock = SGI_SSWAP32(sgilabel->directory[0].vol_file_start);
2015 if (lseek(fd, infostartblock*SECTOR_SIZE, SEEK_SET) < 0)
2016 fdisk_fatal(unable_to_seek);
2017 if (write(fd, info, SECTOR_SIZE) != SECTOR_SIZE)
2018 fdisk_fatal(unable_to_write);
2019 free(info);
2020 }
2021}
2022
2023static int
2024compare_start(int *x, int *y) {
2025 /*
2026 * sort according to start sectors
2027 * and prefers largest partition:
2028 * entry zero is entire disk entry
2029 */
2030 unsigned int i = *x;
2031 unsigned int j = *y;
2032 unsigned int a = sgi_get_start_sector(i);
2033 unsigned int b = sgi_get_start_sector(j);
2034 unsigned int c = sgi_get_num_sectors(i);
2035 unsigned int d = sgi_get_num_sectors(j);
2036
2037 if (a == b)
2038 return (d > c) ? 1 : (d == c) ? 0 : -1;
2039 return (a > b) ? 1 : -1;
2040}
2041
2042
2043static int
2044verify_sgi(int verbose)
2045{
2046 int Index[16]; /* list of valid partitions */
2047 int sortcount = 0; /* number of used partitions, i.e. non-zero lengths */
2048 int entire = 0, i = 0;
2049 unsigned int start = 0;
2050 long long gap = 0; /* count unused blocks */
2051 unsigned int lastblock = sgi_get_lastblock();
2052
2053 clearfreelist();
2054 for (i=0; i<16; i++) {
2055 if (sgi_get_num_sectors(i) != 0) {
2056 Index[sortcount++]=i;
2057 if (sgi_get_sysid(i) == ENTIRE_DISK) {
2058 if (entire++ == 1) {
2059 if (verbose)
2060 printf(_("More than one entire disk entry present.\n"));
2061 }
2062 }
2063 }
2064 }
2065 if (sortcount == 0) {
2066 if (verbose)
2067 printf(_("No partitions defined\n"));
2068 return (lastblock > 0) ? 1 : (lastblock == 0) ? 0 : -1;
2069 }
2070 qsort(Index, sortcount, sizeof(Index[0]), (void*)compare_start);
2071 if (sgi_get_sysid(Index[0]) == ENTIRE_DISK) {
2072 if ((Index[0] != 10) && verbose)
2073 printf(_("IRIX likes when Partition 11 covers the entire disk.\n"));
2074 if ((sgi_get_start_sector(Index[0]) != 0) && verbose)
2075 printf(_("The entire disk partition should start "
2076 "at block 0,\n"
2077 "not at diskblock %d.\n"),
2078 sgi_get_start_sector(Index[0]));
2079 if (debug) /* I do not understand how some disks fulfil it */
2080 if ((sgi_get_num_sectors(Index[0]) != lastblock) && verbose)
2081 printf(_("The entire disk partition is only %d diskblock large,\n"
2082 "but the disk is %d diskblocks long.\n"),
2083 sgi_get_num_sectors(Index[0]), lastblock);
2084 lastblock = sgi_get_num_sectors(Index[0]);
2085 } else {
2086 if (verbose)
2087 printf(_("One Partition (#11) should cover the entire disk.\n"));
2088 if (debug>2)
2089 printf("sysid=%d\tpartition=%d\n",
2090 sgi_get_sysid(Index[0]), Index[0]+1);
2091 }
2092 for (i=1, start=0; i<sortcount; i++) {
2093 int cylsize = sgi_get_nsect() * sgi_get_ntrks();
2094
2095 if ((sgi_get_start_sector(Index[i]) % cylsize) != 0) {
2096 if (debug) /* I do not understand how some disks fulfil it */
2097 if (verbose)
2098 printf(_("Partition %d does not start on cylinder boundary.\n"),
2099 Index[i]+1);
2100 }
2101 if (sgi_get_num_sectors(Index[i]) % cylsize != 0) {
2102 if (debug) /* I do not understand how some disks fulfil it */
2103 if (verbose)
2104 printf(_("Partition %d does not end on cylinder boundary.\n"),
2105 Index[i]+1);
2106 }
2107 /* We cannot handle several "entire disk" entries. */
2108 if (sgi_get_sysid(Index[i]) == ENTIRE_DISK) continue;
2109 if (start > sgi_get_start_sector(Index[i])) {
2110 if (verbose)
2111 printf(_("The Partition %d and %d overlap by %d sectors.\n"),
2112 Index[i-1]+1, Index[i]+1,
2113 start - sgi_get_start_sector(Index[i]));
2114 if (gap > 0) gap = -gap;
2115 if (gap == 0) gap = -1;
2116 }
2117 if (start < sgi_get_start_sector(Index[i])) {
2118 if (verbose)
2119 printf(_("Unused gap of %8u sectors - sectors %8u-%u\n"),
2120 sgi_get_start_sector(Index[i]) - start,
2121 start, sgi_get_start_sector(Index[i])-1);
2122 gap += sgi_get_start_sector(Index[i]) - start;
2123 add2freelist(start, sgi_get_start_sector(Index[i]));
2124 }
2125 start = sgi_get_start_sector(Index[i])
2126 + sgi_get_num_sectors(Index[i]);
2127 if (debug > 1) {
2128 if (verbose)
2129 printf("%2d:%12d\t%12d\t%12d\n", Index[i],
2130 sgi_get_start_sector(Index[i]),
2131 sgi_get_num_sectors(Index[i]),
2132 sgi_get_sysid(Index[i]));
2133 }
2134 }
2135 if (start < lastblock) {
2136 if (verbose)
2137 printf(_("Unused gap of %8u sectors - sectors %8u-%u\n"),
2138 lastblock - start, start, lastblock-1);
2139 gap += lastblock - start;
2140 add2freelist(start, lastblock);
2141 }
2142 /*
2143 * Done with arithmetics
2144 * Go for details now
2145 */
2146 if (verbose) {
2147 if (!sgi_get_num_sectors(sgi_get_bootpartition())) {
2148 printf(_("\nThe boot partition does not exist.\n"));
2149 }
2150 if (!sgi_get_num_sectors(sgi_get_swappartition())) {
2151 printf(_("\nThe swap partition does not exist.\n"));
2152 } else {
2153 if ((sgi_get_sysid(sgi_get_swappartition()) != SGI_SWAP)
2154 && (sgi_get_sysid(sgi_get_swappartition()) != LINUX_SWAP))
2155 printf(_("\nThe swap partition has no swap type.\n"));
2156 }
2157 if (sgi_check_bootfile("/unix"))
2158 printf(_("\tYou have chosen an unusual boot file name.\n"));
2159 }
2160 return (gap > 0) ? 1 : (gap == 0) ? 0 : -1;
2161}
2162
2163static int
2164sgi_gaps(void) {
2165 /*
2166 * returned value is:
2167 * = 0 : disk is properly filled to the rim
2168 * < 0 : there is an overlap
2169 * > 0 : there is still some vacant space
2170 */
2171 return verify_sgi(0);
2172}
2173
2174static void
2175sgi_change_sysid( int i, int sys )
2176{
2177 if( sgi_get_num_sectors(i) == 0 ) /* caught already before, ... */
2178 {
2179 printf(_("Sorry You may change the Tag of non-empty partitions.\n"));
2180 return;
2181 }
2182 if( ((sys != ENTIRE_DISK ) && (sys != SGI_VOLHDR))
2183 && (sgi_get_start_sector(i)<1) )
2184 {
2185 read_chars(
2186 _("It is highly recommended that the partition at offset 0\n"
2187 "is of type \"SGI volhdr\", the IRIX system will rely on it to\n"
2188 "retrieve from its directory standalone tools like sash and fx.\n"
2189 "Only the \"SGI volume\" entire disk section may violate this.\n"
2190 "Type YES if you are sure about tagging this partition differently.\n"));
2191 if (strcmp (line_ptr, _("YES\n")))
2192 return;
2193 }
2194 sgilabel->partitions[i].id = SGI_SSWAP32(sys);
2195}
2196
2197/* returns partition index of first entry marked as entire disk */
2198static int
2199sgi_entire(void) {
2200 int i;
2201
2202 for(i=0; i<16; i++)
2203 if(sgi_get_sysid(i) == SGI_VOLUME)
2204 return i;
2205 return -1;
2206}
2207
2208static void
2209sgi_set_partition(int i, unsigned int start, unsigned int length, int sys) {
2210
2211 sgilabel->partitions[i].id = SGI_SSWAP32(sys);
2212 sgilabel->partitions[i].num_sectors = SGI_SSWAP32(length);
2213 sgilabel->partitions[i].start_sector = SGI_SSWAP32(start);
2214 set_changed(i);
2215 if (sgi_gaps() < 0) /* rebuild freelist */
2216 printf(_("Do You know, You got a partition overlap on the disk?\n"));
2217}
2218
2219static void
2220sgi_set_entire(void) {
2221 int n;
2222
2223 for(n=10; n < partitions; n++) {
2224 if(!sgi_get_num_sectors(n) ) {
2225 sgi_set_partition(n, 0, sgi_get_lastblock(), SGI_VOLUME);
2226 break;
2227 }
2228 }
2229}
2230
2231static void
2232sgi_set_volhdr(void)
2233{
2234 int n;
2235 for( n=8; n<partitions; n++ )
2236 {
2237 if(!sgi_get_num_sectors( n ) )
2238 {
2239 /*
2240 * 5 cylinders is an arbitrary value I like
2241 * IRIX 5.3 stored files in the volume header
2242 * (like sash, symmon, fx, ide) with ca. 3200
2243 * sectors.
2244 */
2245 if( heads * sectors * 5 < sgi_get_lastblock() )
2246 sgi_set_partition( n, 0, heads * sectors * 5, SGI_VOLHDR );
2247 break;
2248 }
2249 }
2250}
2251
2252static void
2253sgi_delete_partition( int i )
2254{
2255 sgi_set_partition( i, 0, 0, 0 );
2256}
2257
2258static void
2259sgi_add_partition( int n, int sys )
2260{
2261 char mesg[256];
2262 unsigned int first=0, last=0;
2263
2264 if( n == 10 ) {
2265 sys = SGI_VOLUME;
2266 } else if ( n == 8 ) {
2267 sys = 0;
2268 }
2269 if(sgi_get_num_sectors(n)) {
2270 printf(_("Partition %d is already defined. Delete "
2271 "it before re-adding it.\n"), n + 1);
2272 return;
2273 }
2274 if( (sgi_entire() == -1) && (sys != SGI_VOLUME) ) {
2275 printf(_("Attempting to generate entire disk entry automatically.\n"));
2276 sgi_set_entire();
2277 sgi_set_volhdr();
2278 }
2279 if( (sgi_gaps() == 0) && (sys != SGI_VOLUME) ) {
2280 printf(_("The entire disk is already covered with partitions.\n"));
2281 return;
2282 }
2283 if(sgi_gaps() < 0) {
2284 printf(_("You got a partition overlap on the disk. Fix it first!\n"));
2285 return;
2286 }
2287 snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR));
2288 for(;;) {
2289 if(sys == SGI_VOLUME) {
2290 last = sgi_get_lastblock();
2291 first = read_int(0, 0, last-1, 0, mesg);
2292 if( first != 0 ) {
2293 printf(_("It is highly recommended that eleventh partition\n"
2294 "covers the entire disk and is of type `SGI volume'\n"));
2295 }
2296 } else {
2297 first = freelist[0].first;
2298 last = freelist[0].last;
2299 first = read_int(scround(first), scround(first), scround(last)-1,
2300 0, mesg);
2301 }
2302 if (display_in_cyl_units)
2303 first *= units_per_sector;
2304 else
2305 first = first; /* align to cylinder if you know how ... */
2306 if( !last )
2307 last = isinfreelist(first);
2308 if( last == 0 ) {
2309 printf(_("You will get a partition overlap on the disk. "
2310 "Fix it first!\n"));
2311 } else
2312 break;
2313 }
2314 snprintf(mesg, sizeof(mesg), _(" Last %s"), str_units(SINGULAR));
2315 last = read_int(scround(first), scround(last)-1, scround(last)-1,
2316 scround(first), mesg)+1;
2317 if (display_in_cyl_units)
2318 last *= units_per_sector;
2319 else
2320 last = last; /* align to cylinder if You know how ... */
2321 if( (sys == SGI_VOLUME) && ( first != 0 || last != sgi_get_lastblock() ) )
2322 printf(_("It is highly recommended that eleventh partition\n"
2323 "covers the entire disk and is of type `SGI volume'\n"));
2324 sgi_set_partition( n, first, last-first, sys );
2325}
2326
2327#ifdef CONFIG_FEATURE_FDISK_ADVANCED
2328static void
2329create_sgilabel(void)
2330{
2331 struct hd_geometry geometry;
2332 struct {
2333 unsigned int start;
2334 unsigned int nsect;
2335 int sysid;
2336 } old[4];
2337 int i=0;
2338 long longsectors; /* the number of sectors on the device */
2339 int res; /* the result from the ioctl */
2340 int sec_fac; /* the sector factor */
2341
2342 sec_fac = sector_size / 512; /* determine the sector factor */
2343
2344 fprintf( stderr,
2345 _("Building a new SGI disklabel. Changes will remain in memory only,\n"
2346 "until you decide to write them. After that, of course, the previous\n"
2347 "content will be unrecoverably lost.\n\n"));
2348
2349 sgi_other_endian = (BYTE_ORDER == LITTLE_ENDIAN);
2350 res = ioctl(fd, BLKGETSIZE, &longsectors);
2351 if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
2352 heads = geometry.heads;
2353 sectors = geometry.sectors;
2354 if (res == 0) {
2355 /* the get device size ioctl was successful */
2356 cylinders = longsectors / (heads * sectors);
2357 cylinders /= sec_fac;
2358 } else {
2359 /* otherwise print error and use truncated version */
2360 cylinders = geometry.cylinders;
2361 fprintf(stderr,
2362 _("Warning: BLKGETSIZE ioctl failed on %s. "
2363 "Using geometry cylinder value of %d.\n"
2364 "This value may be truncated for devices"
2365 " > 33.8 GB.\n"), disk_device, cylinders);
2366 }
2367 }
2368 for (i = 0; i < 4; i++) {
2369 old[i].sysid = 0;
2370 if(valid_part_table_flag(MBRbuffer)) {
2371 if(get_part_table(i)->sys_ind) {
2372 old[i].sysid = get_part_table(i)->sys_ind;
2373 old[i].start = get_start_sect(get_part_table(i));
2374 old[i].nsect = get_nr_sects(get_part_table(i));
2375 printf(_("Trying to keep parameters of partition %d.\n"), i);
2376 if (debug)
2377 printf(_("ID=%02x\tSTART=%d\tLENGTH=%d\n"),
2378 old[i].sysid, old[i].start, old[i].nsect);
2379 }
2380 }
2381 }
2382
2383 memset(MBRbuffer, 0, sizeof(MBRbuffer));
2384 sgilabel->magic = SGI_SSWAP32(SGI_LABEL_MAGIC);
2385 sgilabel->boot_part = SGI_SSWAP16(0);
2386 sgilabel->swap_part = SGI_SSWAP16(1);
2387
2388 /* sizeof(sgilabel->boot_file) = 16 > 6 */
2389 memset(sgilabel->boot_file, 0, 16);
2390 strcpy(sgilabel->boot_file, "/unix");
2391
2392 sgilabel->devparam.skew = (0);
2393 sgilabel->devparam.gap1 = (0);
2394 sgilabel->devparam.gap2 = (0);
2395 sgilabel->devparam.sparecyl = (0);
2396 sgilabel->devparam.pcylcount = SGI_SSWAP16(geometry.cylinders);
2397 sgilabel->devparam.head_vol0 = SGI_SSWAP16(0);
2398 sgilabel->devparam.ntrks = SGI_SSWAP16(geometry.heads);
2399 /* tracks/cylinder (heads) */
2400 sgilabel->devparam.cmd_tag_queue_depth = (0);
2401 sgilabel->devparam.unused0 = (0);
2402 sgilabel->devparam.unused1 = SGI_SSWAP16(0);
2403 sgilabel->devparam.nsect = SGI_SSWAP16(geometry.sectors);
2404 /* sectors/track */
2405 sgilabel->devparam.bytes = SGI_SSWAP16(512);
2406 sgilabel->devparam.ilfact = SGI_SSWAP16(1);
2407 sgilabel->devparam.flags = SGI_SSWAP32(TRACK_FWD|
2408 IGNORE_ERRORS|RESEEK);
2409 sgilabel->devparam.datarate = SGI_SSWAP32(0);
2410 sgilabel->devparam.retries_on_error = SGI_SSWAP32(1);
2411 sgilabel->devparam.ms_per_word = SGI_SSWAP32(0);
2412 sgilabel->devparam.xylogics_gap1 = SGI_SSWAP16(0);
2413 sgilabel->devparam.xylogics_syncdelay = SGI_SSWAP16(0);
2414 sgilabel->devparam.xylogics_readdelay = SGI_SSWAP16(0);
2415 sgilabel->devparam.xylogics_gap2 = SGI_SSWAP16(0);
2416 sgilabel->devparam.xylogics_readgate = SGI_SSWAP16(0);
2417 sgilabel->devparam.xylogics_writecont = SGI_SSWAP16(0);
2418 memset( &(sgilabel->directory), 0, sizeof(struct volume_directory)*15 );
2419 memset( &(sgilabel->partitions), 0, sizeof(struct sgi_partition)*16 );
2420 sgi_label = 1;
2421 partitions = 16;
2422 sgi_volumes = 15;
2423 sgi_set_entire();
2424 sgi_set_volhdr();
2425 for (i = 0; i < 4; i++) {
2426 if(old[i].sysid) {
2427 sgi_set_partition(i, old[i].start, old[i].nsect, old[i].sysid);
2428 }
2429 }
2430}
2431
2432static void
2433sgi_set_xcyl(void)
2434{
2435 /* do nothing in the beginning */
2436}
2437#endif /* CONFIG_FEATURE_FDISK_ADVANCED */
2438
2439/* _____________________________________________________________
2440 */
2441
2442static sgiinfo *
2443fill_sgiinfo(void)
2444{
2445 sgiinfo *info = calloc(1, sizeof(sgiinfo));
2446
2447 info->magic=SGI_SSWAP32(SGI_INFO_MAGIC);
2448 info->b1=SGI_SSWAP32(-1);
2449 info->b2=SGI_SSWAP16(-1);
2450 info->b3=SGI_SSWAP16(1);
2451 /* You may want to replace this string !!!!!!! */
2452 strcpy( info->scsi_string, "IBM OEM 0662S12 3 30" );
2453 strcpy( info->serial, "0000" );
2454 info->check1816 = SGI_SSWAP16(18*256 +16 );
2455 strcpy( info->installer, "Sfx version 5.3, Oct 18, 1994" );
2456 return info;
2457}
2458#endif /* SGI_LABEL */
2459
2460
2461#ifdef CONFIG_FEATURE_SUN_LABEL
2462/*
2463 * fdisksunlabel.c
2464 *
2465 * I think this is mostly, or entirely, due to
2466 * Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996
2467 *
2468 * Merged with fdisk for other architectures, aeb, June 1998.
2469 *
2470 * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
2471 * Internationalization
2472 */
2473
2474
2475static int sun_other_endian;
2476static int scsi_disk;
2477static int floppy;
2478
2479#ifndef IDE0_MAJOR
2480#define IDE0_MAJOR 3
2481#endif
2482#ifndef IDE1_MAJOR
2483#define IDE1_MAJOR 22
2484#endif
2485
2486static void guess_device_type(void) {
2487 struct stat bootstat;
2488
2489 if (fstat (fd, &bootstat) < 0) {
2490 scsi_disk = 0;
2491 floppy = 0;
2492 } else if (S_ISBLK(bootstat.st_mode)
2493 && (major(bootstat.st_rdev) == IDE0_MAJOR ||
2494 major(bootstat.st_rdev) == IDE1_MAJOR)) {
2495 scsi_disk = 0;
2496 floppy = 0;
2497 } else if (S_ISBLK(bootstat.st_mode)
2498 && major(bootstat.st_rdev) == FLOPPY_MAJOR) {
2499 scsi_disk = 0;
2500 floppy = 1;
2501 } else {
2502 scsi_disk = 1;
2503 floppy = 0;
2504 }
2505}
2506
2507static const struct systypes sun_sys_types[] = {
2508/* 0 */ {"\x00" "Empty" },
2509/* 1 */ {"\x01" "Boot" },
2510/* 2 */ {"\x02" "SunOS root" },
2511/* SUNOS_SWAP */ {"\x03" "SunOS swap" },
2512/* 4 */ {"\x04" "SunOS usr" },
2513/* WHOLE_DISK */ {"\x05" "Whole disk" },
2514/* 6 */ {"\x06" "SunOS stand" },
2515/* 7 */ {"\x07" "SunOS var" },
2516/* 8 */ {"\x08" "SunOS home" },
2517/* LINUX_SWAP */ {"\x82" "Linux swap" },
2518/* LINUX_NATIVE */ {"\x83" "Linux native" },
2519/* 0x8e */ {"\x8e" "Linux LVM" },
2520/* New (2.2.x) raid partition with autodetect using persistent superblock */
2521/* 0xfd */ {"\xfd" "Linux raid autodetect" },
2522 { NULL }
2523};
2524
2525
2526static void
2527set_sun_partition(int i, uint start, uint stop, int sysid) {
2528 sunlabel->infos[i].id = sysid;
2529 sunlabel->partitions[i].start_cylinder =
2530 SUN_SSWAP32(start / (heads * sectors));
2531 sunlabel->partitions[i].num_sectors =
2532 SUN_SSWAP32(stop - start);
2533 set_changed(i);
2534}
2535
2536static void
2537sun_nolabel(void) {
2538 sun_label = 0;
2539 sunlabel->magic = 0;
2540 partitions = 4;
2541}
2542
2543static int
2544check_sun_label(void) {
2545 unsigned short *ush;
2546 int csum;
2547
2548 if (sunlabel->magic != SUN_LABEL_MAGIC &&
2549 sunlabel->magic != SUN_LABEL_MAGIC_SWAPPED) {
2550 sun_label = 0;
2551 sun_other_endian = 0;
2552 return 0;
2553 }
2554 sun_other_endian = (sunlabel->magic == SUN_LABEL_MAGIC_SWAPPED);
2555 ush = ((unsigned short *) (sunlabel + 1)) - 1;
2556 for (csum = 0; ush >= (unsigned short *)sunlabel;) csum ^= *ush--;
2557 if (csum) {
2558 fprintf(stderr,_("Detected sun disklabel with wrong checksum.\n"
2559 "Probably you'll have to set all the values,\n"
2560 "e.g. heads, sectors, cylinders and partitions\n"
2561 "or force a fresh label (s command in main menu)\n"));
2562 } else {
2563 heads = SUN_SSWAP16(sunlabel->ntrks);
2564 cylinders = SUN_SSWAP16(sunlabel->ncyl);
2565 sectors = SUN_SSWAP16(sunlabel->nsect);
2566 }
2567 update_units();
2568 sun_label = 1;
2569 partitions = 8;
2570 return 1;
2571}
2572
2573static const struct sun_predefined_drives {
2574 const char *vendor;
2575 const char *model;
2576 unsigned short sparecyl;
2577 unsigned short ncyl;
2578 unsigned short nacyl;
2579 unsigned short pcylcount;
2580 unsigned short ntrks;
2581 unsigned short nsect;
2582 unsigned short rspeed;
2583} sun_drives[] = {
2584{"Quantum","ProDrive 80S",1,832,2,834,6,34,3662},
2585{"Quantum","ProDrive 105S",1,974,2,1019,6,35,3662},
2586{"CDC","Wren IV 94171-344",3,1545,2,1549,9,46,3600},
2587{"IBM","DPES-31080",0,4901,2,4903,4,108,5400},
2588{"IBM","DORS-32160",0,1015,2,1017,67,62,5400},
2589{"IBM","DNES-318350",0,11199,2,11474,10,320,7200},
2590{"SEAGATE","ST34371",0,3880,2,3882,16,135,7228},
2591{"","SUN0104",1,974,2,1019,6,35,3662},
2592{"","SUN0207",4,1254,2,1272,9,36,3600},
2593{"","SUN0327",3,1545,2,1549,9,46,3600},
2594{"","SUN0340",0,1538,2,1544,6,72,4200},
2595{"","SUN0424",2,1151,2,2500,9,80,4400},
2596{"","SUN0535",0,1866,2,2500,7,80,5400},
2597{"","SUN0669",5,1614,2,1632,15,54,3600},
2598{"","SUN1.0G",5,1703,2,1931,15,80,3597},
2599{"","SUN1.05",0,2036,2,2038,14,72,5400},
2600{"","SUN1.3G",6,1965,2,3500,17,80,5400},
2601{"","SUN2.1G",0,2733,2,3500,19,80,5400},
2602{"IOMEGA","Jaz",0,1019,2,1021,64,32,5394},
2603};
2604
2605static const struct sun_predefined_drives *
2606sun_autoconfigure_scsi(void) {
2607 const struct sun_predefined_drives *p = NULL;
2608
2609#ifdef SCSI_IOCTL_GET_IDLUN
2610 unsigned int id[2];
2611 char buffer[2048];
2612 char buffer2[2048];
2613 FILE *pfd;
2614 char *vendor;
2615 char *model;
2616 char *q;
2617 int i;
2618
2619 if (!ioctl(fd, SCSI_IOCTL_GET_IDLUN, &id)) {
2620 sprintf(buffer,
2621 "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n",
2622#if 0
2623 ((id[0]>>24)&0xff)-/*PROC_SCSI_SCSI+PROC_SCSI_FILE*/33,
2624#else
2625 /* This is very wrong (works only if you have one HBA),
2626 but I haven't found a way how to get hostno
2627 from the current kernel */
2628 0,
2629#endif
2630 (id[0]>>16)&0xff,
2631 id[0]&0xff,
2632 (id[0]>>8)&0xff);
2633 pfd = fopen("/proc/scsi/scsi","r");
2634 if (pfd) {
2635 while (fgets(buffer2,2048,pfd)) {
2636 if (!strcmp(buffer, buffer2)) {
2637 if (fgets(buffer2,2048,pfd)) {
2638 q = strstr(buffer2,"Vendor: ");
2639 if (q) {
2640 q += 8;
2641 vendor = q;
2642 q = strstr(q," ");
2643 *q++ = 0; /* truncate vendor name */
2644 q = strstr(q,"Model: ");
2645 if (q) {
2646 *q = 0;
2647 q += 7;
2648 model = q;
2649 q = strstr(q," Rev: ");
2650 if (q) {
2651 *q = 0;
2652 for (i = 0; i < SIZE(sun_drives); i++) {
2653 if (*sun_drives[i].vendor && strcasecmp(sun_drives[i].vendor, vendor))
2654 continue;
2655 if (!strstr(model, sun_drives[i].model))
2656 continue;
2657 printf(_("Autoconfigure found a %s%s%s\n"),sun_drives[i].vendor,(*sun_drives[i].vendor) ? " " : "",sun_drives[i].model);
2658 p = sun_drives + i;
2659 break;
2660 }
2661 }
2662 }
2663 }
2664 }
2665 break;
2666 }
2667 }
2668 fclose(pfd);
2669 }
2670 }
2671#endif
2672 return p;
2673}
2674
2675static void create_sunlabel(void)
2676{
2677 struct hd_geometry geometry;
2678 unsigned int ndiv;
2679 int i;
2680 unsigned char c;
2681 const struct sun_predefined_drives *p = NULL;
2682
2683 fprintf(stderr,
2684 _("Building a new sun disklabel. Changes will remain in memory only,\n"
2685 "until you decide to write them. After that, of course, the previous\n"
2686 "content won't be recoverable.\n\n"));
2687#if BYTE_ORDER == LITTLE_ENDIAN
2688 sun_other_endian = 1;
2689#else
2690 sun_other_endian = 0;
2691#endif
2692 memset(MBRbuffer, 0, sizeof(MBRbuffer));
2693 sunlabel->magic = SUN_SSWAP16(SUN_LABEL_MAGIC);
2694 if (!floppy) {
2695 puts(_("Drive type\n"
2696 " ? auto configure\n"
2697 " 0 custom (with hardware detected defaults)"));
2698 for (i = 0; i < SIZE(sun_drives); i++) {
2699 printf(" %c %s%s%s\n",
2700 i + 'a', sun_drives[i].vendor,
2701 (*sun_drives[i].vendor) ? " " : "",
2702 sun_drives[i].model);
2703 }
2704 for (;;) {
2705 c = read_char(_("Select type (? for auto, 0 for custom): "));
2706 if (c >= 'a' && c < 'a' + SIZE(sun_drives)) {
2707 p = sun_drives + c - 'a';
2708 break;
2709 } else if (c >= 'A' && c < 'A' + SIZE(sun_drives)) {
2710 p = sun_drives + c - 'A';
2711 break;
2712 } else if (c == '0') {
2713 break;
2714 } else if (c == '?' && scsi_disk) {
2715 p = sun_autoconfigure_scsi();
2716 if (!p)
2717 printf(_("Autoconfigure failed.\n"));
2718 else
2719 break;
2720 }
2721 }
2722 }
2723 if (!p || floppy) {
2724 if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
2725 heads = geometry.heads;
2726 sectors = geometry.sectors;
2727 cylinders = geometry.cylinders;
2728 } else {
2729 heads = 0;
2730 sectors = 0;
2731 cylinders = 0;
2732 }
2733 if (floppy) {
2734 sunlabel->nacyl = 0;
2735 sunlabel->pcylcount = SUN_SSWAP16(cylinders);
2736 sunlabel->rspeed = SUN_SSWAP16(300);
2737 sunlabel->ilfact = SUN_SSWAP16(1);
2738 sunlabel->sparecyl = 0;
2739 } else {
2740 heads = read_int(1,heads,1024,0,_("Heads"));
2741 sectors = read_int(1,sectors,1024,0,_("Sectors/track"));
2742 if (cylinders)
2743 cylinders = read_int(1,cylinders-2,65535,0,_("Cylinders"));
2744 else
2745 cylinders = read_int(1,0,65535,0,_("Cylinders"));
2746 sunlabel->nacyl =
2747 SUN_SSWAP16(read_int(0,2,65535,0,
2748 _("Alternate cylinders")));
2749 sunlabel->pcylcount =
2750 SUN_SSWAP16(read_int(0,cylinders+SUN_SSWAP16(sunlabel->nacyl),
2751 65535,0,_("Physical cylinders")));
2752 sunlabel->rspeed =
2753 SUN_SSWAP16(read_int(1,5400,100000,0,
2754 _("Rotation speed (rpm)")));
2755 sunlabel->ilfact =
2756 SUN_SSWAP16(read_int(1,1,32,0,_("Interleave factor")));
2757 sunlabel->sparecyl =
2758 SUN_SSWAP16(read_int(0,0,sectors,0,
2759 _("Extra sectors per cylinder")));
2760 }
2761 } else {
2762 sunlabel->sparecyl = SUN_SSWAP16(p->sparecyl);
2763 sunlabel->ncyl = SUN_SSWAP16(p->ncyl);
2764 sunlabel->nacyl = SUN_SSWAP16(p->nacyl);
2765 sunlabel->pcylcount = SUN_SSWAP16(p->pcylcount);
2766 sunlabel->ntrks = SUN_SSWAP16(p->ntrks);
2767 sunlabel->nsect = SUN_SSWAP16(p->nsect);
2768 sunlabel->rspeed = SUN_SSWAP16(p->rspeed);
2769 sunlabel->ilfact = SUN_SSWAP16(1);
2770 cylinders = p->ncyl;
2771 heads = p->ntrks;
2772 sectors = p->nsect;
2773 puts(_("You may change all the disk params from the x menu"));
2774 }
2775
2776 snprintf(sunlabel->info, sizeof(sunlabel->info),
2777 "%s%s%s cyl %d alt %d hd %d sec %d",
2778 p ? p->vendor : "", (p && *p->vendor) ? " " : "",
2779 p ? p->model
2780 : (floppy ? _("3,5\" floppy") : _("Linux custom")),
2781 cylinders, SUN_SSWAP16(sunlabel->nacyl), heads, sectors);
2782
2783 sunlabel->ntrks = SUN_SSWAP16(heads);
2784 sunlabel->nsect = SUN_SSWAP16(sectors);
2785 sunlabel->ncyl = SUN_SSWAP16(cylinders);
2786 if (floppy)
2787 set_sun_partition(0, 0, cylinders * heads * sectors, LINUX_NATIVE);
2788 else {
2789 if (cylinders * heads * sectors >= 150 * 2048) {
2790 ndiv = cylinders - (50 * 2048 / (heads * sectors)); /* 50M swap */
2791 } else
2792 ndiv = cylinders * 2 / 3;
2793 set_sun_partition(0, 0, ndiv * heads * sectors, LINUX_NATIVE);
2794 set_sun_partition(1, ndiv * heads * sectors, cylinders * heads * sectors, LINUX_SWAP);
2795 sunlabel->infos[1].flags |= 0x01; /* Not mountable */
2796 }
2797 set_sun_partition(2, 0, cylinders * heads * sectors, WHOLE_DISK);
2798 {
2799 unsigned short *ush = (unsigned short *)sunlabel;
2800 unsigned short csum = 0;
2801 while(ush < (unsigned short *)(&sunlabel->csum))
2802 csum ^= *ush++;
2803 sunlabel->csum = csum;
2804 }
2805
2806 set_all_unchanged();
2807 set_changed(0);
2808 get_boot(create_empty_sun);
2809}
2810
2811static void
2812toggle_sunflags(int i, unsigned char mask) {
2813 if (sunlabel->infos[i].flags & mask)
2814 sunlabel->infos[i].flags &= ~mask;
2815 else sunlabel->infos[i].flags |= mask;
2816 set_changed(i);
2817}
2818
2819static void
2820fetch_sun(uint *starts, uint *lens, uint *start, uint *stop) {
2821 int i, continuous = 1;
2822 *start = 0; *stop = cylinders * heads * sectors;
2823 for (i = 0; i < partitions; i++) {
2824 if (sunlabel->partitions[i].num_sectors
2825 && sunlabel->infos[i].id
2826 && sunlabel->infos[i].id != WHOLE_DISK) {
2827 starts[i] = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * heads * sectors;
2828 lens[i] = SUN_SSWAP32(sunlabel->partitions[i].num_sectors);
2829 if (continuous) {
2830 if (starts[i] == *start)
2831 *start += lens[i];
2832 else if (starts[i] + lens[i] >= *stop)
2833 *stop = starts[i];
2834 else
2835 continuous = 0;
2836 /* There will be probably more gaps
2837 than one, so lets check afterwards */
2838 }
2839 } else {
2840 starts[i] = 0;
2841 lens[i] = 0;
2842 }
2843 }
2844}
2845
2846static uint *verify_sun_starts;
2847
2848static int
2849verify_sun_cmp(int *a, int *b) {
2850 if (*a == -1) return 1;
2851 if (*b == -1) return -1;
2852 if (verify_sun_starts[*a] > verify_sun_starts[*b]) return 1;
2853 return -1;
2854}
2855
2856static void
2857verify_sun(void) {
2858 uint starts[8], lens[8], start, stop;
2859 int i,j,k,starto,endo;
2860 int array[8];
2861
2862 verify_sun_starts = starts;
2863 fetch_sun(starts,lens,&start,&stop);
2864 for (k = 0; k < 7; k++) {
2865 for (i = 0; i < 8; i++) {
2866 if (k && (lens[i] % (heads * sectors))) {
2867 printf(_("Partition %d doesn't end on cylinder boundary\n"), i+1);
2868 }
2869 if (lens[i]) {
2870 for (j = 0; j < i; j++)
2871 if (lens[j]) {
2872 if (starts[j] == starts[i]+lens[i]) {
2873 starts[j] = starts[i]; lens[j] += lens[i];
2874 lens[i] = 0;
2875 } else if (starts[i] == starts[j]+lens[j]){
2876 lens[j] += lens[i];
2877 lens[i] = 0;
2878 } else if (!k) {
2879 if (starts[i] < starts[j]+lens[j] &&
2880 starts[j] < starts[i]+lens[i]) {
2881 starto = starts[i];
2882 if (starts[j] > starto)
2883 starto = starts[j];
2884 endo = starts[i]+lens[i];
2885 if (starts[j]+lens[j] < endo)
2886 endo = starts[j]+lens[j];
2887 printf(_("Partition %d overlaps with others in "
2888 "sectors %d-%d\n"), i+1, starto, endo);
2889 }
2890 }
2891 }
2892 }
2893 }
2894 }
2895 for (i = 0; i < 8; i++) {
2896 if (lens[i])
2897 array[i] = i;
2898 else
2899 array[i] = -1;
2900 }
2901 qsort(array,SIZE(array),sizeof(array[0]),
2902 (int (*)(const void *,const void *)) verify_sun_cmp);
2903 if (array[0] == -1) {
2904 printf(_("No partitions defined\n"));
2905 return;
2906 }
2907 stop = cylinders * heads * sectors;
2908 if (starts[array[0]])
2909 printf(_("Unused gap - sectors 0-%d\n"),starts[array[0]]);
2910 for (i = 0; i < 7 && array[i+1] != -1; i++) {
2911 printf(_("Unused gap - sectors %d-%d\n"),starts[array[i]]+lens[array[i]],starts[array[i+1]]);
2912 }
2913 start = starts[array[i]]+lens[array[i]];
2914 if (start < stop)
2915 printf(_("Unused gap - sectors %d-%d\n"),start,stop);
2916}
2917
2918static void
2919add_sun_partition(int n, int sys) {
2920 uint start, stop, stop2;
2921 uint starts[8], lens[8];
2922 int whole_disk = 0;
2923
2924 char mesg[256];
2925 int i, first, last;
2926
2927 if (sunlabel->partitions[n].num_sectors && sunlabel->infos[n].id) {
2928 printf(_("Partition %d is already defined. Delete "
2929 "it before re-adding it.\n"), n + 1);
2930 return;
2931 }
2932
2933 fetch_sun(starts,lens,&start,&stop);
2934 if (stop <= start) {
2935 if (n == 2)
2936 whole_disk = 1;
2937 else {
2938 printf(_("Other partitions already cover the whole disk.\nDelete "
2939 "some/shrink them before retry.\n"));
2940 return;
2941 }
2942 }
2943 snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR));
2944 for (;;) {
2945 if (whole_disk)
2946 first = read_int(0, 0, 0, 0, mesg);
2947 else
2948 first = read_int(scround(start), scround(stop)+1,
2949 scround(stop), 0, mesg);
2950 if (display_in_cyl_units)
2951 first *= units_per_sector;
2952 else
2953 /* Starting sector has to be properly aligned */
2954 first = (first + heads * sectors - 1) / (heads * sectors);
2955 if (n == 2 && first != 0)
2956 printf ("\
2957It is highly recommended that the third partition covers the whole disk\n\
2958and is of type `Whole disk'\n");
2959 /* ewt asks to add: "don't start a partition at cyl 0"
2960 However, edmundo@rano.demon.co.uk writes:
2961 "In addition to having a Sun partition table, to be able to
2962 boot from the disc, the first partition, /dev/sdX1, must
2963 start at cylinder 0. This means that /dev/sdX1 contains
2964 the partition table and the boot block, as these are the
2965 first two sectors of the disc. Therefore you must be
2966 careful what you use /dev/sdX1 for. In particular, you must
2967 not use a partition starting at cylinder 0 for Linux swap,
2968 as that would overwrite the partition table and the boot
2969 block. You may, however, use such a partition for a UFS
2970 or EXT2 file system, as these file systems leave the first
2971 1024 bytes undisturbed. */
2972 /* On the other hand, one should not use partitions
2973 starting at block 0 in an md, or the label will
2974 be trashed. */
2975 for (i = 0; i < partitions; i++)
2976 if (lens[i] && starts[i] <= first
2977 && starts[i] + lens[i] > first)
2978 break;
2979 if (i < partitions && !whole_disk) {
2980 if (n == 2 && !first) {
2981 whole_disk = 1;
2982 break;
2983 }
2984 printf(_("Sector %d is already allocated\n"), first);
2985 } else
2986 break;
2987 }
2988 stop = cylinders * heads * sectors;
2989 stop2 = stop;
2990 for (i = 0; i < partitions; i++) {
2991 if (starts[i] > first && starts[i] < stop)
2992 stop = starts[i];
2993 }
2994 snprintf(mesg, sizeof(mesg),
2995 _("Last %s or +size or +sizeM or +sizeK"),
2996 str_units(SINGULAR));
2997 if (whole_disk)
2998 last = read_int(scround(stop2), scround(stop2), scround(stop2),
2999 0, mesg);
3000 else if (n == 2 && !first)
3001 last = read_int(scround(first), scround(stop2), scround(stop2),
3002 scround(first), mesg);
3003 else
3004 last = read_int(scround(first), scround(stop), scround(stop),
3005 scround(first), mesg);
3006 if (display_in_cyl_units)
3007 last *= units_per_sector;
3008 if (n == 2 && !first) {
3009 if (last >= stop2) {
3010 whole_disk = 1;
3011 last = stop2;
3012 } else if (last > stop) {
3013 printf (
3014 _("You haven't covered the whole disk with the 3rd partition, but your value\n"
3015 "%d %s covers some other partition. Your entry has been changed\n"
3016 "to %d %s\n"),
3017 scround(last), str_units(SINGULAR),
3018 scround(stop), str_units(SINGULAR));
3019 last = stop;
3020 }
3021 } else if (!whole_disk && last > stop)
3022 last = stop;
3023
3024 if (whole_disk) sys = WHOLE_DISK;
3025 set_sun_partition(n, first, last, sys);
3026}
3027
3028static void
3029sun_delete_partition(int i) {
3030 unsigned int nsec;
3031
3032 if (i == 2 && sunlabel->infos[i].id == WHOLE_DISK &&
3033 !sunlabel->partitions[i].start_cylinder &&
3034 (nsec = SUN_SSWAP32(sunlabel->partitions[i].num_sectors))
3035 == heads * sectors * cylinders)
3036 printf(_("If you want to maintain SunOS/Solaris compatibility, "
3037 "consider leaving this\n"
3038 "partition as Whole disk (5), starting at 0, with %u "
3039 "sectors\n"), nsec);
3040 sunlabel->infos[i].id = 0;
3041 sunlabel->partitions[i].num_sectors = 0;
3042}
3043
3044static void
3045sun_change_sysid(int i, int sys) {
3046 if (sys == LINUX_SWAP && !sunlabel->partitions[i].start_cylinder) {
3047 read_chars(
3048 _("It is highly recommended that the partition at offset 0\n"
3049 "is UFS, EXT2FS filesystem or SunOS swap. Putting Linux swap\n"
3050 "there may destroy your partition table and bootblock.\n"
3051 "Type YES if you're very sure you would like that partition\n"
3052 "tagged with 82 (Linux swap): "));
3053 if (strcmp (line_ptr, _("YES\n")))
3054 return;
3055 }
3056 switch (sys) {
3057 case SUNOS_SWAP:
3058 case LINUX_SWAP:
3059 /* swaps are not mountable by default */
3060 sunlabel->infos[i].flags |= 0x01;
3061 break;
3062 default:
3063 /* assume other types are mountable;
3064 user can change it anyway */
3065 sunlabel->infos[i].flags &= ~0x01;
3066 break;
3067 }
3068 sunlabel->infos[i].id = sys;
3069}
3070
3071static void
3072sun_list_table(int xtra) {
3073 int i, w;
3074
3075 w = strlen(disk_device);
3076 if (xtra)
3077 printf(
3078 _("\nDisk %s (Sun disk label): %d heads, %d sectors, %d rpm\n"
3079 "%d cylinders, %d alternate cylinders, %d physical cylinders\n"
3080 "%d extra sects/cyl, interleave %d:1\n"
3081 "%s\n"
3082 "Units = %s of %d * 512 bytes\n\n"),
3083 disk_device, heads, sectors, SUN_SSWAP16(sunlabel->rspeed),
3084 cylinders, SUN_SSWAP16(sunlabel->nacyl),
3085 SUN_SSWAP16(sunlabel->pcylcount),
3086 SUN_SSWAP16(sunlabel->sparecyl),
3087 SUN_SSWAP16(sunlabel->ilfact),
3088 (char *)sunlabel,
3089 str_units(PLURAL), units_per_sector);
3090 else
3091 printf(
3092 _("\nDisk %s (Sun disk label): %d heads, %d sectors, %d cylinders\n"
3093 "Units = %s of %d * 512 bytes\n\n"),
3094 disk_device, heads, sectors, cylinders,
3095 str_units(PLURAL), units_per_sector);
3096
3097 printf(_("%*s Flag Start End Blocks Id System\n"),
3098 w + 1, _("Device"));
3099 for (i = 0 ; i < partitions; i++) {
3100 if (sunlabel->partitions[i].num_sectors) {
3101 uint32_t start = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * heads * sectors;
3102 uint32_t len = SUN_SSWAP32(sunlabel->partitions[i].num_sectors);
3103 printf(
3104 "%s %c%c %9ld %9ld %9ld%c %2x %s\n",
3105/* device */ partname(disk_device, i+1, w),
3106/* flags */ (sunlabel->infos[i].flags & 0x01) ? 'u' : ' ',
3107 (sunlabel->infos[i].flags & 0x10) ? 'r' : ' ',
3108/* start */ (long) scround(start),
3109/* end */ (long) scround(start+len),
3110/* odd flag on end */ (long) len / 2, len & 1 ? '+' : ' ',
3111/* type id */ sunlabel->infos[i].id,
3112/* type name */ partition_type(sunlabel->infos[i].id));
3113 }
3114 }
3115}
3116
3117#ifdef CONFIG_FEATURE_FDISK_ADVANCED
3118
3119static void
3120sun_set_alt_cyl(void) {
3121 sunlabel->nacyl =
3122 SUN_SSWAP16(read_int(0,SUN_SSWAP16(sunlabel->nacyl), 65535, 0,
3123 _("Number of alternate cylinders")));
3124}
3125
3126static void
3127sun_set_ncyl(int cyl) {
3128 sunlabel->ncyl = SUN_SSWAP16(cyl);
3129}
3130
3131static void
3132sun_set_xcyl(void) {
3133 sunlabel->sparecyl =
3134 SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->sparecyl), sectors, 0,
3135 _("Extra sectors per cylinder")));
3136}
3137
3138static void
3139sun_set_ilfact(void) {
3140 sunlabel->ilfact =
3141 SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->ilfact), 32, 0,
3142 _("Interleave factor")));
3143}
3144
3145static void
3146sun_set_rspeed(void) {
3147 sunlabel->rspeed =
3148 SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->rspeed), 100000, 0,
3149 _("Rotation speed (rpm)")));
3150}
3151
3152static void
3153sun_set_pcylcount(void) {
3154 sunlabel->pcylcount =
3155 SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->pcylcount), 65535, 0,
3156 _("Number of physical cylinders")));
3157}
3158#endif /* CONFIG_FEATURE_FDISK_ADVANCED */
3159
3160static void
3161sun_write_table(void) {
3162 unsigned short *ush = (unsigned short *)sunlabel;
3163 unsigned short csum = 0;
3164
3165 while(ush < (unsigned short *)(&sunlabel->csum))
3166 csum ^= *ush++;
3167 sunlabel->csum = csum;
3168 if (lseek(fd, 0, SEEK_SET) < 0)
3169 fdisk_fatal(unable_to_seek);
3170 if (write(fd, sunlabel, SECTOR_SIZE) != SECTOR_SIZE)
3171 fdisk_fatal(unable_to_write);
3172}
3173#endif /* SUN_LABEL */
3174
3175/* DOS partition types */
3176
3177static const struct systypes i386_sys_types[] = {
3178 {"\x00" "Empty"},
3179 {"\x01" "FAT12"},
3180 {"\x04" "FAT16 <32M"},
3181 {"\x05" "Extended"}, /* DOS 3.3+ extended partition */
3182 {"\x06" "FAT16"}, /* DOS 16-bit >=32M */
3183 {"\x07" "HPFS/NTFS"}, /* OS/2 IFS, eg, HPFS or NTFS or QNX */
3184 {"\x0a" "OS/2 Boot Manager"},/* OS/2 Boot Manager */
3185 {"\x0b" "Win95 FAT32"},
3186 {"\x0c" "Win95 FAT32 (LBA)"},/* LBA really is `Extended Int 13h' */
3187 {"\x0e" "Win95 FAT16 (LBA)"},
3188 {"\x0f" "Win95 Ext'd (LBA)"},
3189 {"\x11" "Hidden FAT12"},
3190 {"\x12" "Compaq diagnostics"},
3191 {"\x14" "Hidden FAT16 <32M"},
3192 {"\x16" "Hidden FAT16"},
3193 {"\x17" "Hidden HPFS/NTFS"},
3194 {"\x1b" "Hidden Win95 FAT32"},
3195 {"\x1c" "Hidden Win95 FAT32 (LBA)"},
3196 {"\x1e" "Hidden Win95 FAT16 (LBA)"},
3197 {"\x3c" "PartitionMagic recovery"},
3198 {"\x41" "PPC PReP Boot"},
3199 {"\x42" "SFS"},
3200 {"\x63" "GNU HURD or SysV"}, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
3201 {"\x80" "Old Minix"}, /* Minix 1.4a and earlier */
3202 {"\x81" "Minix / old Linux"},/* Minix 1.4b and later */
3203 {"\x82" "Linux swap"}, /* also Solaris */
3204 {"\x83" "Linux"},
3205 {"\x84" "OS/2 hidden C: drive"},
3206 {"\x85" "Linux extended"},
3207 {"\x86" "NTFS volume set"},
3208 {"\x87" "NTFS volume set"},
3209 {"\x8e" "Linux LVM"},
3210 {"\x9f" "BSD/OS"}, /* BSDI */
3211 {"\xa0" "IBM Thinkpad hibernation"},
3212 {"\xa5" "FreeBSD"}, /* various BSD flavours */
3213 {"\xa6" "OpenBSD"},
3214 {"\xa8" "Darwin UFS"},
3215 {"\xa9" "NetBSD"},
3216 {"\xab" "Darwin boot"},
3217 {"\xb7" "BSDI fs"},
3218 {"\xb8" "BSDI swap"},
3219 {"\xbe" "Solaris boot"},
3220 {"\xeb" "BeOS fs"},
3221 {"\xee" "EFI GPT"}, /* Intel EFI GUID Partition Table */
3222 {"\xef" "EFI (FAT-12/16/32)"},/* Intel EFI System Partition */
3223 {"\xf0" "Linux/PA-RISC boot"},/* Linux/PA-RISC boot loader */
3224 {"\xf2" "DOS secondary"}, /* DOS 3.3+ secondary */
3225 {"\xfd" "Linux raid autodetect"},/* New (2.2.x) raid partition with
3226 autodetect using persistent
3227 superblock */
3228#ifdef CONFIG_WEIRD_PARTITION_TYPES
3229 {"\x02" "XENIX root"},
3230 {"\x03" "XENIX usr"},
3231 {"\x08" "AIX"}, /* AIX boot (AIX -- PS/2 port) or SplitDrive */
3232 {"\x09" "AIX bootable"}, /* AIX data or Coherent */
3233 {"\x10" "OPUS"},
3234 {"\x18" "AST SmartSleep"},
3235 {"\x24" "NEC DOS"},
3236 {"\x39" "Plan 9"},
3237 {"\x40" "Venix 80286"},
3238 {"\x4d" "QNX4.x"},
3239 {"\x4e" "QNX4.x 2nd part"},
3240 {"\x4f" "QNX4.x 3rd part"},
3241 {"\x50" "OnTrack DM"},
3242 {"\x51" "OnTrack DM6 Aux1"}, /* (or Novell) */
3243 {"\x52" "CP/M"}, /* CP/M or Microport SysV/AT */
3244 {"\x53" "OnTrack DM6 Aux3"},
3245 {"\x54" "OnTrackDM6"},
3246 {"\x55" "EZ-Drive"},
3247 {"\x56" "Golden Bow"},
3248 {"\x5c" "Priam Edisk"},
3249 {"\x61" "SpeedStor"},
3250 {"\x64" "Novell Netware 286"},
3251 {"\x65" "Novell Netware 386"},
3252 {"\x70" "DiskSecure Multi-Boot"},
3253 {"\x75" "PC/IX"},
3254 {"\x93" "Amoeba"},
3255 {"\x94" "Amoeba BBT"}, /* (bad block table) */
3256 {"\xa7" "NeXTSTEP"},
3257 {"\xbb" "Boot Wizard hidden"},
3258 {"\xc1" "DRDOS/sec (FAT-12)"},
3259 {"\xc4" "DRDOS/sec (FAT-16 < 32M)"},
3260 {"\xc6" "DRDOS/sec (FAT-16)"},
3261 {"\xc7" "Syrinx"},
3262 {"\xda" "Non-FS data"},
3263 {"\xdb" "CP/M / CTOS / ..."},/* CP/M or Concurrent CP/M or
3264 Concurrent DOS or CTOS */
3265 {"\xde" "Dell Utility"}, /* Dell PowerEdge Server utilities */
3266 {"\xdf" "BootIt"}, /* BootIt EMBRM */
3267 {"\xe1" "DOS access"}, /* DOS access or SpeedStor 12-bit FAT
3268 extended partition */
3269 {"\xe3" "DOS R/O"}, /* DOS R/O or SpeedStor */
3270 {"\xe4" "SpeedStor"}, /* SpeedStor 16-bit FAT extended
3271 partition < 1024 cyl. */
3272 {"\xf1" "SpeedStor"},
3273 {"\xf4" "SpeedStor"}, /* SpeedStor large partition */
3274 {"\xfe" "LANstep"}, /* SpeedStor >1024 cyl. or LANstep */
3275 {"\xff" "BBT"}, /* Xenix Bad Block Table */
3276#endif
3277 { 0 }
3278};
3279
3280
3281
3282/* A valid partition table sector ends in 0x55 0xaa */
3283static unsigned int
3284part_table_flag(const char *b) {
3285 return ((uint) b[510]) + (((uint) b[511]) << 8);
3286}
3287
3288
3289#ifdef CONFIG_FEATURE_FDISK_WRITABLE
3290static void
3291write_part_table_flag(char *b) {
3292 b[510] = 0x55;
3293 b[511] = 0xaa;
3294}
3295
3296/* start_sect and nr_sects are stored little endian on all machines */
3297/* moreover, they are not aligned correctly */
3298static void
3299store4_little_endian(unsigned char *cp, unsigned int val) {
3300 cp[0] = (val & 0xff);
3301 cp[1] = ((val >> 8) & 0xff);
3302 cp[2] = ((val >> 16) & 0xff);
3303 cp[3] = ((val >> 24) & 0xff);
3304}
3305#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
3306
3307static unsigned int
3308read4_little_endian(const unsigned char *cp) {
3309 return (uint)(cp[0]) + ((uint)(cp[1]) << 8)
3310 + ((uint)(cp[2]) << 16) + ((uint)(cp[3]) << 24);
3311}
3312
3313#ifdef CONFIG_FEATURE_FDISK_WRITABLE
3314static void
3315set_start_sect(struct partition *p, unsigned int start_sect) {
3316 store4_little_endian(p->start4, start_sect);
3317}
3318#endif
3319
3320static int32_t
3321get_start_sect(const struct partition *p) {
3322 return read4_little_endian(p->start4);
3323}
3324
3325#ifdef CONFIG_FEATURE_FDISK_WRITABLE
3326static void
3327set_nr_sects(struct partition *p, int32_t nr_sects) {
3328 store4_little_endian(p->size4, nr_sects);
3329}
3330#endif
3331
3332static int32_t
3333get_nr_sects(const struct partition *p) {
3334 return read4_little_endian(p->size4);
3335}
3336
3337/* normally O_RDWR, -l option gives O_RDONLY */
3338static int type_open = O_RDWR;
3339
3340
3341static int ext_index, /* the prime extended partition */
3342 listing, /* no aborts for fdisk -l */
3343 dos_compatible_flag = ~0;
3344#ifdef CONFIG_FEATURE_FDISK_WRITABLE
3345static int dos_changed;
3346static int nowarn; /* no warnings for fdisk -l/-s */
3347#endif
3348
3349
3350
3351static uint user_cylinders, user_heads, user_sectors;
3352static uint pt_heads, pt_sectors;
3353static uint kern_heads, kern_sectors;
3354
3355static off_t extended_offset; /* offset of link pointers */
3356
3357static unsigned long long total_number_of_sectors;
3358
3359
3360static jmp_buf listingbuf;
3361
3362static void fdisk_fatal(enum failure why) {
3363 const char *message;
3364
3365 if (listing) {
3366 close(fd);
3367 longjmp(listingbuf, 1);
3368 }
3369
3370 switch (why) {
3371 case unable_to_open:
3372 message = "Unable to open %s\n";
3373 break;
3374 case unable_to_read:
3375 message = "Unable to read %s\n";
3376 break;
3377 case unable_to_seek:
3378 message = "Unable to seek on %s\n";
3379 break;
3380 case unable_to_write:
3381 message = "Unable to write %s\n";
3382 break;
3383 case ioctl_error:
3384 message = "BLKGETSIZE ioctl failed on %s\n";
3385 break;
3386 default:
3387 message = "Fatal error\n";
3388 }
3389
3390 fputc('\n', stderr);
3391 fprintf(stderr, message, disk_device);
3392 exit(1);
3393}
3394
3395static void
3396seek_sector(off_t secno) {
3397 off_t offset = secno * sector_size;
3398 if (lseek(fd, offset, SEEK_SET) == (off_t) -1)
3399 fdisk_fatal(unable_to_seek);
3400}
3401
3402#ifdef CONFIG_FEATURE_FDISK_WRITABLE
3403static void
3404write_sector(off_t secno, char *buf) {
3405 seek_sector(secno);
3406 if (write(fd, buf, sector_size) != sector_size)
3407 fdisk_fatal(unable_to_write);
3408}
3409#endif
3410
3411/* Allocate a buffer and read a partition table sector */
3412static void
3413read_pte(struct pte *pe, off_t offset) {
3414
3415 pe->offset = offset;
3416 pe->sectorbuffer = (char *) xmalloc(sector_size);
3417 seek_sector(offset);
3418 if (read(fd, pe->sectorbuffer, sector_size) != sector_size)
3419 fdisk_fatal(unable_to_read);
3420#ifdef CONFIG_FEATURE_FDISK_WRITABLE
3421 pe->changed = 0;
3422#endif
3423 pe->part_table = pe->ext_pointer = NULL;
3424}
3425
3426static unsigned int
3427get_partition_start(const struct pte *pe) {
3428 return pe->offset + get_start_sect(pe->part_table);
3429}
3430
3431#ifdef CONFIG_FEATURE_FDISK_WRITABLE
3432/*
3433 * Avoid warning about DOS partitions when no DOS partition was changed.
3434 * Here a heuristic "is probably dos partition".
3435 * We might also do the opposite and warn in all cases except
3436 * for "is probably nondos partition".
3437 */
3438static int
3439is_dos_partition(int t) {
3440 return (t == 1 || t == 4 || t == 6 ||
3441 t == 0x0b || t == 0x0c || t == 0x0e ||
3442 t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 ||
3443 t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 ||
3444 t == 0xc1 || t == 0xc4 || t == 0xc6);
3445}
3446
3447static void
3448menu(void) {
3449#ifdef CONFIG_FEATURE_SUN_LABEL
3450 if (sun_label) {
3451 puts(_("Command action"));
3452 puts(_("\ta\ttoggle a read only flag")); /* sun */
3453 puts(_("\tb\tedit bsd disklabel"));
3454 puts(_("\tc\ttoggle the mountable flag")); /* sun */
3455 puts(_("\td\tdelete a partition"));
3456 puts(_("\tl\tlist known partition types"));
3457 puts(_("\tm\tprint this menu"));
3458 puts(_("\tn\tadd a new partition"));
3459 puts(_("\to\tcreate a new empty DOS partition table"));
3460 puts(_("\tp\tprint the partition table"));
3461 puts(_("\tq\tquit without saving changes"));
3462 puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */
3463 puts(_("\tt\tchange a partition's system id"));
3464 puts(_("\tu\tchange display/entry units"));
3465 puts(_("\tv\tverify the partition table"));
3466 puts(_("\tw\twrite table to disk and exit"));
3467#ifdef CONFIG_FEATURE_FDISK_ADVANCED
3468 puts(_("\tx\textra functionality (experts only)"));
3469#endif
3470 } else
3471#endif
3472#ifdef CONFIG_FEATURE_SGI_LABEL
3473 if (sgi_label) {
3474 puts(_("Command action"));
3475 puts(_("\ta\tselect bootable partition")); /* sgi flavour */
3476 puts(_("\tb\tedit bootfile entry")); /* sgi */
3477 puts(_("\tc\tselect sgi swap partition")); /* sgi flavour */
3478 puts(_("\td\tdelete a partition"));
3479 puts(_("\tl\tlist known partition types"));
3480 puts(_("\tm\tprint this menu"));
3481 puts(_("\tn\tadd a new partition"));
3482 puts(_("\to\tcreate a new empty DOS partition table"));
3483 puts(_("\tp\tprint the partition table"));
3484 puts(_("\tq\tquit without saving changes"));
3485 puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */
3486 puts(_("\tt\tchange a partition's system id"));
3487 puts(_("\tu\tchange display/entry units"));
3488 puts(_("\tv\tverify the partition table"));
3489 puts(_("\tw\twrite table to disk and exit"));
3490 } else
3491#endif
3492#ifdef CONFIG_FEATURE_AIX_LABEL
3493 if (aix_label) {
3494 puts(_("Command action"));
3495 puts(_("\tm\tprint this menu"));
3496 puts(_("\to\tcreate a new empty DOS partition table"));
3497 puts(_("\tq\tquit without saving changes"));
3498 puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */
3499 } else
3500#endif
3501 {
3502 puts(_("Command action"));
3503 puts(_("\ta\ttoggle a bootable flag"));
3504 puts(_("\tb\tedit bsd disklabel"));
3505 puts(_("\tc\ttoggle the dos compatibility flag"));
3506 puts(_("\td\tdelete a partition"));
3507 puts(_("\tl\tlist known partition types"));
3508 puts(_("\tm\tprint this menu"));
3509 puts(_("\tn\tadd a new partition"));
3510 puts(_("\to\tcreate a new empty DOS partition table"));
3511 puts(_("\tp\tprint the partition table"));
3512 puts(_("\tq\tquit without saving changes"));
3513 puts(_("\ts\tcreate a new empty Sun disklabel")); /* sun */
3514 puts(_("\tt\tchange a partition's system id"));
3515 puts(_("\tu\tchange display/entry units"));
3516 puts(_("\tv\tverify the partition table"));
3517 puts(_("\tw\twrite table to disk and exit"));
3518#ifdef CONFIG_FEATURE_FDISK_ADVANCED
3519 puts(_("\tx\textra functionality (experts only)"));
3520#endif
3521 }
3522}
3523#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
3524
3525
3526#ifdef CONFIG_FEATURE_FDISK_ADVANCED
3527static void
3528xmenu(void) {
3529#ifdef CONFIG_FEATURE_SUN_LABEL
3530 if (sun_label) {
3531 puts(_("Command action"));
3532 puts(_("\ta\tchange number of alternate cylinders")); /*sun*/
3533 puts(_("\tc\tchange number of cylinders"));
3534 puts(_("\td\tprint the raw data in the partition table"));
3535 puts(_("\te\tchange number of extra sectors per cylinder"));/*sun*/
3536 puts(_("\th\tchange number of heads"));
3537 puts(_("\ti\tchange interleave factor")); /*sun*/
3538 puts(_("\to\tchange rotation speed (rpm)")); /*sun*/
3539 puts(_("\tm\tprint this menu"));
3540 puts(_("\tp\tprint the partition table"));
3541 puts(_("\tq\tquit without saving changes"));
3542 puts(_("\tr\treturn to main menu"));
3543 puts(_("\ts\tchange number of sectors/track"));
3544 puts(_("\tv\tverify the partition table"));
3545 puts(_("\tw\twrite table to disk and exit"));
3546 puts(_("\ty\tchange number of physical cylinders")); /*sun*/
3547 } else
3548#endif
3549#ifdef CONFIG_FEATURE_SGI_LABEL
3550 if (sgi_label) {
3551 puts(_("Command action"));
3552 puts(_("\tb\tmove beginning of data in a partition")); /* !sun */
3553 puts(_("\tc\tchange number of cylinders"));
3554 puts(_("\td\tprint the raw data in the partition table"));
3555 puts(_("\te\tlist extended partitions")); /* !sun */
3556 puts(_("\tg\tcreate an IRIX (SGI) partition table"));/* sgi */
3557 puts(_("\th\tchange number of heads"));
3558 puts(_("\tm\tprint this menu"));
3559 puts(_("\tp\tprint the partition table"));
3560 puts(_("\tq\tquit without saving changes"));
3561 puts(_("\tr\treturn to main menu"));
3562 puts(_("\ts\tchange number of sectors/track"));
3563 puts(_("\tv\tverify the partition table"));
3564 puts(_("\tw\twrite table to disk and exit"));
3565 } else
3566#endif
3567#ifdef CONFIG_FEATURE_AIX_LABEL
3568 if (aix_label) {
3569 puts(_("Command action"));
3570 puts(_("\tb\tmove beginning of data in a partition")); /* !sun */
3571 puts(_("\tc\tchange number of cylinders"));
3572 puts(_("\td\tprint the raw data in the partition table"));
3573 puts(_("\te\tlist extended partitions")); /* !sun */
3574 puts(_("\tg\tcreate an IRIX (SGI) partition table"));/* sgi */
3575 puts(_("\th\tchange number of heads"));
3576 puts(_("\tm\tprint this menu"));
3577 puts(_("\tp\tprint the partition table"));
3578 puts(_("\tq\tquit without saving changes"));
3579 puts(_("\tr\treturn to main menu"));
3580 puts(_("\ts\tchange number of sectors/track"));
3581 puts(_("\tv\tverify the partition table"));
3582 puts(_("\tw\twrite table to disk and exit"));
3583 } else
3584#endif
3585 {
3586 puts(_("Command action"));
3587 puts(_("\tb\tmove beginning of data in a partition")); /* !sun */
3588 puts(_("\tc\tchange number of cylinders"));
3589 puts(_("\td\tprint the raw data in the partition table"));
3590 puts(_("\te\tlist extended partitions")); /* !sun */
3591 puts(_("\tf\tfix partition order")); /* !sun, !aix, !sgi */
3592#ifdef CONFIG_FEATURE_SGI_LABEL
3593 puts(_("\tg\tcreate an IRIX (SGI) partition table"));/* sgi */
3594#endif
3595 puts(_("\th\tchange number of heads"));
3596 puts(_("\tm\tprint this menu"));
3597 puts(_("\tp\tprint the partition table"));
3598 puts(_("\tq\tquit without saving changes"));
3599 puts(_("\tr\treturn to main menu"));
3600 puts(_("\ts\tchange number of sectors/track"));
3601 puts(_("\tv\tverify the partition table"));
3602 puts(_("\tw\twrite table to disk and exit"));
3603 }
3604}
3605#endif /* ADVANCED mode */
3606
3607#ifdef CONFIG_FEATURE_FDISK_WRITABLE
3608static const struct systypes *
3609get_sys_types(void) {
3610 return (
3611#ifdef CONFIG_FEATURE_SUN_LABEL
3612 sun_label ? sun_sys_types :
3613#endif
3614#ifdef CONFIG_FEATURE_SGI_LABEL
3615 sgi_label ? sgi_sys_types :
3616#endif
3617 i386_sys_types);
3618}
3619#else
3620#define get_sys_types() i386_sys_types
3621#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
3622
3623static const char *partition_type(unsigned char type)
3624{
3625 int i;
3626 const struct systypes *types = get_sys_types();
3627
3628 for (i=0; types[i].name; i++)
3629 if (types[i].name[0] == type)
3630 return types[i].name + 1;
3631
3632 return _("Unknown");
3633}
3634
3635
3636#ifdef CONFIG_FEATURE_FDISK_WRITABLE
3637static int
3638get_sysid(int i) {
3639 return (
3640#ifdef CONFIG_FEATURE_SUN_LABEL
3641 sun_label ? sunlabel->infos[i].id :
3642#endif
3643#ifdef CONFIG_FEATURE_SGI_LABEL
3644 sgi_label ? sgi_get_sysid(i) :
3645#endif
3646 ptes[i].part_table->sys_ind);
3647}
3648
3649void list_types(const struct systypes *sys)
3650{
3651 uint last[4], done = 0, next = 0, size;
3652 int i;
3653
3654 for (i = 0; sys[i].name; i++);
3655 size = i;
3656
3657 for (i = 3; i >= 0; i--)
3658 last[3 - i] = done += (size + i - done) / (i + 1);
3659 i = done = 0;
3660
3661 do {
3662 printf("%c%2x %-15.15s", i ? ' ' : '\n',
3663 sys[next].name[0], partition_type(sys[next].name[0]));
3664 next = last[i++] + done;
3665 if (i > 3 || next >= last[i]) {
3666 i = 0;
3667 next = ++done;
3668 }
3669 } while (done < last[0]);
3670 putchar('\n');
3671}
3672#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
3673
3674static int
3675is_cleared_partition(const struct partition *p) {
3676 return !(!p || p->boot_ind || p->head || p->sector || p->cyl ||
3677 p->sys_ind || p->end_head || p->end_sector || p->end_cyl ||
3678 get_start_sect(p) || get_nr_sects(p));
3679}
3680
3681static void
3682clear_partition(struct partition *p) {
3683 if (!p)
3684 return;
3685 memset(p, 0, sizeof(struct partition));
3686}
3687
3688#ifdef CONFIG_FEATURE_FDISK_WRITABLE
3689static void
3690set_partition(int i, int doext, off_t start, off_t stop, int sysid) {
3691 struct partition *p;
3692 off_t offset;
3693
3694 if (doext) {
3695 p = ptes[i].ext_pointer;
3696 offset = extended_offset;
3697 } else {
3698 p = ptes[i].part_table;
3699 offset = ptes[i].offset;
3700 }
3701 p->boot_ind = 0;
3702 p->sys_ind = sysid;
3703 set_start_sect(p, start - offset);
3704 set_nr_sects(p, stop - start + 1);
3705 if (dos_compatible_flag && (start/(sectors*heads) > 1023))
3706 start = heads*sectors*1024 - 1;
3707 set_hsc(p->head, p->sector, p->cyl, start);
3708 if (dos_compatible_flag && (stop/(sectors*heads) > 1023))
3709 stop = heads*sectors*1024 - 1;
3710 set_hsc(p->end_head, p->end_sector, p->end_cyl, stop);
3711 ptes[i].changed = 1;
3712}
3713#endif
3714
3715static int
3716test_c(const char **m, const char *mesg) {
3717 int val = 0;
3718 if (!*m)
3719 fprintf(stderr, _("You must set"));
3720 else {
3721 fprintf(stderr, " %s", *m);
3722 val = 1;
3723 }
3724 *m = mesg;
3725 return val;
3726}
3727
3728static int
3729warn_geometry(void) {
3730 const char *m = NULL;
3731 int prev = 0;
3732
3733 if (!heads)
3734 prev = test_c(&m, _("heads"));
3735 if (!sectors)
3736 prev = test_c(&m, _("sectors"));
3737 if (!cylinders)
3738 prev = test_c(&m, _("cylinders"));
3739 if (!m)
3740 return 0;
3741
3742 fprintf(stderr, "%s%s.\n"
3743#ifdef CONFIG_FEATURE_FDISK_WRITABLE
3744 "You can do this from the extra functions menu.\n"
3745#endif
3746 , prev ? _(" and ") : " ", m);
3747
3748 return 1;
3749}
3750
3751static void update_units(void)
3752{
3753 int cyl_units = heads * sectors;
3754
3755 if (display_in_cyl_units && cyl_units)
3756 units_per_sector = cyl_units;
3757 else
3758 units_per_sector = 1; /* in sectors */
3759}
3760
3761#ifdef CONFIG_FEATURE_FDISK_WRITABLE
3762static void
3763warn_cylinders(void) {
3764 if (dos_label && cylinders > 1024 && !nowarn)
3765 fprintf(stderr, _("\n"
3766"The number of cylinders for this disk is set to %d.\n"
3767"There is nothing wrong with that, but this is larger than 1024,\n"
3768"and could in certain setups cause problems with:\n"
3769"1) software that runs at boot time (e.g., old versions of LILO)\n"
3770"2) booting and partitioning software from other OSs\n"
3771" (e.g., DOS FDISK, OS/2 FDISK)\n"),
3772 cylinders);
3773}
3774#endif
3775
3776static void
3777read_extended(int ext) {
3778 int i;
3779 struct pte *pex;
3780 struct partition *p, *q;
3781
3782 ext_index = ext;
3783 pex = &ptes[ext];
3784 pex->ext_pointer = pex->part_table;
3785
3786 p = pex->part_table;
3787 if (!get_start_sect(p)) {
3788 fprintf(stderr,
3789 _("Bad offset in primary extended partition\n"));
3790 return;
3791 }
3792
3793 while (IS_EXTENDED (p->sys_ind)) {
3794 struct pte *pe = &ptes[partitions];
3795
3796 if (partitions >= MAXIMUM_PARTS) {
3797 /* This is not a Linux restriction, but
3798 this program uses arrays of size MAXIMUM_PARTS.
3799 Do not try to `improve' this test. */
3800 struct pte *pre = &ptes[partitions-1];
3801#ifdef CONFIG_FEATURE_FDISK_WRITABLE
3802 fprintf(stderr,
3803 _("Warning: deleting partitions after %d\n"),
3804 partitions);
3805 pre->changed = 1;
3806#endif
3807 clear_partition(pre->ext_pointer);
3808 return;
3809 }
3810
3811 read_pte(pe, extended_offset + get_start_sect(p));
3812
3813 if (!extended_offset)
3814 extended_offset = get_start_sect(p);
3815
3816 q = p = pt_offset(pe->sectorbuffer, 0);
3817 for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) {
3818 if (IS_EXTENDED (p->sys_ind)) {
3819 if (pe->ext_pointer)
3820 fprintf(stderr,
3821 _("Warning: extra link "
3822 "pointer in partition table"
3823 " %d\n"), partitions + 1);
3824 else
3825 pe->ext_pointer = p;
3826 } else if (p->sys_ind) {
3827 if (pe->part_table)
3828 fprintf(stderr,
3829 _("Warning: ignoring extra "
3830 "data in partition table"
3831 " %d\n"), partitions + 1);
3832 else
3833 pe->part_table = p;
3834 }
3835 }
3836
3837 /* very strange code here... */
3838 if (!pe->part_table) {
3839 if (q != pe->ext_pointer)
3840 pe->part_table = q;
3841 else
3842 pe->part_table = q + 1;
3843 }
3844 if (!pe->ext_pointer) {
3845 if (q != pe->part_table)
3846 pe->ext_pointer = q;
3847 else
3848 pe->ext_pointer = q + 1;
3849 }
3850
3851 p = pe->ext_pointer;
3852 partitions++;
3853 }
3854
3855#ifdef CONFIG_FEATURE_FDISK_WRITABLE
3856 /* remove empty links */
3857 remove:
3858 for (i = 4; i < partitions; i++) {
3859 struct pte *pe = &ptes[i];
3860
3861 if (!get_nr_sects(pe->part_table) &&
3862 (partitions > 5 || ptes[4].part_table->sys_ind)) {
3863 printf("omitting empty partition (%d)\n", i+1);
3864 delete_partition(i);
3865 goto remove; /* numbering changed */
3866 }
3867 }
3868#endif
3869}
3870
3871#ifdef CONFIG_FEATURE_FDISK_WRITABLE
3872static void
3873create_doslabel(void) {
3874 int i;
3875
3876 fprintf(stderr,
3877 _("Building a new DOS disklabel. Changes will remain in memory only,\n"
3878 "until you decide to write them. After that, of course, the previous\n"
3879 "content won't be recoverable.\n\n"));
3880#ifdef CONFIG_FEATURE_SUN_LABEL
3881 sun_nolabel(); /* otherwise always recognised as sun */
3882#endif
3883#ifdef CONFIG_FEATURE_SGI_LABEL
3884 sgi_nolabel(); /* otherwise always recognised as sgi */
3885#endif
3886#ifdef CONFIG_FEATURE_AIX_LABEL
3887 aix_label = 0;
3888#endif
3889#ifdef CONFIG_FEATURE_OSF_LABEL
3890 osf_label = 0;
3891 possibly_osf_label = 0;
3892#endif
3893 partitions = 4;
3894
3895 for (i = 510-64; i < 510; i++)
3896 MBRbuffer[i] = 0;
3897 write_part_table_flag(MBRbuffer);
3898 extended_offset = 0;
3899 set_all_unchanged();
3900 set_changed(0);
3901 get_boot(create_empty_dos);
3902}
3903#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
3904
3905static void
3906get_sectorsize(void) {
3907 if (!user_set_sector_size &&
3908 get_kernel_revision() >= MAKE_VERSION(2,3,3)) {
3909 int arg;
3910 if (ioctl(fd, BLKSSZGET, &arg) == 0)
3911 sector_size = arg;
3912 if (sector_size != DEFAULT_SECTOR_SIZE)
3913 printf(_("Note: sector size is %d (not %d)\n"),
3914 sector_size, DEFAULT_SECTOR_SIZE);
3915 }
3916}
3917
3918static inline void
3919get_kernel_geometry(void) {
3920 struct hd_geometry geometry;
3921
3922 if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
3923 kern_heads = geometry.heads;
3924 kern_sectors = geometry.sectors;
3925 /* never use geometry.cylinders - it is truncated */
3926 }
3927}
3928
3929static void
3930get_partition_table_geometry(void) {
3931 const unsigned char *bufp = MBRbuffer;
3932 struct partition *p;
3933 int i, h, s, hh, ss;
3934 int first = 1;
3935 int bad = 0;
3936
3937 if (!(valid_part_table_flag(bufp)))
3938 return;
3939
3940 hh = ss = 0;
3941 for (i=0; i<4; i++) {
3942 p = pt_offset(bufp, i);
3943 if (p->sys_ind != 0) {
3944 h = p->end_head + 1;
3945 s = (p->end_sector & 077);
3946 if (first) {
3947 hh = h;
3948 ss = s;
3949 first = 0;
3950 } else if (hh != h || ss != s)
3951 bad = 1;
3952 }
3953 }
3954
3955 if (!first && !bad) {
3956 pt_heads = hh;
3957 pt_sectors = ss;
3958 }
3959}
3960
3961void
3962get_geometry(void) {
3963 int sec_fac;
3964 unsigned long long bytes; /* really u64 */
3965
3966 get_sectorsize();
3967 sec_fac = sector_size / 512;
3968#ifdef CONFIG_FEATURE_SUN_LABEL
3969 guess_device_type();
3970#endif
3971 heads = cylinders = sectors = 0;
3972 kern_heads = kern_sectors = 0;
3973 pt_heads = pt_sectors = 0;
3974
3975 get_kernel_geometry();
3976 get_partition_table_geometry();
3977
3978 heads = user_heads ? user_heads :
3979 pt_heads ? pt_heads :
3980 kern_heads ? kern_heads : 255;
3981 sectors = user_sectors ? user_sectors :
3982 pt_sectors ? pt_sectors :
3983 kern_sectors ? kern_sectors : 63;
3984 if (ioctl(fd, BLKGETSIZE64, &bytes) == 0) {
3985 /* got bytes */
3986 } else {
3987 unsigned long longsectors;
3988
3989 if (ioctl(fd, BLKGETSIZE, &longsectors))
3990 longsectors = 0;
3991 bytes = ((unsigned long long) longsectors) << 9;
3992 }
3993
3994 total_number_of_sectors = (bytes >> 9);
3995
3996 sector_offset = 1;
3997 if (dos_compatible_flag)
3998 sector_offset = sectors;
3999
4000 cylinders = total_number_of_sectors / (heads * sectors * sec_fac);
4001 if (!cylinders)
4002 cylinders = user_cylinders;
4003}
4004
4005/*
4006 * Read MBR. Returns:
4007 * -1: no 0xaa55 flag present (possibly entire disk BSD)
4008 * 0: found or created label
4009 * 1: I/O error
4010 */
4011int
4012get_boot(enum action what) {
4013 int i;
4014
4015 partitions = 4;
4016
4017 for (i = 0; i < 4; i++) {
4018 struct pte *pe = &ptes[i];
4019
4020 pe->part_table = pt_offset(MBRbuffer, i);
4021 pe->ext_pointer = NULL;
4022 pe->offset = 0;
4023 pe->sectorbuffer = MBRbuffer;
4024#ifdef CONFIG_FEATURE_FDISK_WRITABLE
4025 pe->changed = (what == create_empty_dos);
4026#endif
4027 }
4028
4029#ifdef CONFIG_FEATURE_SUN_LABEL
4030 if (what == create_empty_sun && check_sun_label())
4031 return 0;
4032#endif
4033
4034 memset(MBRbuffer, 0, 512);
4035
4036#ifdef CONFIG_FEATURE_FDISK_WRITABLE
4037 if (what == create_empty_dos)
4038 goto got_dos_table; /* skip reading disk */
4039
4040 if ((fd = open(disk_device, type_open)) < 0) {
4041 if ((fd = open(disk_device, O_RDONLY)) < 0) {
4042 if (what == try_only)
4043 return 1;
4044 fdisk_fatal(unable_to_open);
4045 } else
4046 printf(_("You will not be able to write "
4047 "the partition table.\n"));
4048 }
4049
4050 if (512 != read(fd, MBRbuffer, 512)) {
4051 if (what == try_only)
4052 return 1;
4053 fdisk_fatal(unable_to_read);
4054 }
4055#else
4056 if ((fd = open(disk_device, O_RDONLY)) < 0)
4057 return 1;
4058 if (512 != read(fd, MBRbuffer, 512))
4059 return 1;
4060#endif
4061
4062 get_geometry();
4063
4064 update_units();
4065
4066#ifdef CONFIG_FEATURE_SUN_LABEL
4067 if (check_sun_label())
4068 return 0;
4069#endif
4070
4071#ifdef CONFIG_FEATURE_SGI_LABEL
4072 if (check_sgi_label())
4073 return 0;
4074#endif
4075
4076#ifdef CONFIG_FEATURE_AIX_LABEL
4077 if (check_aix_label())
4078 return 0;
4079#endif
4080
4081#ifdef CONFIG_FEATURE_OSF_LABEL
4082 if (check_osf_label()) {
4083 possibly_osf_label = 1;
4084 if (!valid_part_table_flag(MBRbuffer)) {
4085 osf_label = 1;
4086 return 0;
4087 }
4088 printf(_("This disk has both DOS and BSD magic.\n"
4089 "Give the 'b' command to go to BSD mode.\n"));
4090 }
4091#endif
4092
4093#ifdef CONFIG_FEATURE_FDISK_WRITABLE
4094got_dos_table:
4095#endif
4096
4097 if (!valid_part_table_flag(MBRbuffer)) {
4098#ifndef CONFIG_FEATURE_FDISK_WRITABLE
4099 return -1;
4100#else
4101 switch(what) {
4102 case fdisk:
4103 fprintf(stderr,
4104 _("Device contains neither a valid DOS "
4105 "partition table, nor Sun, SGI or OSF "
4106 "disklabel\n"));
4107#ifdef __sparc__
4108#ifdef CONFIG_FEATURE_SUN_LABEL
4109 create_sunlabel();
4110#endif
4111#else
4112 create_doslabel();
4113#endif
4114 return 0;
4115 case try_only:
4116 return -1;
4117 case create_empty_dos:
4118#ifdef CONFIG_FEATURE_SUN_LABEL
4119 case create_empty_sun:
4120#endif
4121 break;
4122 default:
4123 fprintf(stderr, _("Internal error\n"));
4124 exit(1);
4125 }
4126#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
4127 }
4128
4129#ifdef CONFIG_FEATURE_FDISK_WRITABLE
4130 warn_cylinders();
4131#endif
4132 warn_geometry();
4133
4134 for (i = 0; i < 4; i++) {
4135 struct pte *pe = &ptes[i];
4136
4137 if (IS_EXTENDED (pe->part_table->sys_ind)) {
4138 if (partitions != 4)
4139 fprintf(stderr, _("Ignoring extra extended "
4140 "partition %d\n"), i + 1);
4141 else
4142 read_extended(i);
4143 }
4144 }
4145
4146 for (i = 3; i < partitions; i++) {
4147 struct pte *pe = &ptes[i];
4148
4149 if (!valid_part_table_flag(pe->sectorbuffer)) {
4150 fprintf(stderr,
4151 _("Warning: invalid flag 0x%04x of partition "
4152 "table %d will be corrected by w(rite)\n"),
4153 part_table_flag(pe->sectorbuffer), i + 1);
4154#ifdef CONFIG_FEATURE_FDISK_WRITABLE
4155 pe->changed = 1;
4156#endif
4157 }
4158 }
4159
4160 return 0;
4161}
4162
4163#ifdef CONFIG_FEATURE_FDISK_WRITABLE
4164/*
4165 * Print the message MESG, then read an integer between LOW and HIGH (inclusive).
4166 * If the user hits Enter, DFLT is returned.
4167 * Answers like +10 are interpreted as offsets from BASE.
4168 *
4169 * There is no default if DFLT is not between LOW and HIGH.
4170 */
4171static uint
4172read_int(uint low, uint dflt, uint high, uint base, char *mesg)
4173{
4174 uint i;
4175 int default_ok = 1;
4176 static char *ms = NULL;
4177 static int mslen = 0;
4178
4179 if (!ms || strlen(mesg)+100 > mslen) {
4180 mslen = strlen(mesg)+200;
4181 ms = xrealloc(ms,mslen);
4182 }
4183
4184 if (dflt < low || dflt > high)
4185 default_ok = 0;
4186
4187 if (default_ok)
4188 snprintf(ms, mslen, _("%s (%u-%u, default %u): "),
4189 mesg, low, high, dflt);
4190 else
4191 snprintf(ms, mslen, "%s (%u-%u): ",
4192 mesg, low, high);
4193
4194 while (1) {
4195 int use_default = default_ok;
4196
4197 /* ask question and read answer */
4198 while (read_chars(ms) != '\n' && !isdigit(*line_ptr)
4199 && *line_ptr != '-' && *line_ptr != '+')
4200 continue;
4201
4202 if (*line_ptr == '+' || *line_ptr == '-') {
4203 int minus = (*line_ptr == '-');
4204 int absolute = 0;
4205
4206 i = atoi(line_ptr+1);
4207
4208 while (isdigit(*++line_ptr))
4209 use_default = 0;
4210
4211 switch (*line_ptr) {
4212 case 'c':
4213 case 'C':
4214 if (!display_in_cyl_units)
4215 i *= heads * sectors;
4216 break;
4217 case 'K':
4218 absolute = 1024;
4219 break;
4220 case 'k':
4221 absolute = 1000;
4222 break;
4223 case 'm':
4224 case 'M':
4225 absolute = 1000000;
4226 break;
4227 case 'g':
4228 case 'G':
4229 absolute = 1000000000;
4230 break;
4231 default:
4232 break;
4233 }
4234 if (absolute) {
4235 unsigned long long bytes;
4236 unsigned long unit;
4237
4238 bytes = (unsigned long long) i * absolute;
4239 unit = sector_size * units_per_sector;
4240 bytes += unit/2; /* round */
4241 bytes /= unit;
4242 i = bytes;
4243 }
4244 if (minus)
4245 i = -i;
4246 i += base;
4247 } else {
4248 i = atoi(line_ptr);
4249 while (isdigit(*line_ptr)) {
4250 line_ptr++;
4251 use_default = 0;
4252 }
4253 }
4254 if (use_default)
4255 printf(_("Using default value %u\n"), i = dflt);
4256 if (i >= low && i <= high)
4257 break;
4258 else
4259 printf(_("Value out of range.\n"));
4260 }
4261 return i;
4262}
4263
4264int
4265get_partition(int warn, int max) {
4266 struct pte *pe;
4267 int i;
4268
4269 i = read_int(1, 0, max, 0, _("Partition number")) - 1;
4270 pe = &ptes[i];
4271
4272 if (warn) {
4273 if ((!sun_label && !sgi_label && !pe->part_table->sys_ind)
4274#ifdef CONFIG_FEATURE_SUN_LABEL
4275 || (sun_label &&
4276 (!sunlabel->partitions[i].num_sectors ||
4277 !sunlabel->infos[i].id))
4278#endif
4279#ifdef CONFIG_FEATURE_SGI_LABEL
4280 || (sgi_label && (!sgi_get_num_sectors(i)))
4281#endif
4282 )
4283 fprintf(stderr,
4284 _("Warning: partition %d has empty type\n"),
4285 i+1);
4286 }
4287 return i;
4288}
4289
4290static int
4291get_existing_partition(int warn, int max) {
4292 int pno = -1;
4293 int i;
4294
4295 for (i = 0; i < max; i++) {
4296 struct pte *pe = &ptes[i];
4297 struct partition *p = pe->part_table;
4298
4299 if (p && !is_cleared_partition(p)) {
4300 if (pno >= 0)
4301 goto not_unique;
4302 pno = i;
4303 }
4304 }
4305 if (pno >= 0) {
4306 printf(_("Selected partition %d\n"), pno+1);
4307 return pno;
4308 }
4309 printf(_("No partition is defined yet!\n"));
4310 return -1;
4311
4312 not_unique:
4313 return get_partition(warn, max);
4314}
4315
4316static int
4317get_nonexisting_partition(int warn, int max) {
4318 int pno = -1;
4319 int i;
4320
4321 for (i = 0; i < max; i++) {
4322 struct pte *pe = &ptes[i];
4323 struct partition *p = pe->part_table;
4324
4325 if (p && is_cleared_partition(p)) {
4326 if (pno >= 0)
4327 goto not_unique;
4328 pno = i;
4329 }
4330 }
4331 if (pno >= 0) {
4332 printf(_("Selected partition %d\n"), pno+1);
4333 return pno;
4334 }
4335 printf(_("All primary partitions have been defined already!\n"));
4336 return -1;
4337
4338 not_unique:
4339 return get_partition(warn, max);
4340}
4341
4342
4343void change_units(void)
4344{
4345 display_in_cyl_units = !display_in_cyl_units;
4346 update_units();
4347 printf(_("Changing display/entry units to %s\n"),
4348 str_units(PLURAL));
4349}
4350
4351static void
4352toggle_active(int i) {
4353 struct pte *pe = &ptes[i];
4354 struct partition *p = pe->part_table;
4355
4356 if (IS_EXTENDED (p->sys_ind) && !p->boot_ind)
4357 fprintf(stderr,
4358 _("WARNING: Partition %d is an extended partition\n"),
4359 i + 1);
4360 p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
4361 pe->changed = 1;
4362}
4363
4364static void
4365toggle_dos_compatibility_flag(void) {
4366 dos_compatible_flag = ~dos_compatible_flag;
4367 if (dos_compatible_flag) {
4368 sector_offset = sectors;
4369 printf(_("DOS Compatibility flag is set\n"));
4370 }
4371 else {
4372 sector_offset = 1;
4373 printf(_("DOS Compatibility flag is not set\n"));
4374 }
4375}
4376
4377static void
4378delete_partition(int i) {
4379 struct pte *pe = &ptes[i];
4380 struct partition *p = pe->part_table;
4381 struct partition *q = pe->ext_pointer;
4382
4383/* Note that for the fifth partition (i == 4) we don't actually
4384 * decrement partitions.
4385 */
4386
4387 if (warn_geometry())
4388 return; /* C/H/S not set */
4389 pe->changed = 1;
4390
4391#ifdef CONFIG_FEATURE_SUN_LABEL
4392 if (sun_label) {
4393 sun_delete_partition(i);
4394 return;
4395 }
4396#endif
4397#ifdef CONFIG_FEATURE_SGI_LABEL
4398 if (sgi_label) {
4399 sgi_delete_partition(i);
4400 return;
4401 }
4402#endif
4403
4404 if (i < 4) {
4405 if (IS_EXTENDED (p->sys_ind) && i == ext_index) {
4406 partitions = 4;
4407 ptes[ext_index].ext_pointer = NULL;
4408 extended_offset = 0;
4409 }
4410 clear_partition(p);
4411 return;
4412 }
4413
4414 if (!q->sys_ind && i > 4) {
4415 /* the last one in the chain - just delete */
4416 --partitions;
4417 --i;
4418 clear_partition(ptes[i].ext_pointer);
4419 ptes[i].changed = 1;
4420 } else {
4421 /* not the last one - further ones will be moved down */
4422 if (i > 4) {
4423 /* delete this link in the chain */
4424 p = ptes[i-1].ext_pointer;
4425 *p = *q;
4426 set_start_sect(p, get_start_sect(q));
4427 set_nr_sects(p, get_nr_sects(q));
4428 ptes[i-1].changed = 1;
4429 } else if (partitions > 5) { /* 5 will be moved to 4 */
4430 /* the first logical in a longer chain */
4431 pe = &ptes[5];
4432
4433 if (pe->part_table) /* prevent SEGFAULT */
4434 set_start_sect(pe->part_table,
4435 get_partition_start(pe) -
4436 extended_offset);
4437 pe->offset = extended_offset;
4438 pe->changed = 1;
4439 }
4440
4441 if (partitions > 5) {
4442 partitions--;
4443 while (i < partitions) {
4444 ptes[i] = ptes[i+1];
4445 i++;
4446 }
4447 } else
4448 /* the only logical: clear only */
4449 clear_partition(ptes[i].part_table);
4450 }
4451}
4452
4453static void
4454change_sysid(void) {
4455 int i, sys, origsys;
4456 struct partition *p;
4457
4458#ifdef CONFIG_FEATURE_SGI_LABEL
4459 /* If sgi_label then don't use get_existing_partition,
4460 let the user select a partition, since get_existing_partition()
4461 only works for Linux like partition tables. */
4462 if (!sgi_label) {
4463 i = get_existing_partition(0, partitions);
4464 } else {
4465 i = get_partition(0, partitions);
4466 }
4467#else
4468 i = get_existing_partition(0, partitions);
4469#endif
4470 if (i == -1)
4471 return;
4472 p = ptes[i].part_table;
4473 origsys = sys = get_sysid(i);
4474
4475 /* if changing types T to 0 is allowed, then
4476 the reverse change must be allowed, too */
4477 if (!sys && !sgi_label && !sun_label && !get_nr_sects(p))
4478 printf(_("Partition %d does not exist yet!\n"), i + 1);
4479 else while (1) {
4480 sys = read_hex (get_sys_types());
4481
4482 if (!sys && !sgi_label && !sun_label) {
4483 printf(_("Type 0 means free space to many systems\n"
4484 "(but not to Linux). Having partitions of\n"
4485 "type 0 is probably unwise. You can delete\n"
4486 "a partition using the `d' command.\n"));
4487 /* break; */
4488 }
4489
4490 if (!sun_label && !sgi_label) {
4491 if (IS_EXTENDED (sys) != IS_EXTENDED (p->sys_ind)) {
4492 printf(_("You cannot change a partition into"
4493 " an extended one or vice versa\n"
4494 "Delete it first.\n"));
4495 break;
4496 }
4497 }
4498
4499 if (sys < 256) {
4500#ifdef CONFIG_FEATURE_SUN_LABEL
4501 if (sun_label && i == 2 && sys != WHOLE_DISK)
4502 printf(_("Consider leaving partition 3 "
4503 "as Whole disk (5),\n"
4504 "as SunOS/Solaris expects it and "
4505 "even Linux likes it.\n\n"));
4506#endif
4507#ifdef CONFIG_FEATURE_SGI_LABEL
4508 if (sgi_label && ((i == 10 && sys != ENTIRE_DISK)
4509 || (i == 8 && sys != 0)))
4510 printf(_("Consider leaving partition 9 "
4511 "as volume header (0),\nand "
4512 "partition 11 as entire volume (6)"
4513 "as IRIX expects it.\n\n"));
4514#endif
4515 if (sys == origsys)
4516 break;
4517#ifdef CONFIG_FEATURE_SUN_LABEL
4518 if (sun_label) {
4519 sun_change_sysid(i, sys);
4520 } else
4521#endif
4522#ifdef CONFIG_FEATURE_SGI_LABEL
4523 if (sgi_label) {
4524 sgi_change_sysid(i, sys);
4525 } else
4526#endif
4527 p->sys_ind = sys;
4528 printf (_("Changed system type of partition %d "
4529 "to %x (%s)\n"), i + 1, sys,
4530 partition_type(sys));
4531 ptes[i].changed = 1;
4532 if (is_dos_partition(origsys) ||
4533 is_dos_partition(sys))
4534 dos_changed = 1;
4535 break;
4536 }
4537 }
4538}
4539#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
4540
4541
4542/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
4543 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
4544 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
4545 * Lubkin Oct. 1991). */
4546
4547static void long2chs(ulong ls, uint *c, uint *h, uint *s) {
4548 int spc = heads * sectors;
4549
4550 *c = ls / spc;
4551 ls = ls % spc;
4552 *h = ls / sectors;
4553 *s = ls % sectors + 1; /* sectors count from 1 */
4554}
4555
4556static void check_consistency(const struct partition *p, int partition) {
4557 uint pbc, pbh, pbs; /* physical beginning c, h, s */
4558 uint pec, peh, pes; /* physical ending c, h, s */
4559 uint lbc, lbh, lbs; /* logical beginning c, h, s */
4560 uint lec, leh, les; /* logical ending c, h, s */
4561
4562 if (!heads || !sectors || (partition >= 4))
4563 return; /* do not check extended partitions */
4564
4565/* physical beginning c, h, s */
4566 pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300);
4567 pbh = p->head;
4568 pbs = p->sector & 0x3f;
4569
4570/* physical ending c, h, s */
4571 pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300);
4572 peh = p->end_head;
4573 pes = p->end_sector & 0x3f;
4574
4575/* compute logical beginning (c, h, s) */
4576 long2chs(get_start_sect(p), &lbc, &lbh, &lbs);
4577
4578/* compute logical ending (c, h, s) */
4579 long2chs(get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les);
4580
4581/* Same physical / logical beginning? */
4582 if (cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
4583 printf(_("Partition %d has different physical/logical "
4584 "beginnings (non-Linux?):\n"), partition + 1);
4585 printf(_(" phys=(%d, %d, %d) "), pbc, pbh, pbs);
4586 printf(_("logical=(%d, %d, %d)\n"),lbc, lbh, lbs);
4587 }
4588
4589/* Same physical / logical ending? */
4590 if (cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) {
4591 printf(_("Partition %d has different physical/logical "
4592 "endings:\n"), partition + 1);
4593 printf(_(" phys=(%d, %d, %d) "), pec, peh, pes);
4594 printf(_("logical=(%d, %d, %d)\n"),lec, leh, les);
4595 }
4596
4597#if 0
4598/* Beginning on cylinder boundary? */
4599 if (pbh != !pbc || pbs != 1) {
4600 printf(_("Partition %i does not start on cylinder "
4601 "boundary:\n"), partition + 1);
4602 printf(_(" phys=(%d, %d, %d) "), pbc, pbh, pbs);
4603 printf(_("should be (%d, %d, 1)\n"), pbc, !pbc);
4604 }
4605#endif
4606
4607/* Ending on cylinder boundary? */
4608 if (peh != (heads - 1) || pes != sectors) {
4609 printf(_("Partition %i does not end on cylinder boundary.\n"),
4610 partition + 1);
4611#if 0
4612 printf(_(" phys=(%d, %d, %d) "), pec, peh, pes);
4613 printf(_("should be (%d, %d, %d)\n"),
4614 pec, heads - 1, sectors);
4615#endif
4616 }
4617}
4618
4619static void
4620list_disk_geometry(void) {
4621 long long bytes = (total_number_of_sectors << 9);
4622 long megabytes = bytes/1000000;
4623
4624 if (megabytes < 10000)
4625 printf(_("\nDisk %s: %ld MB, %lld bytes\n"),
4626 disk_device, megabytes, bytes);
4627 else
4628 printf(_("\nDisk %s: %ld.%ld GB, %lld bytes\n"),
4629 disk_device, megabytes/1000, (megabytes/100)%10, bytes);
4630 printf(_("%d heads, %d sectors/track, %d cylinders"),
4631 heads, sectors, cylinders);
4632 if (units_per_sector == 1)
4633 printf(_(", total %llu sectors"),
4634 total_number_of_sectors / (sector_size/512));
4635 printf(_("\nUnits = %s of %d * %d = %d bytes\n\n"),
4636 str_units(PLURAL),
4637 units_per_sector, sector_size, units_per_sector * sector_size);
4638}
4639
4640/*
4641 * Check whether partition entries are ordered by their starting positions.
4642 * Return 0 if OK. Return i if partition i should have been earlier.
4643 * Two separate checks: primary and logical partitions.
4644 */
4645static int
4646wrong_p_order(int *prev) {
4647 const struct pte *pe;
4648 const struct partition *p;
4649 off_t last_p_start_pos = 0, p_start_pos;
4650 int i, last_i = 0;
4651
4652 for (i = 0 ; i < partitions; i++) {
4653 if (i == 4) {
4654 last_i = 4;
4655 last_p_start_pos = 0;
4656 }
4657 pe = &ptes[i];
4658 if ((p = pe->part_table)->sys_ind) {
4659 p_start_pos = get_partition_start(pe);
4660
4661 if (last_p_start_pos > p_start_pos) {
4662 if (prev)
4663 *prev = last_i;
4664 return i;
4665 }
4666
4667 last_p_start_pos = p_start_pos;
4668 last_i = i;
4669 }
4670 }
4671 return 0;
4672}
4673
4674#ifdef CONFIG_FEATURE_FDISK_ADVANCED
4675/*
4676 * Fix the chain of logicals.
4677 * extended_offset is unchanged, the set of sectors used is unchanged
4678 * The chain is sorted so that sectors increase, and so that
4679 * starting sectors increase.
4680 *
4681 * After this it may still be that cfdisk doesnt like the table.
4682 * (This is because cfdisk considers expanded parts, from link to
4683 * end of partition, and these may still overlap.)
4684 * Now
4685 * sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda
4686 * may help.
4687 */
4688static void
4689fix_chain_of_logicals(void) {
4690 int j, oj, ojj, sj, sjj;
4691 struct partition *pj,*pjj,tmp;
4692
4693 /* Stage 1: sort sectors but leave sector of part 4 */
4694 /* (Its sector is the global extended_offset.) */
4695 stage1:
4696 for (j = 5; j < partitions-1; j++) {
4697 oj = ptes[j].offset;
4698 ojj = ptes[j+1].offset;
4699 if (oj > ojj) {
4700 ptes[j].offset = ojj;
4701 ptes[j+1].offset = oj;
4702 pj = ptes[j].part_table;
4703 set_start_sect(pj, get_start_sect(pj)+oj-ojj);
4704 pjj = ptes[j+1].part_table;
4705 set_start_sect(pjj, get_start_sect(pjj)+ojj-oj);
4706 set_start_sect(ptes[j-1].ext_pointer,
4707 ojj-extended_offset);
4708 set_start_sect(ptes[j].ext_pointer,
4709 oj-extended_offset);
4710 goto stage1;
4711 }
4712 }
4713
4714 /* Stage 2: sort starting sectors */
4715 stage2:
4716 for (j = 4; j < partitions-1; j++) {
4717 pj = ptes[j].part_table;
4718 pjj = ptes[j+1].part_table;
4719 sj = get_start_sect(pj);
4720 sjj = get_start_sect(pjj);
4721 oj = ptes[j].offset;
4722 ojj = ptes[j+1].offset;
4723 if (oj+sj > ojj+sjj) {
4724 tmp = *pj;
4725 *pj = *pjj;
4726 *pjj = tmp;
4727 set_start_sect(pj, ojj+sjj-oj);
4728 set_start_sect(pjj, oj+sj-ojj);
4729 goto stage2;
4730 }
4731 }
4732
4733 /* Probably something was changed */
4734 for (j = 4; j < partitions; j++)
4735 ptes[j].changed = 1;
4736}
4737
4738
4739static void
4740fix_partition_table_order(void) {
4741 struct pte *pei, *pek;
4742 int i,k;
4743
4744 if (!wrong_p_order(NULL)) {
4745 printf(_("Nothing to do. Ordering is correct already.\n\n"));
4746 return;
4747 }
4748
4749 while ((i = wrong_p_order(&k)) != 0 && i < 4) {
4750 /* partition i should have come earlier, move it */
4751 /* We have to move data in the MBR */
4752 struct partition *pi, *pk, *pe, pbuf;
4753 pei = &ptes[i];
4754 pek = &ptes[k];
4755
4756 pe = pei->ext_pointer;
4757 pei->ext_pointer = pek->ext_pointer;
4758 pek->ext_pointer = pe;
4759
4760 pi = pei->part_table;
4761 pk = pek->part_table;
4762
4763 memmove(&pbuf, pi, sizeof(struct partition));
4764 memmove(pi, pk, sizeof(struct partition));
4765 memmove(pk, &pbuf, sizeof(struct partition));
4766
4767 pei->changed = pek->changed = 1;
4768 }
4769
4770 if (i)
4771 fix_chain_of_logicals();
4772
4773 printf("Done.\n");
4774
4775}
4776#endif
4777
4778static void
4779list_table(int xtra) {
4780 const struct partition *p;
4781 int i, w;
4782
4783#ifdef CONFIG_FEATURE_SUN_LABEL
4784 if (sun_label) {
4785 sun_list_table(xtra);
4786 return;
4787 }
4788#endif
4789
4790#ifdef CONFIG_FEATURE_SGI_LABEL
4791 if (sgi_label) {
4792 sgi_list_table(xtra);
4793 return;
4794 }
4795#endif
4796
4797 list_disk_geometry();
4798
4799#ifdef CONFIG_FEATURE_OSF_LABEL
4800 if (osf_label) {
4801 xbsd_print_disklabel(xtra);
4802 return;
4803 }
4804#endif
4805
4806 /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3,
4807 but if the device name ends in a digit, say /dev/foo1,
4808 then the partition is called /dev/foo1p3. */
4809 w = strlen(disk_device);
4810 if (w && isdigit(disk_device[w-1]))
4811 w++;
4812 if (w < 5)
4813 w = 5;
4814
4815 printf(_("%*s Boot Start End Blocks Id System\n"),
4816 w+1, _("Device"));
4817
4818 for (i = 0; i < partitions; i++) {
4819 const struct pte *pe = &ptes[i];
4820
4821 p = pe->part_table;
4822 if (p && !is_cleared_partition(p)) {
4823 off_t psects = get_nr_sects(p);
4824 off_t pblocks = psects;
4825 unsigned int podd = 0;
4826
4827 if (sector_size < 1024) {
4828 pblocks /= (1024 / sector_size);
4829 podd = psects % (1024 / sector_size);
4830 }
4831 if (sector_size > 1024)
4832 pblocks *= (sector_size / 1024);
4833 printf(
4834 "%s %c %11llu %11llu %11llu%c %2x %s\n",
4835 partname(disk_device, i+1, w+2),
4836/* boot flag */ !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG
4837 ? '*' : '?',
4838/* start */ (unsigned long long) cround(get_partition_start(pe)),
4839/* end */ (unsigned long long) cround(get_partition_start(pe) + psects
4840 - (psects ? 1 : 0)),
4841/* odd flag on end */ (unsigned long long) pblocks, podd ? '+' : ' ',
4842/* type id */ p->sys_ind,
4843/* type name */ partition_type(p->sys_ind));
4844 check_consistency(p, i);
4845 }
4846 }
4847
4848 /* Is partition table in disk order? It need not be, but... */
4849 /* partition table entries are not checked for correct order if this
4850 is a sgi, sun or aix labeled disk... */
4851 if (dos_label && wrong_p_order(NULL)) {
4852 printf(_("\nPartition table entries are not in disk order\n"));
4853 }
4854}
4855
4856#ifdef CONFIG_FEATURE_FDISK_ADVANCED
4857static void
4858x_list_table(int extend) {
4859 const struct pte *pe;
4860 const struct partition *p;
4861 int i;
4862
4863 printf(_("\nDisk %s: %d heads, %d sectors, %d cylinders\n\n"),
4864 disk_device, heads, sectors, cylinders);
4865 printf(_("Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID\n"));
4866 for (i = 0 ; i < partitions; i++) {
4867 pe = &ptes[i];
4868 p = (extend ? pe->ext_pointer : pe->part_table);
4869 if (p != NULL) {
4870 printf("%2d %02x%4d%4d%5d%4d%4d%5d%11u%11u %02x\n",
4871 i + 1, p->boot_ind, p->head,
4872 sector(p->sector),
4873 cylinder(p->sector, p->cyl), p->end_head,
4874 sector(p->end_sector),
4875 cylinder(p->end_sector, p->end_cyl),
4876 get_start_sect(p), get_nr_sects(p), p->sys_ind);
4877 if (p->sys_ind)
4878 check_consistency(p, i);
4879 }
4880 }
4881}
4882#endif
4883
4884#ifdef CONFIG_FEATURE_FDISK_WRITABLE
4885static void
4886fill_bounds(off_t *first, off_t *last) {
4887 int i;
4888 const struct pte *pe = &ptes[0];
4889 const struct partition *p;
4890
4891 for (i = 0; i < partitions; pe++,i++) {
4892 p = pe->part_table;
4893 if (!p->sys_ind || IS_EXTENDED (p->sys_ind)) {
4894 first[i] = 0xffffffff;
4895 last[i] = 0;
4896 } else {
4897 first[i] = get_partition_start(pe);
4898 last[i] = first[i] + get_nr_sects(p) - 1;
4899 }
4900 }
4901}
4902
4903static void
4904check(int n, uint h, uint s, uint c, off_t start) {
4905 off_t total, real_s, real_c;
4906
4907 real_s = sector(s) - 1;
4908 real_c = cylinder(s, c);
4909 total = (real_c * sectors + real_s) * heads + h;
4910 if (!total)
4911 fprintf(stderr, _("Warning: partition %d contains sector 0\n"), n);
4912 if (h >= heads)
4913 fprintf(stderr,
4914 _("Partition %d: head %d greater than maximum %d\n"),
4915 n, h + 1, heads);
4916 if (real_s >= sectors)
4917 fprintf(stderr, _("Partition %d: sector %d greater than "
4918 "maximum %d\n"), n, s, sectors);
4919 if (real_c >= cylinders)
4920 fprintf(stderr, _("Partitions %d: cylinder %llu greater than "
4921 "maximum %d\n"), n, (unsigned long long)real_c + 1, cylinders);
4922 if (cylinders <= 1024 && start != total)
4923 fprintf(stderr,
4924 _("Partition %d: previous sectors %llu disagrees with "
4925 "total %llu\n"), n, (unsigned long long)start, (unsigned long long)total);
4926}
4927
4928static void
4929verify(void) {
4930 int i, j;
4931 uint total = 1;
4932 off_t first[partitions], last[partitions];
4933 struct partition *p;
4934
4935 if (warn_geometry())
4936 return;
4937
4938#ifdef CONFIG_FEATURE_SUN_LABEL
4939 if (sun_label) {
4940 verify_sun();
4941 return;
4942 }
4943#endif
4944#ifdef CONFIG_FEATURE_SGI_LABEL
4945 if (sgi_label) {
4946 verify_sgi(1);
4947 return;
4948 }
4949#endif
4950
4951 fill_bounds(first, last);
4952 for (i = 0; i < partitions; i++) {
4953 struct pte *pe = &ptes[i];
4954
4955 p = pe->part_table;
4956 if (p->sys_ind && !IS_EXTENDED (p->sys_ind)) {
4957 check_consistency(p, i);
4958 if (get_partition_start(pe) < first[i])
4959 printf(_("Warning: bad start-of-data in "
4960 "partition %d\n"), i + 1);
4961 check(i + 1, p->end_head, p->end_sector, p->end_cyl,
4962 last[i]);
4963 total += last[i] + 1 - first[i];
4964 for (j = 0; j < i; j++)
4965 if ((first[i] >= first[j] && first[i] <= last[j])
4966 || ((last[i] <= last[j] && last[i] >= first[j]))) {
4967 printf(_("Warning: partition %d overlaps "
4968 "partition %d.\n"), j + 1, i + 1);
4969 total += first[i] >= first[j] ?
4970 first[i] : first[j];
4971 total -= last[i] <= last[j] ?
4972 last[i] : last[j];
4973 }
4974 }
4975 }
4976
4977 if (extended_offset) {
4978 struct pte *pex = &ptes[ext_index];
4979 off_t e_last = get_start_sect(pex->part_table) +
4980 get_nr_sects(pex->part_table) - 1;
4981
4982 for (i = 4; i < partitions; i++) {
4983 total++;
4984 p = ptes[i].part_table;
4985 if (!p->sys_ind) {
4986 if (i != 4 || i + 1 < partitions)
4987 printf(_("Warning: partition %d "
4988 "is empty\n"), i + 1);
4989 }
4990 else if (first[i] < extended_offset ||
4991 last[i] > e_last)
4992 printf(_("Logical partition %d not entirely in "
4993 "partition %d\n"), i + 1, ext_index + 1);
4994 }
4995 }
4996
4997 if (total > heads * sectors * cylinders)
4998 printf(_("Total allocated sectors %d greater than the maximum "
4999 "%d\n"), total, heads * sectors * cylinders);
5000 else if ((total = heads * sectors * cylinders - total) != 0)
5001 printf(_("%d unallocated sectors\n"), total);
5002}
5003
5004static void
5005add_partition(int n, int sys) {
5006 char mesg[256]; /* 48 does not suffice in Japanese */
5007 int i, readed = 0;
5008 struct partition *p = ptes[n].part_table;
5009 struct partition *q = ptes[ext_index].part_table;
5010 long long llimit;
5011 off_t start, stop = 0, limit, temp,
5012 first[partitions], last[partitions];
5013
5014 if (p && p->sys_ind) {
5015 printf(_("Partition %d is already defined. Delete "
5016 "it before re-adding it.\n"), n + 1);
5017 return;
5018 }
5019 fill_bounds(first, last);
5020 if (n < 4) {
5021 start = sector_offset;
5022 if (display_in_cyl_units || !total_number_of_sectors)
5023 llimit = heads * sectors * cylinders - 1;
5024 else
5025 llimit = total_number_of_sectors - 1;
5026 limit = llimit;
5027 if (limit != llimit)
5028 limit = 0x7fffffff;
5029 if (extended_offset) {
5030 first[ext_index] = extended_offset;
5031 last[ext_index] = get_start_sect(q) +
5032 get_nr_sects(q) - 1;
5033 }
5034 } else {
5035 start = extended_offset + sector_offset;
5036 limit = get_start_sect(q) + get_nr_sects(q) - 1;
5037 }
5038 if (display_in_cyl_units)
5039 for (i = 0; i < partitions; i++)
5040 first[i] = (cround(first[i]) - 1) * units_per_sector;
5041
5042 snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR));
5043 do {
5044 temp = start;
5045 for (i = 0; i < partitions; i++) {
5046 int lastplusoff;
5047
5048 if (start == ptes[i].offset)
5049 start += sector_offset;
5050 lastplusoff = last[i] + ((n<4) ? 0 : sector_offset);
5051 if (start >= first[i] && start <= lastplusoff)
5052 start = lastplusoff + 1;
5053 }
5054 if (start > limit)
5055 break;
5056 if (start >= temp+units_per_sector && readed) {
5057 printf(_("Sector %llu is already allocated\n"), (unsigned long long)temp);
5058 temp = start;
5059 readed = 0;
5060 }
5061 if (!readed && start == temp) {
5062 off_t saved_start;
5063
5064 saved_start = start;
5065 start = read_int(cround(saved_start), cround(saved_start), cround(limit),
5066 0, mesg);
5067 if (display_in_cyl_units) {
5068 start = (start - 1) * units_per_sector;
5069 if (start < saved_start) start = saved_start;
5070 }
5071 readed = 1;
5072 }
5073 } while (start != temp || !readed);
5074 if (n > 4) { /* NOT for fifth partition */
5075 struct pte *pe = &ptes[n];
5076
5077 pe->offset = start - sector_offset;
5078 if (pe->offset == extended_offset) { /* must be corrected */
5079 pe->offset++;
5080 if (sector_offset == 1)
5081 start++;
5082 }
5083 }
5084
5085 for (i = 0; i < partitions; i++) {
5086 struct pte *pe = &ptes[i];
5087
5088 if (start < pe->offset && limit >= pe->offset)
5089 limit = pe->offset - 1;
5090 if (start < first[i] && limit >= first[i])
5091 limit = first[i] - 1;
5092 }
5093 if (start > limit) {
5094 printf(_("No free sectors available\n"));
5095 if (n > 4)
5096 partitions--;
5097 return;
5098 }
5099 if (cround(start) == cround(limit)) {
5100 stop = limit;
5101 } else {
5102 snprintf(mesg, sizeof(mesg),
5103 _("Last %s or +size or +sizeM or +sizeK"),
5104 str_units(SINGULAR));
5105 stop = read_int(cround(start), cround(limit), cround(limit),
5106 cround(start), mesg);
5107 if (display_in_cyl_units) {
5108 stop = stop * units_per_sector - 1;
5109 if (stop >limit)
5110 stop = limit;
5111 }
5112 }
5113
5114 set_partition(n, 0, start, stop, sys);
5115 if (n > 4)
5116 set_partition(n - 1, 1, ptes[n].offset, stop, EXTENDED);
5117
5118 if (IS_EXTENDED (sys)) {
5119 struct pte *pe4 = &ptes[4];
5120 struct pte *pen = &ptes[n];
5121
5122 ext_index = n;
5123 pen->ext_pointer = p;
5124 pe4->offset = extended_offset = start;
5125 pe4->sectorbuffer = xcalloc(1, sector_size);
5126 pe4->part_table = pt_offset(pe4->sectorbuffer, 0);
5127 pe4->ext_pointer = pe4->part_table + 1;
5128 pe4->changed = 1;
5129 partitions = 5;
5130 }
5131}
5132
5133static void
5134add_logical(void) {
5135 if (partitions > 5 || ptes[4].part_table->sys_ind) {
5136 struct pte *pe = &ptes[partitions];
5137
5138 pe->sectorbuffer = xcalloc(1, sector_size);
5139 pe->part_table = pt_offset(pe->sectorbuffer, 0);
5140 pe->ext_pointer = pe->part_table + 1;
5141 pe->offset = 0;
5142 pe->changed = 1;
5143 partitions++;
5144 }
5145 add_partition(partitions - 1, LINUX_NATIVE);
5146}
5147
5148static void
5149new_partition(void) {
5150 int i, free_primary = 0;
5151
5152 if (warn_geometry())
5153 return;
5154
5155#ifdef CONFIG_FEATURE_SUN_LABEL
5156 if (sun_label) {
5157 add_sun_partition(get_partition(0, partitions), LINUX_NATIVE);
5158 return;
5159 }
5160#endif
5161#ifdef CONFIG_FEATURE_SGI_LABEL
5162 if (sgi_label) {
5163 sgi_add_partition(get_partition(0, partitions), LINUX_NATIVE);
5164 return;
5165 }
5166#endif
5167#ifdef CONFIG_FEATURE_AIX_LABEL
5168 if (aix_label) {
5169 printf(_("\tSorry - this fdisk cannot handle AIX disk labels."
5170 "\n\tIf you want to add DOS-type partitions, create"
5171 "\n\ta new empty DOS partition table first. (Use o.)"
5172 "\n\tWARNING: "
5173 "This will destroy the present disk contents.\n"));
5174 return;
5175 }
5176#endif
5177
5178 for (i = 0; i < 4; i++)
5179 free_primary += !ptes[i].part_table->sys_ind;
5180
5181 if (!free_primary && partitions >= MAXIMUM_PARTS) {
5182 printf(_("The maximum number of partitions has been created\n"));
5183 return;
5184 }
5185
5186 if (!free_primary) {
5187 if (extended_offset)
5188 add_logical();
5189 else
5190 printf(_("You must delete some partition and add "
5191 "an extended partition first\n"));
5192 } else {
5193 char c, line[LINE_LENGTH];
5194 snprintf(line, sizeof(line), "%s\n %s\n p primary "
5195 "partition (1-4)\n",
5196 "Command action", (extended_offset ?
5197 "l logical (5 or over)" : "e extended"));
5198 while (1) {
5199 if ((c = read_char(line)) == 'p' || c == 'P') {
5200 i = get_nonexisting_partition(0, 4);
5201 if (i >= 0)
5202 add_partition(i, LINUX_NATIVE);
5203 return;
5204 }
5205 else if (c == 'l' && extended_offset) {
5206 add_logical();
5207 return;
5208 }
5209 else if (c == 'e' && !extended_offset) {
5210 i = get_nonexisting_partition(0, 4);
5211 if (i >= 0)
5212 add_partition(i, EXTENDED);
5213 return;
5214 }
5215 else
5216 printf(_("Invalid partition number "
5217 "for type `%c'\n"), c);
5218 }
5219 }
5220}
5221
5222static void
5223write_table(void) {
5224 int i;
5225
5226 if (dos_label) {
5227 for (i=0; i<3; i++)
5228 if (ptes[i].changed)
5229 ptes[3].changed = 1;
5230 for (i = 3; i < partitions; i++) {
5231 struct pte *pe = &ptes[i];
5232
5233 if (pe->changed) {
5234 write_part_table_flag(pe->sectorbuffer);
5235 write_sector(pe->offset, pe->sectorbuffer);
5236 }
5237 }
5238 }
5239#ifdef CONFIG_FEATURE_SGI_LABEL
5240 else if (sgi_label) {
5241 /* no test on change? the printf below might be mistaken */
5242 sgi_write_table();
5243 }
5244#endif
5245#ifdef CONFIG_FEATURE_SUN_LABEL
5246 else if (sun_label) {
5247 int needw = 0;
5248
5249 for (i=0; i<8; i++)
5250 if (ptes[i].changed)
5251 needw = 1;
5252 if (needw)
5253 sun_write_table();
5254 }
5255#endif
5256
5257 printf(_("The partition table has been altered!\n\n"));
5258 reread_partition_table(1);
5259}
5260
5261void
5262reread_partition_table(int leave) {
5263 int error = 0;
5264 int i;
5265
5266 printf(_("Calling ioctl() to re-read partition table.\n"));
5267 sync();
5268 sleep(2);
5269 if ((i = ioctl(fd, BLKRRPART)) != 0) {
5270 error = errno;
5271 } else {
5272 /* some kernel versions (1.2.x) seem to have trouble
5273 rereading the partition table, but if asked to do it
5274 twice, the second time works. - biro@yggdrasil.com */
5275 sync();
5276 sleep(2);
5277 if ((i = ioctl(fd, BLKRRPART)) != 0)
5278 error = errno;
5279 }
5280
5281 if (i) {
5282 printf(_("\nWARNING: Re-reading the partition table "
5283 "failed with error %d: %s.\n"
5284 "The kernel still uses the old table.\n"
5285 "The new table will be used "
5286 "at the next reboot.\n"),
5287 error, strerror(error));
5288 }
5289
5290 if (dos_changed)
5291 printf(
5292 _("\nWARNING: If you have created or modified any DOS 6.x\n"
5293 "partitions, please see the fdisk manual page for additional\n"
5294 "information.\n"));
5295
5296 if (leave) {
5297 close(fd);
5298
5299 printf(_("Syncing disks.\n"));
5300 sync();
5301 sleep(4); /* for sync() */
5302 exit(!!i);
5303 }
5304}
5305#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
5306
5307#ifdef CONFIG_FEATURE_FDISK_ADVANCED
5308#define MAX_PER_LINE 16
5309static void
5310print_buffer(char pbuffer[]) {
5311 int i,
5312 l;
5313
5314 for (i = 0, l = 0; i < sector_size; i++, l++) {
5315 if (l == 0)
5316 printf("0x%03X:", i);
5317 printf(" %02X", (unsigned char) pbuffer[i]);
5318 if (l == MAX_PER_LINE - 1) {
5319 printf("\n");
5320 l = -1;
5321 }
5322 }
5323 if (l > 0)
5324 printf("\n");
5325 printf("\n");
5326}
5327
5328
5329static void
5330print_raw(void) {
5331 int i;
5332
5333 printf(_("Device: %s\n"), disk_device);
5334#if defined(CONFIG_FEATURE_SGI_LABEL) || defined(CONFIG_FEATURE_SUN_LABEL)
5335 if (sun_label || sgi_label)
5336 print_buffer(MBRbuffer);
5337 else
5338#endif
5339 for (i = 3; i < partitions; i++)
5340 print_buffer(ptes[i].sectorbuffer);
5341}
5342
5343static void
5344move_begin(int i) {
5345 struct pte *pe = &ptes[i];
5346 struct partition *p = pe->part_table;
5347 off_t new, first;
5348
5349 if (warn_geometry())
5350 return;
5351 if (!p->sys_ind || !get_nr_sects(p) || IS_EXTENDED (p->sys_ind)) {
5352 printf(_("Partition %d has no data area\n"), i + 1);
5353 return;
5354 }
5355 first = get_partition_start(pe);
5356 new = read_int(first, first, first + get_nr_sects(p) - 1, first,
5357 _("New beginning of data")) - pe->offset;
5358
5359 if (new != get_nr_sects(p)) {
5360 first = get_nr_sects(p) + get_start_sect(p) - new;
5361 set_nr_sects(p, first);
5362 set_start_sect(p, new);
5363 pe->changed = 1;
5364 }
5365}
5366
5367static void
5368xselect(void) {
5369 char c;
5370
5371 while(1) {
5372 putchar('\n');
5373 c = tolower(read_char(_("Expert command (m for help): ")));
5374 switch (c) {
5375 case 'a':
5376#ifdef CONFIG_FEATURE_SUN_LABEL
5377 if (sun_label)
5378 sun_set_alt_cyl();
5379#endif
5380 break;
5381 case 'b':
5382 if (dos_label)
5383 move_begin(get_partition(0, partitions));
5384 break;
5385 case 'c':
5386 user_cylinders = cylinders =
5387 read_int(1, cylinders, 1048576, 0,
5388 _("Number of cylinders"));
5389#ifdef CONFIG_FEATURE_SUN_LABEL
5390 if (sun_label)
5391 sun_set_ncyl(cylinders);
5392#endif
5393 if (dos_label)
5394 warn_cylinders();
5395 break;
5396 case 'd':
5397 print_raw();
5398 break;
5399 case 'e':
5400#ifdef CONFIG_FEATURE_SGI_LABEL
5401 if (sgi_label)
5402 sgi_set_xcyl();
5403 else
5404#endif
5405#ifdef CONFIG_FEATURE_SUN_LABEL
5406 if (sun_label)
5407 sun_set_xcyl();
5408 else
5409#endif
5410 if (dos_label)
5411 x_list_table(1);
5412 break;
5413 case 'f':
5414 if (dos_label)
5415 fix_partition_table_order();
5416 break;
5417 case 'g':
5418#ifdef CONFIG_FEATURE_SGI_LABEL
5419 create_sgilabel();
5420#endif
5421 break;
5422 case 'h':
5423 user_heads = heads = read_int(1, heads, 256, 0,
5424 _("Number of heads"));
5425 update_units();
5426 break;
5427 case 'i':
5428#ifdef CONFIG_FEATURE_SUN_LABEL
5429 if (sun_label)
5430 sun_set_ilfact();
5431#endif
5432 break;
5433 case 'o':
5434#ifdef CONFIG_FEATURE_SUN_LABEL
5435 if (sun_label)
5436 sun_set_rspeed();
5437#endif
5438 break;
5439 case 'p':
5440#ifdef CONFIG_FEATURE_SUN_LABEL
5441 if (sun_label)
5442 list_table(1);
5443 else
5444#endif
5445 x_list_table(0);
5446 break;
5447 case 'q':
5448 close(fd);
5449 printf("\n");
5450 exit(0);
5451 case 'r':
5452 return;
5453 case 's':
5454 user_sectors = sectors = read_int(1, sectors, 63, 0,
5455 _("Number of sectors"));
5456 if (dos_compatible_flag) {
5457 sector_offset = sectors;
5458 fprintf(stderr, _("Warning: setting "
5459 "sector offset for DOS "
5460 "compatiblity\n"));
5461 }
5462 update_units();
5463 break;
5464 case 'v':
5465 verify();
5466 break;
5467 case 'w':
5468 write_table(); /* does not return */
5469 break;
5470 case 'y':
5471#ifdef CONFIG_FEATURE_SUN_LABEL
5472 if (sun_label)
5473 sun_set_pcylcount();
5474#endif
5475 break;
5476 default:
5477 xmenu();
5478 }
5479 }
5480}
5481#endif /* ADVANCED mode */
5482
5483static int
5484is_ide_cdrom_or_tape(const char *device) {
5485 FILE *procf;
5486 char buf[100];
5487 struct stat statbuf;
5488 int is_ide = 0;
5489
5490 /* No device was given explicitly, and we are trying some
5491 likely things. But opening /dev/hdc may produce errors like
5492 "hdc: tray open or drive not ready"
5493 if it happens to be a CD-ROM drive. It even happens that
5494 the process hangs on the attempt to read a music CD.
5495 So try to be careful. This only works since 2.1.73. */
5496
5497 if (strncmp("/dev/hd", device, 7))
5498 return 0;
5499
5500 snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5);
5501 procf = fopen(buf, "r");
5502 if (procf != NULL && fgets(buf, sizeof(buf), procf))
5503 is_ide = (!strncmp(buf, "cdrom", 5) ||
5504 !strncmp(buf, "tape", 4));
5505 else
5506 /* Now when this proc file does not exist, skip the
5507 device when it is read-only. */
5508 if (stat(device, &statbuf) == 0)
5509 is_ide = ((statbuf.st_mode & 0222) == 0);
5510
5511 if (procf)
5512 fclose(procf);
5513 return is_ide;
5514}
5515
5516static void
5517try(const char *device, int user_specified) {
5518 int gb;
5519
5520 disk_device = device;
5521 if (setjmp(listingbuf))
5522 return;
5523 if (!user_specified)
5524 if (is_ide_cdrom_or_tape(device))
5525 return;
5526 if ((fd = open(disk_device, type_open)) >= 0) {
5527 gb = get_boot(try_only);
5528 if (gb > 0) { /* I/O error */
5529 close(fd);
5530 } else if (gb < 0) { /* no DOS signature */
5531 list_disk_geometry();
5532 if (aix_label)
5533 return;
5534#ifdef CONFIG_FEATURE_OSF_LABEL
5535 if (btrydev(device) < 0)
5536#endif
5537 fprintf(stderr,
5538 _("Disk %s doesn't contain a valid "
5539 "partition table\n"), device);
5540 close(fd);
5541 } else {
5542 close(fd);
5543 list_table(0);
5544#ifdef CONFIG_FEATURE_FDISK_WRITABLE
5545 if (!sun_label && partitions > 4)
5546 delete_partition(ext_index);
5547#endif
5548 }
5549 } else {
5550 /* Ignore other errors, since we try IDE
5551 and SCSI hard disks which may not be
5552 installed on the system. */
5553 if (errno == EACCES) {
5554 fprintf(stderr, _("Cannot open %s\n"), device);
5555 return;
5556 }
5557 }
5558}
5559
5560/* for fdisk -l: try all things in /proc/partitions
5561 that look like a partition name (do not end in a digit) */
5562static void
5563tryprocpt(void) {
5564 FILE *procpt;
5565 char line[100], ptname[100], devname[120], *s;
5566 int ma, mi, sz;
5567
5568 procpt = bb_wfopen(PROC_PARTITIONS, "r");
5569
5570 while (fgets(line, sizeof(line), procpt)) {
5571 if (sscanf (line, " %d %d %d %[^\n ]",
5572 &ma, &mi, &sz, ptname) != 4)
5573 continue;
5574 for (s = ptname; *s; s++);
5575 if (isdigit(s[-1]))
5576 continue;
5577 sprintf(devname, "/dev/%s", ptname);
5578 try(devname, 0);
5579 }
5580#ifdef CONFIG_FEATURE_CLEAN_UP
5581 fclose(procpt);
5582#endif
5583}
5584
5585#ifdef CONFIG_FEATURE_FDISK_WRITABLE
5586static void
5587unknown_command(int c) {
5588 printf(_("%c: unknown command\n"), c);
5589}
5590#endif
5591
5592int fdisk_main(int argc, char **argv) {
5593 int c;
5594#ifdef CONFIG_FEATURE_FDISK_WRITABLE
5595 int optl = 0;
5596#endif
5597#ifdef CONFIG_FEATURE_FDISK_BLKSIZE
5598 int opts = 0;
5599#endif
5600 /*
5601 * Calls:
5602 * fdisk -v
5603 * fdisk -l [-b sectorsize] [-u] device ...
5604 * fdisk -s [partition] ...
5605 * fdisk [-b sectorsize] [-u] device
5606 *
5607 * Options -C, -H, -S set the geometry.
5608 *
5609 */
5610 while ((c = getopt(argc, argv, "b:C:H:lS:uvV"
5611#ifdef CONFIG_FEATURE_FDISK_BLKSIZE
5612 "s"
5613#endif
5614 )) != -1) {
5615 switch (c) {
5616 case 'b':
5617 /* Ugly: this sector size is really per device,
5618 so cannot be combined with multiple disks,
5619 and te same goes for the C/H/S options.
5620 */
5621 sector_size = atoi(optarg);
5622 if (sector_size != 512 && sector_size != 1024 &&
5623 sector_size != 2048)
5624 bb_show_usage();
5625 sector_offset = 2;
5626 user_set_sector_size = 1;
5627 break;
5628 case 'C':
5629 user_cylinders = atoi(optarg);
5630 break;
5631 case 'H':
5632 user_heads = atoi(optarg);
5633 if (user_heads <= 0 || user_heads >= 256)
5634 user_heads = 0;
5635 break;
5636 case 'S':
5637 user_sectors = atoi(optarg);
5638 if (user_sectors <= 0 || user_sectors >= 64)
5639 user_sectors = 0;
5640 break;
5641 case 'l':
5642#ifdef CONFIG_FEATURE_FDISK_WRITABLE
5643 optl = 1;
5644#endif
5645 break;
5646#ifdef CONFIG_FEATURE_FDISK_BLKSIZE
5647 case 's':
5648 opts = 1;
5649 break;
5650#endif
5651 case 'u':
5652 display_in_cyl_units = 0;
5653 break;
5654 case 'V':
5655 case 'v':
5656 printf("fdisk v" UTIL_LINUX_VERSION "\n");
5657 return 0;
5658 default:
5659 bb_show_usage();
5660 }
5661 }
5662
5663#if 0
5664 printf(_("This kernel finds the sector size itself - "
5665 "-b option ignored\n"));
5666#else
5667 if (user_set_sector_size && argc-optind != 1)
5668 printf(_("Warning: the -b (set sector size) option should"
5669 " be used with one specified device\n"));
5670#endif
5671
5672#ifdef CONFIG_FEATURE_FDISK_WRITABLE
5673 if (optl) {
5674 nowarn = 1;
5675#endif
5676 type_open = O_RDONLY;
5677 if (argc > optind) {
5678 int k;
5679#if __GNUC__
5680 /* avoid gcc warning:
5681 variable `k' might be clobbered by `longjmp' */
5682 (void)&k;
5683#endif
5684 listing = 1;
5685 for (k=optind; k<argc; k++)
5686 try(argv[k], 1);
5687 } else {
5688 /* we no longer have default device names */
5689 /* but, we can use /proc/partitions instead */
5690 tryprocpt();
5691 }
5692 return 0;
5693#ifdef CONFIG_FEATURE_FDISK_WRITABLE
5694 }
5695#endif
5696
5697#ifdef CONFIG_FEATURE_FDISK_BLKSIZE
5698 if (opts) {
5699 long size;
5700 int j;
5701
5702 nowarn = 1;
5703 type_open = O_RDONLY;
5704
5705 opts = argc - optind;
5706 if (opts <= 0)
5707 bb_show_usage();
5708
5709 for (j = optind; j < argc; j++) {
5710 disk_device = argv[j];
5711 if ((fd = open(disk_device, type_open)) < 0)
5712 fdisk_fatal(unable_to_open);
5713 if (ioctl(fd, BLKGETSIZE, &size))
5714 fdisk_fatal(ioctl_error);
5715 close(fd);
5716 if (opts == 1)
5717 printf("%ld\n", size/2);
5718 else
5719 printf("%s: %ld\n", argv[j], size/2);
5720 }
5721 return 0;
5722 }
5723#endif
5724
5725#ifdef CONFIG_FEATURE_FDISK_WRITABLE
5726 if (argc-optind == 1)
5727 disk_device = argv[optind];
5728 else
5729 bb_show_usage();
5730
5731 get_boot(fdisk);
5732
5733#ifdef CONFIG_FEATURE_OSF_LABEL
5734 if (osf_label) {
5735 /* OSF label, and no DOS label */
5736 printf(_("Detected an OSF/1 disklabel on %s, entering "
5737 "disklabel mode.\n"),
5738 disk_device);
5739 bselect();
5740 osf_label = 0;
5741 /* If we return we may want to make an empty DOS label? */
5742 }
5743#endif
5744
5745 while (1) {
5746 putchar('\n');
5747 c = tolower(read_char(_("Command (m for help): ")));
5748 switch (c) {
5749 case 'a':
5750 if (dos_label)
5751 toggle_active(get_partition(1, partitions));
5752#ifdef CONFIG_FEATURE_SUN_LABEL
5753 else if (sun_label)
5754 toggle_sunflags(get_partition(1, partitions),
5755 0x01);
5756#endif
5757#ifdef CONFIG_FEATURE_SGI_LABEL
5758 else if (sgi_label)
5759 sgi_set_bootpartition(
5760 get_partition(1, partitions));
5761#endif
5762 else
5763 unknown_command(c);
5764 break;
5765 case 'b':
5766#ifdef CONFIG_FEATURE_SGI_LABEL
5767 if (sgi_label) {
5768 printf(_("\nThe current boot file is: %s\n"),
5769 sgi_get_bootfile());
5770 if (read_chars(_("Please enter the name of the "
5771 "new boot file: ")) == '\n')
5772 printf(_("Boot file unchanged\n"));
5773 else
5774 sgi_set_bootfile(line_ptr);
5775 } else
5776#endif
5777#ifdef CONFIG_FEATURE_OSF_LABEL
5778 bselect();
5779#endif
5780 break;
5781 case 'c':
5782 if (dos_label)
5783 toggle_dos_compatibility_flag();
5784#ifdef CONFIG_FEATURE_SUN_LABEL
5785 else if (sun_label)
5786 toggle_sunflags(get_partition(1, partitions),
5787 0x10);
5788#endif
5789#ifdef CONFIG_FEATURE_SGI_LABEL
5790 else if (sgi_label)
5791 sgi_set_swappartition(
5792 get_partition(1, partitions));
5793#endif
5794 else
5795 unknown_command(c);
5796 break;
5797 case 'd':
5798 {
5799 int j;
5800#ifdef CONFIG_FEATURE_SGI_LABEL
5801 /* If sgi_label then don't use get_existing_partition,
5802 let the user select a partition, since
5803 get_existing_partition() only works for Linux-like
5804 partition tables */
5805 if (!sgi_label) {
5806 j = get_existing_partition(1, partitions);
5807 } else {
5808 j = get_partition(1, partitions);
5809 }
5810#else
5811 j = get_existing_partition(1, partitions);
5812#endif
5813 if (j >= 0)
5814 delete_partition(j);
5815 }
5816 break;
5817 case 'i':
5818#ifdef CONFIG_FEATURE_SGI_LABEL
5819 if (sgi_label)
5820 create_sgiinfo();
5821 else
5822#endif
5823 unknown_command(c);
5824 case 'l':
5825 list_types(get_sys_types());
5826 break;
5827 case 'm':
5828 menu();
5829 break;
5830 case 'n':
5831 new_partition();
5832 break;
5833 case 'o':
5834 create_doslabel();
5835 break;
5836 case 'p':
5837 list_table(0);
5838 break;
5839 case 'q':
5840 close(fd);
5841 printf("\n");
5842 return 0;
5843 case 's':
5844#ifdef CONFIG_FEATURE_SUN_LABEL
5845 create_sunlabel();
5846#endif
5847 break;
5848 case 't':
5849 change_sysid();
5850 break;
5851 case 'u':
5852 change_units();
5853 break;
5854 case 'v':
5855 verify();
5856 break;
5857 case 'w':
5858 write_table(); /* does not return */
5859 break;
5860#ifdef CONFIG_FEATURE_FDISK_ADVANCED
5861 case 'x':
5862#ifdef CONFIG_FEATURE_SGI_LABEL
5863 if (sgi_label) {
5864 fprintf(stderr,
5865 _("\n\tSorry, no experts menu for SGI "
5866 "partition tables available.\n\n"));
5867 } else
5868#endif
5869
5870 xselect();
5871 break;
5872#endif
5873 default:
5874 unknown_command(c);
5875 menu();
5876 }
5877 }
5878 return 0;
5879#endif /* CONFIG_FEATURE_FDISK_WRITABLE */
5880}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * freeramdisk implementation for busybox
4 *
5 * Copyright (C) 2000 and written by Emanuele Caratti <wiz@iol.it>
6 * Adjusted a bit by Erik Andersen <andersen@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <stdio.h>
25#include <string.h>
26#include <sys/types.h>
27#include <fcntl.h>
28#include <sys/ioctl.h>
29#include <stdlib.h>
30#include <unistd.h>
31#include "busybox.h"
32
33
34/* From linux/fs.h */
35#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */
36
37extern int
38freeramdisk_main(int argc, char **argv)
39{
40 int result;
41 int fd;
42
43 if (argc != 2) {
44 bb_show_usage();
45 }
46
47 fd = bb_xopen(argv[1], O_RDWR);
48
49 result = ioctl(fd, BLKFLSBUF);
50#ifdef CONFIG_FEATURE_CLEAN_UP
51 close(fd);
52#endif
53 if (result < 0) {
54 bb_perror_msg_and_die("failed ioctl on %s", argv[1]);
55 }
56
57 /* Don't bother closing. Exit does
58 * that, so we can save a few bytes */
59 return EXIT_SUCCESS;
60}
61
62/*
63Local Variables:
64c-file-style: "linux"
65c-basic-offset: 4
66tab-width: 4
67End:
68*/
69
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * fsck.c - a file system consistency checker for Linux.
4 *
5 * (C) 1991, 1992 Linus Torvalds. This file may be redistributed
6 * as per the GNU copyleft.
7 */
8
9/*
10 * 09.11.91 - made the first rudimentary functions
11 *
12 * 10.11.91 - updated, does checking, no repairs yet.
13 * Sent out to the mailing-list for testing.
14 *
15 * 14.11.91 - Testing seems to have gone well. Added some
16 * correction-code, and changed some functions.
17 *
18 * 15.11.91 - More correction code. Hopefully it notices most
19 * cases now, and tries to do something about them.
20 *
21 * 16.11.91 - More corrections (thanks to Mika Jalava). Most
22 * things seem to work now. Yeah, sure.
23 *
24 *
25 * 19.04.92 - Had to start over again from this old version, as a
26 * kernel bug ate my enhanced fsck in february.
27 *
28 * 28.02.93 - added support for different directory entry sizes..
29 *
30 * Sat Mar 6 18:59:42 1993, faith@cs.unc.edu: Output namelen with
31 * super-block information
32 *
33 * Sat Oct 9 11:17:11 1993, faith@cs.unc.edu: make exit status conform
34 * to that required by fsutil
35 *
36 * Mon Jan 3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu)
37 * Added support for file system valid flag. Also
38 * added program_version variable and output of
39 * program name and version number when program
40 * is executed.
41 *
42 * 30.10.94 - added support for v2 filesystem
43 * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
44 *
45 * 10.12.94 - added test to prevent checking of mounted fs adapted
46 * from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck
47 * program. (Daniel Quinlan, quinlan@yggdrasil.com)
48 *
49 * 01.07.96 - Fixed the v2 fs stuff to use the right #defines and such
50 * for modern libcs (janl@math.uio.no, Nicolai Langfeldt)
51 *
52 * 02.07.96 - Added C bit fiddling routines from rmk@ecs.soton.ac.uk
53 * (Russell King). He made them for ARM. It would seem
54 * that the ARM is powerful enough to do this in C whereas
55 * i386 and m64k must use assembly to get it fast >:-)
56 * This should make minix fsck system-independent.
57 * (janl@math.uio.no, Nicolai Langfeldt)
58 *
59 * 04.11.96 - Added minor fixes from Andreas Schwab to avoid compiler
60 * warnings. Added mc68k bitops from
61 * Joerg Dorchain <dorchain@mpi-sb.mpg.de>.
62 *
63 * 06.11.96 - Added v2 code submitted by Joerg Dorchain, but written by
64 * Andreas Schwab.
65 *
66 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
67 * - added Native Language Support
68 *
69 *
70 * I've had no time to add comments - hopefully the function names
71 * are comments enough. As with all file system checkers, this assumes
72 * the file system is quiescent - don't use it on a mounted device
73 * unless you can be sure nobody is writing to it (and remember that the
74 * kernel can write to it when it searches for files).
75 *
76 * Usage: fsck [-larvsm] device
77 * -l for a listing of all the filenames
78 * -a for automatic repairs (not implemented)
79 * -r for repairs (interactive) (not implemented)
80 * -v for verbose (tells how many files)
81 * -s for super-block info
82 * -m for minix-like "mode not cleared" warnings
83 * -f force filesystem check even if filesystem marked as valid
84 *
85 * The device may be a block device or a image of one, but this isn't
86 * enforced (but it's not much fun on a character device :-).
87 */
88
89#include <stdio.h>
90#include <errno.h>
91#include <unistd.h>
92#include <string.h>
93#include <fcntl.h>
94#include <ctype.h>
95#include <stdlib.h>
96#include <termios.h>
97#include <mntent.h>
98#include <sys/param.h>
99#include "busybox.h"
100
101static const int MINIX_ROOT_INO = 1;
102static const int MINIX_LINK_MAX = 250;
103static const int MINIX2_LINK_MAX = 65530;
104
105static const int MINIX_I_MAP_SLOTS = 8;
106static const int MINIX_Z_MAP_SLOTS = 64;
107static const int MINIX_SUPER_MAGIC = 0x137F; /* original minix fs */
108static const int MINIX_SUPER_MAGIC2 = 0x138F; /* minix fs, 30 char names */
109static const int MINIX2_SUPER_MAGIC = 0x2468; /* minix V2 fs */
110static const int MINIX2_SUPER_MAGIC2 = 0x2478; /* minix V2 fs, 30 char names */
111static const int MINIX_VALID_FS = 0x0001; /* Clean fs. */
112static const int MINIX_ERROR_FS = 0x0002; /* fs has errors. */
113
114#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode)))
115#define MINIX2_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix2_inode)))
116
117static const int MINIX_V1 = 0x0001; /* original minix fs */
118static const int MINIX_V2 = 0x0002; /* minix V2 fs */
119
120#define INODE_VERSION(inode) inode->i_sb->u.minix_sb.s_version
121
122/*
123 * This is the original minix inode layout on disk.
124 * Note the 8-bit gid and atime and ctime.
125 */
126struct minix_inode {
127 uint16_t i_mode;
128 uint16_t i_uid;
129 uint32_t i_size;
130 uint32_t i_time;
131 uint8_t i_gid;
132 uint8_t i_nlinks;
133 uint16_t i_zone[9];
134};
135
136/*
137 * The new minix inode has all the time entries, as well as
138 * long block numbers and a third indirect block (7+1+1+1
139 * instead of 7+1+1). Also, some previously 8-bit values are
140 * now 16-bit. The inode is now 64 bytes instead of 32.
141 */
142struct minix2_inode {
143 uint16_t i_mode;
144 uint16_t i_nlinks;
145 uint16_t i_uid;
146 uint16_t i_gid;
147 uint32_t i_size;
148 uint32_t i_atime;
149 uint32_t i_mtime;
150 uint32_t i_ctime;
151 uint32_t i_zone[10];
152};
153
154/*
155 * minix super-block data on disk
156 */
157struct minix_super_block {
158 uint16_t s_ninodes;
159 uint16_t s_nzones;
160 uint16_t s_imap_blocks;
161 uint16_t s_zmap_blocks;
162 uint16_t s_firstdatazone;
163 uint16_t s_log_zone_size;
164 uint32_t s_max_size;
165 uint16_t s_magic;
166 uint16_t s_state;
167 uint32_t s_zones;
168};
169
170struct minix_dir_entry {
171 uint16_t inode;
172 char name[0];
173};
174
175#define BLOCK_SIZE_BITS 10
176#define BLOCK_SIZE (1<<BLOCK_SIZE_BITS)
177
178#define NAME_MAX 255 /* # chars in a file name */
179
180#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode)))
181
182#ifndef BLKGETSIZE
183#define BLKGETSIZE _IO(0x12,96) /* return device size */
184#endif
185
186#ifndef __linux__
187#define volatile
188#endif
189
190static const int ROOT_INO = 1;
191
192#define UPPER(size,n) ((size+((n)-1))/(n))
193#define INODE_SIZE (sizeof(struct minix_inode))
194#ifdef CONFIG_FEATURE_MINIX2
195#define INODE_SIZE2 (sizeof(struct minix2_inode))
196#define INODE_BLOCKS UPPER(INODES, (version2 ? MINIX2_INODES_PER_BLOCK \
197 : MINIX_INODES_PER_BLOCK))
198#else
199#define INODE_BLOCKS UPPER(INODES, (MINIX_INODES_PER_BLOCK))
200#endif
201#define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE)
202
203#define BITS_PER_BLOCK (BLOCK_SIZE<<3)
204
205static char *program_version = "1.2 - 11/11/96";
206static char *device_name = NULL;
207static int IN;
208static int repair = 0, automatic = 0, verbose = 0, list = 0, show =
209 0, warn_mode = 0, force = 0;
210static int directory = 0, regular = 0, blockdev = 0, chardev = 0, links =
211 0, symlinks = 0, total = 0;
212
213static int changed = 0; /* flags if the filesystem has been changed */
214static int errors_uncorrected = 0; /* flag if some error was not corrected */
215static int dirsize = 16;
216static int namelen = 14;
217static int version2 = 0;
218static struct termios termios;
219static int termios_set = 0;
220
221/* File-name data */
222static const int MAX_DEPTH = 32;
223static int name_depth = 0;
224// static char name_list[MAX_DEPTH][BUFSIZ + 1];
225static char **name_list = NULL;
226
227static char *inode_buffer = NULL;
228
229#define Inode (((struct minix_inode *) inode_buffer)-1)
230#define Inode2 (((struct minix2_inode *) inode_buffer)-1)
231static char super_block_buffer[BLOCK_SIZE];
232
233#define Super (*(struct minix_super_block *)super_block_buffer)
234#define INODES ((unsigned long)Super.s_ninodes)
235#ifdef CONFIG_FEATURE_MINIX2
236#define ZONES ((unsigned long)(version2 ? Super.s_zones : Super.s_nzones))
237#else
238#define ZONES ((unsigned long)(Super.s_nzones))
239#endif
240#define IMAPS ((unsigned long)Super.s_imap_blocks)
241#define ZMAPS ((unsigned long)Super.s_zmap_blocks)
242#define FIRSTZONE ((unsigned long)Super.s_firstdatazone)
243#define ZONESIZE ((unsigned long)Super.s_log_zone_size)
244#define MAXSIZE ((unsigned long)Super.s_max_size)
245#define MAGIC (Super.s_magic)
246#define NORM_FIRSTZONE (2+IMAPS+ZMAPS+INODE_BLOCKS)
247
248static char *inode_map;
249static char *zone_map;
250
251static unsigned char *inode_count = NULL;
252static unsigned char *zone_count = NULL;
253
254static void recursive_check(unsigned int ino);
255#ifdef CONFIG_FEATURE_MINIX2
256static void recursive_check2(unsigned int ino);
257#endif
258
259static inline int bit(char * a,unsigned int i)
260{
261 return (a[i >> 3] & (1<<(i & 7))) != 0;
262}
263#define inode_in_use(x) (bit(inode_map,(x)))
264#define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1))
265
266#define mark_inode(x) (setbit(inode_map,(x)),changed=1)
267#define unmark_inode(x) (clrbit(inode_map,(x)),changed=1)
268
269#define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1),changed=1)
270#define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1),changed=1)
271
272static void leave(int) __attribute__ ((noreturn));
273static void leave(int status)
274{
275 if (termios_set)
276 tcsetattr(0, TCSANOW, &termios);
277 exit(status);
278}
279
280static void die(const char *str)
281{
282 bb_error_msg("%s", str);
283 leave(8);
284}
285
286/*
287 * This simply goes through the file-name data and prints out the
288 * current file.
289 */
290static void print_current_name(void)
291{
292 int i = 0;
293
294 while (i < name_depth)
295 printf("/%.*s", namelen, name_list[i++]);
296 if (i == 0)
297 printf("/");
298}
299
300static int ask(const char *string, int def)
301{
302 int c;
303
304 if (!repair) {
305 printf("\n");
306 errors_uncorrected = 1;
307 return 0;
308 }
309 if (automatic) {
310 printf("\n");
311 if (!def)
312 errors_uncorrected = 1;
313 return def;
314 }
315 printf(def ? "%s (y/n)? " : "%s (n/y)? ", string);
316 for (;;) {
317 fflush(stdout);
318 if ((c = getchar()) == EOF) {
319 if (!def)
320 errors_uncorrected = 1;
321 return def;
322 }
323 c = toupper(c);
324 if (c == 'Y') {
325 def = 1;
326 break;
327 } else if (c == 'N') {
328 def = 0;
329 break;
330 } else if (c == ' ' || c == '\n')
331 break;
332 }
333 if (def)
334 printf("y\n");
335 else {
336 printf("n\n");
337 errors_uncorrected = 1;
338 }
339 return def;
340}
341
342/*
343 * Make certain that we aren't checking a filesystem that is on a
344 * mounted partition. Code adapted from e2fsck, Copyright (C) 1993,
345 * 1994 Theodore Ts'o. Also licensed under GPL.
346 */
347static void check_mount(void)
348{
349 FILE *f;
350 struct mntent *mnt;
351 int cont;
352 int fd;
353
354 if ((f = setmntent(MOUNTED, "r")) == NULL)
355 return;
356 while ((mnt = getmntent(f)) != NULL)
357 if (strcmp(device_name, mnt->mnt_fsname) == 0)
358 break;
359 endmntent(f);
360 if (!mnt)
361 return;
362
363 /*
364 * If the root is mounted read-only, then /etc/mtab is
365 * probably not correct; so we won't issue a warning based on
366 * it.
367 */
368 fd = open(MOUNTED, O_RDWR);
369 if (fd < 0 && errno == EROFS)
370 return;
371 else
372 close(fd);
373
374 printf("%s is mounted. ", device_name);
375 if (isatty(0) && isatty(1))
376 cont = ask("Do you really want to continue", 0);
377 else
378 cont = 0;
379 if (!cont) {
380 printf("check aborted.\n");
381 exit(0);
382 }
383 return;
384}
385
386/*
387 * check_zone_nr checks to see that *nr is a valid zone nr. If it
388 * isn't, it will possibly be repaired. Check_zone_nr sets *corrected
389 * if an error was corrected, and returns the zone (0 for no zone
390 * or a bad zone-number).
391 */
392static int check_zone_nr(unsigned short *nr, int *corrected)
393{
394 if (!*nr)
395 return 0;
396 if (*nr < FIRSTZONE)
397 printf("Zone nr < FIRSTZONE in file `");
398 else if (*nr >= ZONES)
399 printf("Zone nr >= ZONES in file `");
400 else
401 return *nr;
402 print_current_name();
403 printf("'.");
404 if (ask("Remove block", 1)) {
405 *nr = 0;
406 *corrected = 1;
407 }
408 return 0;
409}
410
411#ifdef CONFIG_FEATURE_MINIX2
412static int check_zone_nr2(unsigned int *nr, int *corrected)
413{
414 if (!*nr)
415 return 0;
416 if (*nr < FIRSTZONE)
417 printf("Zone nr < FIRSTZONE in file `");
418 else if (*nr >= ZONES)
419 printf("Zone nr >= ZONES in file `");
420 else
421 return *nr;
422 print_current_name();
423 printf("'.");
424 if (ask("Remove block", 1)) {
425 *nr = 0;
426 *corrected = 1;
427 }
428 return 0;
429}
430#endif
431
432/*
433 * read-block reads block nr into the buffer at addr.
434 */
435static void read_block(unsigned int nr, char *addr)
436{
437 if (!nr) {
438 memset(addr, 0, BLOCK_SIZE);
439 return;
440 }
441 if (BLOCK_SIZE * nr != lseek(IN, BLOCK_SIZE * nr, SEEK_SET)) {
442 printf("Read error: unable to seek to block in file '");
443 print_current_name();
444 printf("'\n");
445 memset(addr, 0, BLOCK_SIZE);
446 errors_uncorrected = 1;
447 } else if (BLOCK_SIZE != read(IN, addr, BLOCK_SIZE)) {
448 printf("Read error: bad block in file '");
449 print_current_name();
450 printf("'\n");
451 memset(addr, 0, BLOCK_SIZE);
452 errors_uncorrected = 1;
453 }
454}
455
456/*
457 * write_block writes block nr to disk.
458 */
459static void write_block(unsigned int nr, char *addr)
460{
461 if (!nr)
462 return;
463 if (nr < FIRSTZONE || nr >= ZONES) {
464 printf("Internal error: trying to write bad block\n"
465 "Write request ignored\n");
466 errors_uncorrected = 1;
467 return;
468 }
469 if (BLOCK_SIZE * nr != lseek(IN, BLOCK_SIZE * nr, SEEK_SET))
470 die("seek failed in write_block");
471 if (BLOCK_SIZE != write(IN, addr, BLOCK_SIZE)) {
472 printf("Write error: bad block in file '");
473 print_current_name();
474 printf("'\n");
475 errors_uncorrected = 1;
476 }
477}
478
479/*
480 * map-block calculates the absolute block nr of a block in a file.
481 * It sets 'changed' if the inode has needed changing, and re-writes
482 * any indirect blocks with errors.
483 */
484static int map_block(struct minix_inode *inode, unsigned int blknr)
485{
486 unsigned short ind[BLOCK_SIZE >> 1];
487 unsigned short dind[BLOCK_SIZE >> 1];
488 int blk_chg, block, result;
489
490 if (blknr < 7)
491 return check_zone_nr(inode->i_zone + blknr, &changed);
492 blknr -= 7;
493 if (blknr < 512) {
494 block = check_zone_nr(inode->i_zone + 7, &changed);
495 read_block(block, (char *) ind);
496 blk_chg = 0;
497 result = check_zone_nr(blknr + ind, &blk_chg);
498 if (blk_chg)
499 write_block(block, (char *) ind);
500 return result;
501 }
502 blknr -= 512;
503 block = check_zone_nr(inode->i_zone + 8, &changed);
504 read_block(block, (char *) dind);
505 blk_chg = 0;
506 result = check_zone_nr(dind + (blknr / 512), &blk_chg);
507 if (blk_chg)
508 write_block(block, (char *) dind);
509 block = result;
510 read_block(block, (char *) ind);
511 blk_chg = 0;
512 result = check_zone_nr(ind + (blknr % 512), &blk_chg);
513 if (blk_chg)
514 write_block(block, (char *) ind);
515 return result;
516}
517
518#ifdef CONFIG_FEATURE_MINIX2
519static int map_block2(struct minix2_inode *inode, unsigned int blknr)
520{
521 unsigned int ind[BLOCK_SIZE >> 2];
522 unsigned int dind[BLOCK_SIZE >> 2];
523 unsigned int tind[BLOCK_SIZE >> 2];
524 int blk_chg, block, result;
525
526 if (blknr < 7)
527 return check_zone_nr2(inode->i_zone + blknr, &changed);
528 blknr -= 7;
529 if (blknr < 256) {
530 block = check_zone_nr2(inode->i_zone + 7, &changed);
531 read_block(block, (char *) ind);
532 blk_chg = 0;
533 result = check_zone_nr2(blknr + ind, &blk_chg);
534 if (blk_chg)
535 write_block(block, (char *) ind);
536 return result;
537 }
538 blknr -= 256;
539 if (blknr >= 256 * 256) {
540 block = check_zone_nr2(inode->i_zone + 8, &changed);
541 read_block(block, (char *) dind);
542 blk_chg = 0;
543 result = check_zone_nr2(dind + blknr / 256, &blk_chg);
544 if (blk_chg)
545 write_block(block, (char *) dind);
546 block = result;
547 read_block(block, (char *) ind);
548 blk_chg = 0;
549 result = check_zone_nr2(ind + blknr % 256, &blk_chg);
550 if (blk_chg)
551 write_block(block, (char *) ind);
552 return result;
553 }
554 blknr -= 256 * 256;
555 block = check_zone_nr2(inode->i_zone + 9, &changed);
556 read_block(block, (char *) tind);
557 blk_chg = 0;
558 result = check_zone_nr2(tind + blknr / (256 * 256), &blk_chg);
559 if (blk_chg)
560 write_block(block, (char *) tind);
561 block = result;
562 read_block(block, (char *) dind);
563 blk_chg = 0;
564 result = check_zone_nr2(dind + (blknr / 256) % 256, &blk_chg);
565 if (blk_chg)
566 write_block(block, (char *) dind);
567 block = result;
568 read_block(block, (char *) ind);
569 blk_chg = 0;
570 result = check_zone_nr2(ind + blknr % 256, &blk_chg);
571 if (blk_chg)
572 write_block(block, (char *) ind);
573 return result;
574}
575#endif
576
577static void write_super_block(void)
578{
579 /*
580 * Set the state of the filesystem based on whether or not there
581 * are uncorrected errors. The filesystem valid flag is
582 * unconditionally set if we get this far.
583 */
584 Super.s_state |= MINIX_VALID_FS;
585 if (errors_uncorrected)
586 Super.s_state |= MINIX_ERROR_FS;
587 else
588 Super.s_state &= ~MINIX_ERROR_FS;
589
590 if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET))
591 die("seek failed in write_super_block");
592 if (BLOCK_SIZE != write(IN, super_block_buffer, BLOCK_SIZE))
593 die("unable to write super-block");
594
595 return;
596}
597
598static void write_tables(void)
599{
600 write_super_block();
601
602 if (IMAPS * BLOCK_SIZE != write(IN, inode_map, IMAPS * BLOCK_SIZE))
603 die("Unable to write inode map");
604 if (ZMAPS * BLOCK_SIZE != write(IN, zone_map, ZMAPS * BLOCK_SIZE))
605 die("Unable to write zone map");
606 if (INODE_BUFFER_SIZE != write(IN, inode_buffer, INODE_BUFFER_SIZE))
607 die("Unable to write inodes");
608}
609
610static void get_dirsize(void)
611{
612 int block;
613 char blk[BLOCK_SIZE];
614 int size;
615
616#ifdef CONFIG_FEATURE_MINIX2
617 if (version2)
618 block = Inode2[ROOT_INO].i_zone[0];
619 else
620#endif
621 block = Inode[ROOT_INO].i_zone[0];
622 read_block(block, blk);
623 for (size = 16; size < BLOCK_SIZE; size <<= 1) {
624 if (strcmp(blk + size + 2, "..") == 0) {
625 dirsize = size;
626 namelen = size - 2;
627 return;
628 }
629 }
630 /* use defaults */
631}
632
633static void read_superblock(void)
634{
635 if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET))
636 die("seek failed");
637 if (BLOCK_SIZE != read(IN, super_block_buffer, BLOCK_SIZE))
638 die("unable to read super block");
639 if (MAGIC == MINIX_SUPER_MAGIC) {
640 namelen = 14;
641 dirsize = 16;
642 version2 = 0;
643 } else if (MAGIC == MINIX_SUPER_MAGIC2) {
644 namelen = 30;
645 dirsize = 32;
646 version2 = 0;
647#ifdef CONFIG_FEATURE_MINIX2
648 } else if (MAGIC == MINIX2_SUPER_MAGIC) {
649 namelen = 14;
650 dirsize = 16;
651 version2 = 1;
652 } else if (MAGIC == MINIX2_SUPER_MAGIC2) {
653 namelen = 30;
654 dirsize = 32;
655 version2 = 1;
656#endif
657 } else
658 die("bad magic number in super-block");
659 if (ZONESIZE != 0 || BLOCK_SIZE != 1024)
660 die("Only 1k blocks/zones supported");
661 if (IMAPS * BLOCK_SIZE * 8 < INODES + 1)
662 die("bad s_imap_blocks field in super-block");
663 if (ZMAPS * BLOCK_SIZE * 8 < ZONES - FIRSTZONE + 1)
664 die("bad s_zmap_blocks field in super-block");
665}
666
667static void read_tables(void)
668{
669 inode_map = xmalloc(IMAPS * BLOCK_SIZE);
670 zone_map = xmalloc(ZMAPS * BLOCK_SIZE);
671 memset(inode_map, 0, sizeof(inode_map));
672 memset(zone_map, 0, sizeof(zone_map));
673 inode_buffer = xmalloc(INODE_BUFFER_SIZE);
674 inode_count = xmalloc(INODES + 1);
675 zone_count = xmalloc(ZONES);
676 if (IMAPS * BLOCK_SIZE != read(IN, inode_map, IMAPS * BLOCK_SIZE))
677 die("Unable to read inode map");
678 if (ZMAPS * BLOCK_SIZE != read(IN, zone_map, ZMAPS * BLOCK_SIZE))
679 die("Unable to read zone map");
680 if (INODE_BUFFER_SIZE != read(IN, inode_buffer, INODE_BUFFER_SIZE))
681 die("Unable to read inodes");
682 if (NORM_FIRSTZONE != FIRSTZONE) {
683 printf("Warning: Firstzone != Norm_firstzone\n");
684 errors_uncorrected = 1;
685 }
686 get_dirsize();
687 if (show) {
688 printf("%ld inodes\n", INODES);
689 printf("%ld blocks\n", ZONES);
690 printf("Firstdatazone=%ld (%ld)\n", FIRSTZONE, NORM_FIRSTZONE);
691 printf("Zonesize=%d\n", BLOCK_SIZE << ZONESIZE);
692 printf("Maxsize=%ld\n", MAXSIZE);
693 printf("Filesystem state=%d\n", Super.s_state);
694 printf("namelen=%d\n\n", namelen);
695 }
696}
697
698static struct minix_inode *get_inode(unsigned int nr)
699{
700 struct minix_inode *inode;
701
702 if (!nr || nr > INODES)
703 return NULL;
704 total++;
705 inode = Inode + nr;
706 if (!inode_count[nr]) {
707 if (!inode_in_use(nr)) {
708 printf("Inode %d marked not used, but used for file '", nr);
709 print_current_name();
710 printf("'\n");
711 if (repair) {
712 if (ask("Mark in use", 1))
713 mark_inode(nr);
714 } else {
715 errors_uncorrected = 1;
716 }
717 }
718 if (S_ISDIR(inode->i_mode))
719 directory++;
720 else if (S_ISREG(inode->i_mode))
721 regular++;
722 else if (S_ISCHR(inode->i_mode))
723 chardev++;
724 else if (S_ISBLK(inode->i_mode))
725 blockdev++;
726 else if (S_ISLNK(inode->i_mode))
727 symlinks++;
728 else if (S_ISSOCK(inode->i_mode));
729 else if (S_ISFIFO(inode->i_mode));
730 else {
731 print_current_name();
732 printf(" has mode %05o\n", inode->i_mode);
733 }
734
735 } else
736 links++;
737 if (!++inode_count[nr]) {
738 printf("Warning: inode count too big.\n");
739 inode_count[nr]--;
740 errors_uncorrected = 1;
741 }
742 return inode;
743}
744
745#ifdef CONFIG_FEATURE_MINIX2
746static struct minix2_inode *get_inode2(unsigned int nr)
747{
748 struct minix2_inode *inode;
749
750 if (!nr || nr > INODES)
751 return NULL;
752 total++;
753 inode = Inode2 + nr;
754 if (!inode_count[nr]) {
755 if (!inode_in_use(nr)) {
756 printf("Inode %d marked not used, but used for file '", nr);
757 print_current_name();
758 printf("'\n");
759 if (repair) {
760 if (ask("Mark in use", 1))
761 mark_inode(nr);
762 else
763 errors_uncorrected = 1;
764 }
765 }
766 if (S_ISDIR(inode->i_mode))
767 directory++;
768 else if (S_ISREG(inode->i_mode))
769 regular++;
770 else if (S_ISCHR(inode->i_mode))
771 chardev++;
772 else if (S_ISBLK(inode->i_mode))
773 blockdev++;
774 else if (S_ISLNK(inode->i_mode))
775 symlinks++;
776 else if (S_ISSOCK(inode->i_mode));
777 else if (S_ISFIFO(inode->i_mode));
778 else {
779 print_current_name();
780 printf(" has mode %05o\n", inode->i_mode);
781 }
782 } else
783 links++;
784 if (!++inode_count[nr]) {
785 printf("Warning: inode count too big.\n");
786 inode_count[nr]--;
787 errors_uncorrected = 1;
788 }
789 return inode;
790}
791#endif
792
793static void check_root(void)
794{
795 struct minix_inode *inode = Inode + ROOT_INO;
796
797 if (!inode || !S_ISDIR(inode->i_mode))
798 die("root inode isn't a directory");
799}
800
801#ifdef CONFIG_FEATURE_MINIX2
802static void check_root2(void)
803{
804 struct minix2_inode *inode = Inode2 + ROOT_INO;
805
806 if (!inode || !S_ISDIR(inode->i_mode))
807 die("root inode isn't a directory");
808}
809#endif
810
811static int add_zone(unsigned short *znr, int *corrected)
812{
813 int result;
814 int block;
815
816 result = 0;
817 block = check_zone_nr(znr, corrected);
818 if (!block)
819 return 0;
820 if (zone_count[block]) {
821 printf("Block has been used before. Now in file `");
822 print_current_name();
823 printf("'.");
824 if (ask("Clear", 1)) {
825 *znr = 0;
826 block = 0;
827 *corrected = 1;
828 }
829 }
830 if (!block)
831 return 0;
832 if (!zone_in_use(block)) {
833 printf("Block %d in file `", block);
834 print_current_name();
835 printf("' is marked not in use.");
836 if (ask("Correct", 1))
837 mark_zone(block);
838 }
839 if (!++zone_count[block])
840 zone_count[block]--;
841 return block;
842}
843
844#ifdef CONFIG_FEATURE_MINIX2
845static int add_zone2(unsigned int *znr, int *corrected)
846{
847 int result;
848 int block;
849
850 result = 0;
851 block = check_zone_nr2(znr, corrected);
852 if (!block)
853 return 0;
854 if (zone_count[block]) {
855 printf("Block has been used before. Now in file `");
856 print_current_name();
857 printf("'.");
858 if (ask("Clear", 1)) {
859 *znr = 0;
860 block = 0;
861 *corrected = 1;
862 }
863 }
864 if (!block)
865 return 0;
866 if (!zone_in_use(block)) {
867 printf("Block %d in file `", block);
868 print_current_name();
869 printf("' is marked not in use.");
870 if (ask("Correct", 1))
871 mark_zone(block);
872 }
873 if (!++zone_count[block])
874 zone_count[block]--;
875 return block;
876}
877#endif
878
879static void add_zone_ind(unsigned short *znr, int *corrected)
880{
881 static char blk[BLOCK_SIZE];
882 int i, chg_blk = 0;
883 int block;
884
885 block = add_zone(znr, corrected);
886 if (!block)
887 return;
888 read_block(block, blk);
889 for (i = 0; i < (BLOCK_SIZE >> 1); i++)
890 add_zone(i + (unsigned short *) blk, &chg_blk);
891 if (chg_blk)
892 write_block(block, blk);
893}
894
895#ifdef CONFIG_FEATURE_MINIX2
896static void add_zone_ind2(unsigned int *znr, int *corrected)
897{
898 static char blk[BLOCK_SIZE];
899 int i, chg_blk = 0;
900 int block;
901
902 block = add_zone2(znr, corrected);
903 if (!block)
904 return;
905 read_block(block, blk);
906 for (i = 0; i < BLOCK_SIZE >> 2; i++)
907 add_zone2(i + (unsigned int *) blk, &chg_blk);
908 if (chg_blk)
909 write_block(block, blk);
910}
911#endif
912
913static void add_zone_dind(unsigned short *znr, int *corrected)
914{
915 static char blk[BLOCK_SIZE];
916 int i, blk_chg = 0;
917 int block;
918
919 block = add_zone(znr, corrected);
920 if (!block)
921 return;
922 read_block(block, blk);
923 for (i = 0; i < (BLOCK_SIZE >> 1); i++)
924 add_zone_ind(i + (unsigned short *) blk, &blk_chg);
925 if (blk_chg)
926 write_block(block, blk);
927}
928
929#ifdef CONFIG_FEATURE_MINIX2
930static void add_zone_dind2(unsigned int *znr, int *corrected)
931{
932 static char blk[BLOCK_SIZE];
933 int i, blk_chg = 0;
934 int block;
935
936 block = add_zone2(znr, corrected);
937 if (!block)
938 return;
939 read_block(block, blk);
940 for (i = 0; i < BLOCK_SIZE >> 2; i++)
941 add_zone_ind2(i + (unsigned int *) blk, &blk_chg);
942 if (blk_chg)
943 write_block(block, blk);
944}
945
946static void add_zone_tind2(unsigned int *znr, int *corrected)
947{
948 static char blk[BLOCK_SIZE];
949 int i, blk_chg = 0;
950 int block;
951
952 block = add_zone2(znr, corrected);
953 if (!block)
954 return;
955 read_block(block, blk);
956 for (i = 0; i < BLOCK_SIZE >> 2; i++)
957 add_zone_dind2(i + (unsigned int *) blk, &blk_chg);
958 if (blk_chg)
959 write_block(block, blk);
960}
961#endif
962
963static void check_zones(unsigned int i)
964{
965 struct minix_inode *inode;
966
967 if (!i || i > INODES)
968 return;
969 if (inode_count[i] > 1) /* have we counted this file already? */
970 return;
971 inode = Inode + i;
972 if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) &&
973 !S_ISLNK(inode->i_mode)) return;
974 for (i = 0; i < 7; i++)
975 add_zone(i + inode->i_zone, &changed);
976 add_zone_ind(7 + inode->i_zone, &changed);
977 add_zone_dind(8 + inode->i_zone, &changed);
978}
979
980#ifdef CONFIG_FEATURE_MINIX2
981static void check_zones2(unsigned int i)
982{
983 struct minix2_inode *inode;
984
985 if (!i || i > INODES)
986 return;
987 if (inode_count[i] > 1) /* have we counted this file already? */
988 return;
989 inode = Inode2 + i;
990 if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)
991 && !S_ISLNK(inode->i_mode))
992 return;
993 for (i = 0; i < 7; i++)
994 add_zone2(i + inode->i_zone, &changed);
995 add_zone_ind2(7 + inode->i_zone, &changed);
996 add_zone_dind2(8 + inode->i_zone, &changed);
997 add_zone_tind2(9 + inode->i_zone, &changed);
998}
999#endif
1000
1001static void check_file(struct minix_inode *dir, unsigned int offset)
1002{
1003 static char blk[BLOCK_SIZE];
1004 struct minix_inode *inode;
1005 int ino;
1006 char *name;
1007 int block;
1008
1009 block = map_block(dir, offset / BLOCK_SIZE);
1010 read_block(block, blk);
1011 name = blk + (offset % BLOCK_SIZE) + 2;
1012 ino = *(unsigned short *) (name - 2);
1013 if (ino > INODES) {
1014 print_current_name();
1015 printf(" contains a bad inode number for file '");
1016 printf("%.*s'.", namelen, name);
1017 if (ask(" Remove", 1)) {
1018 *(unsigned short *) (name - 2) = 0;
1019 write_block(block, blk);
1020 }
1021 ino = 0;
1022 }
1023 if (name_depth < MAX_DEPTH)
1024 strncpy(name_list[name_depth], name, namelen);
1025 name_depth++;
1026 inode = get_inode(ino);
1027 name_depth--;
1028 if (!offset) {
1029 if (!inode || strcmp(".", name)) {
1030 print_current_name();
1031 printf(": bad directory: '.' isn't first\n");
1032 errors_uncorrected = 1;
1033 } else
1034 return;
1035 }
1036 if (offset == dirsize) {
1037 if (!inode || strcmp("..", name)) {
1038 print_current_name();
1039 printf(": bad directory: '..' isn't second\n");
1040 errors_uncorrected = 1;
1041 } else
1042 return;
1043 }
1044 if (!inode)
1045 return;
1046 if (name_depth < MAX_DEPTH)
1047 strncpy(name_list[name_depth], name, namelen);
1048 name_depth++;
1049 if (list) {
1050 if (verbose)
1051 printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks);
1052 print_current_name();
1053 if (S_ISDIR(inode->i_mode))
1054 printf(":\n");
1055 else
1056 printf("\n");
1057 }
1058 check_zones(ino);
1059 if (inode && S_ISDIR(inode->i_mode))
1060 recursive_check(ino);
1061 name_depth--;
1062 return;
1063}
1064
1065#ifdef CONFIG_FEATURE_MINIX2
1066static void check_file2(struct minix2_inode *dir, unsigned int offset)
1067{
1068 static char blk[BLOCK_SIZE];
1069 struct minix2_inode *inode;
1070 int ino;
1071 char *name;
1072 int block;
1073
1074 block = map_block2(dir, offset / BLOCK_SIZE);
1075 read_block(block, blk);
1076 name = blk + (offset % BLOCK_SIZE) + 2;
1077 ino = *(unsigned short *) (name - 2);
1078 if (ino > INODES) {
1079 print_current_name();
1080 printf(" contains a bad inode number for file '");
1081 printf("%.*s'.", namelen, name);
1082 if (ask(" Remove", 1)) {
1083 *(unsigned short *) (name - 2) = 0;
1084 write_block(block, blk);
1085 }
1086 ino = 0;
1087 }
1088 if (name_depth < MAX_DEPTH)
1089 strncpy(name_list[name_depth], name, namelen);
1090 name_depth++;
1091 inode = get_inode2(ino);
1092 name_depth--;
1093 if (!offset) {
1094 if (!inode || strcmp(".", name)) {
1095 print_current_name();
1096 printf(": bad directory: '.' isn't first\n");
1097 errors_uncorrected = 1;
1098 } else
1099 return;
1100 }
1101 if (offset == dirsize) {
1102 if (!inode || strcmp("..", name)) {
1103 print_current_name();
1104 printf(": bad directory: '..' isn't second\n");
1105 errors_uncorrected = 1;
1106 } else
1107 return;
1108 }
1109 if (!inode)
1110 return;
1111 name_depth++;
1112 if (list) {
1113 if (verbose)
1114 printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks);
1115 print_current_name();
1116 if (S_ISDIR(inode->i_mode))
1117 printf(":\n");
1118 else
1119 printf("\n");
1120 }
1121 check_zones2(ino);
1122 if (inode && S_ISDIR(inode->i_mode))
1123 recursive_check2(ino);
1124 name_depth--;
1125 return;
1126}
1127#endif
1128
1129static void recursive_check(unsigned int ino)
1130{
1131 struct minix_inode *dir;
1132 unsigned int offset;
1133
1134 dir = Inode + ino;
1135 if (!S_ISDIR(dir->i_mode))
1136 die("internal error");
1137 if (dir->i_size < 2 * dirsize) {
1138 print_current_name();
1139 printf(": bad directory: size<32");
1140 errors_uncorrected = 1;
1141 }
1142 for (offset = 0; offset < dir->i_size; offset += dirsize)
1143 check_file(dir, offset);
1144}
1145
1146#ifdef CONFIG_FEATURE_MINIX2
1147static void recursive_check2(unsigned int ino)
1148{
1149 struct minix2_inode *dir;
1150 unsigned int offset;
1151
1152 dir = Inode2 + ino;
1153 if (!S_ISDIR(dir->i_mode))
1154 die("internal error");
1155 if (dir->i_size < 2 * dirsize) {
1156 print_current_name();
1157 printf(": bad directory: size < 32");
1158 errors_uncorrected = 1;
1159 }
1160 for (offset = 0; offset < dir->i_size; offset += dirsize)
1161 check_file2(dir, offset);
1162}
1163#endif
1164
1165static int bad_zone(int i)
1166{
1167 char buffer[1024];
1168
1169 if (BLOCK_SIZE * i != lseek(IN, BLOCK_SIZE * i, SEEK_SET))
1170 die("seek failed in bad_zone");
1171 return (BLOCK_SIZE != read(IN, buffer, BLOCK_SIZE));
1172}
1173
1174static void check_counts(void)
1175{
1176 int i;
1177
1178 for (i = 1; i <= INODES; i++) {
1179 if (!inode_in_use(i) && Inode[i].i_mode && warn_mode) {
1180 printf("Inode %d mode not cleared.", i);
1181 if (ask("Clear", 1)) {
1182 Inode[i].i_mode = 0;
1183 changed = 1;
1184 }
1185 }
1186 if (!inode_count[i]) {
1187 if (!inode_in_use(i))
1188 continue;
1189 printf("Inode %d not used, marked used in the bitmap.", i);
1190 if (ask("Clear", 1))
1191 unmark_inode(i);
1192 continue;
1193 }
1194 if (!inode_in_use(i)) {
1195 printf("Inode %d used, marked unused in the bitmap.", i);
1196 if (ask("Set", 1))
1197 mark_inode(i);
1198 }
1199 if (Inode[i].i_nlinks != inode_count[i]) {
1200 printf("Inode %d (mode = %07o), i_nlinks=%d, counted=%d.",
1201 i, Inode[i].i_mode, Inode[i].i_nlinks, inode_count[i]);
1202 if (ask("Set i_nlinks to count", 1)) {
1203 Inode[i].i_nlinks = inode_count[i];
1204 changed = 1;
1205 }
1206 }
1207 }
1208 for (i = FIRSTZONE; i < ZONES; i++) {
1209 if (zone_in_use(i) == zone_count[i])
1210 continue;
1211 if (!zone_count[i]) {
1212 if (bad_zone(i))
1213 continue;
1214 printf("Zone %d: marked in use, no file uses it.", i);
1215 if (ask("Unmark", 1))
1216 unmark_zone(i);
1217 continue;
1218 }
1219 printf("Zone %d: %sin use, counted=%d\n",
1220 i, zone_in_use(i) ? "" : "not ", zone_count[i]);
1221 }
1222}
1223
1224#ifdef CONFIG_FEATURE_MINIX2
1225static void check_counts2(void)
1226{
1227 int i;
1228
1229 for (i = 1; i <= INODES; i++) {
1230 if (!inode_in_use(i) && Inode2[i].i_mode && warn_mode) {
1231 printf("Inode %d mode not cleared.", i);
1232 if (ask("Clear", 1)) {
1233 Inode2[i].i_mode = 0;
1234 changed = 1;
1235 }
1236 }
1237 if (!inode_count[i]) {
1238 if (!inode_in_use(i))
1239 continue;
1240 printf("Inode %d not used, marked used in the bitmap.", i);
1241 if (ask("Clear", 1))
1242 unmark_inode(i);
1243 continue;
1244 }
1245 if (!inode_in_use(i)) {
1246 printf("Inode %d used, marked unused in the bitmap.", i);
1247 if (ask("Set", 1))
1248 mark_inode(i);
1249 }
1250 if (Inode2[i].i_nlinks != inode_count[i]) {
1251 printf("Inode %d (mode = %07o), i_nlinks=%d, counted=%d.",
1252 i, Inode2[i].i_mode, Inode2[i].i_nlinks,
1253 inode_count[i]);
1254 if (ask("Set i_nlinks to count", 1)) {
1255 Inode2[i].i_nlinks = inode_count[i];
1256 changed = 1;
1257 }
1258 }
1259 }
1260 for (i = FIRSTZONE; i < ZONES; i++) {
1261 if (zone_in_use(i) == zone_count[i])
1262 continue;
1263 if (!zone_count[i]) {
1264 if (bad_zone(i))
1265 continue;
1266 printf("Zone %d: marked in use, no file uses it.", i);
1267 if (ask("Unmark", 1))
1268 unmark_zone(i);
1269 continue;
1270 }
1271 printf("Zone %d: %sin use, counted=%d\n",
1272 i, zone_in_use(i) ? "" : "not ", zone_count[i]);
1273 }
1274}
1275#endif
1276
1277static void check(void)
1278{
1279 memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count));
1280 memset(zone_count, 0, ZONES * sizeof(*zone_count));
1281 check_zones(ROOT_INO);
1282 recursive_check(ROOT_INO);
1283 check_counts();
1284}
1285
1286#ifdef CONFIG_FEATURE_MINIX2
1287static void check2(void)
1288{
1289 memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count));
1290 memset(zone_count, 0, ZONES * sizeof(*zone_count));
1291 check_zones2(ROOT_INO);
1292 recursive_check2(ROOT_INO);
1293 check_counts2();
1294}
1295#endif
1296
1297/* Wed Feb 9 15:17:06 MST 2000 */
1298/* dynamically allocate name_list (instead of making it static) */
1299static void alloc_name_list(void)
1300{
1301 int i;
1302
1303 name_list = xmalloc(sizeof(char *) * MAX_DEPTH);
1304 for (i = 0; i < MAX_DEPTH; i++)
1305 name_list[i] = xmalloc(sizeof(char) * BUFSIZ + 1);
1306}
1307
1308#ifdef CONFIG_FEATURE_CLEAN_UP
1309/* execute this atexit() to deallocate name_list[] */
1310/* piptigger was here */
1311static void free_name_list(void)
1312{
1313 int i;
1314
1315 if (name_list) {
1316 for (i = 0; i < MAX_DEPTH; i++) {
1317 free(name_list[i]);
1318 }
1319 free(name_list);
1320 }
1321}
1322#endif
1323
1324extern int fsck_minix_main(int argc, char **argv)
1325{
1326 struct termios tmp;
1327 int count;
1328 int retcode = 0;
1329
1330 alloc_name_list();
1331#ifdef CONFIG_FEATURE_CLEAN_UP
1332 /* Don't bother to free memory. Exit does
1333 * that automagically, so we can save a few bytes */
1334 atexit(free_name_list);
1335#endif
1336
1337 if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE)
1338 die("bad inode size");
1339#ifdef CONFIG_FEATURE_MINIX2
1340 if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE)
1341 die("bad v2 inode size");
1342#endif
1343 while (argc-- > 1) {
1344 argv++;
1345 if (argv[0][0] != '-') {
1346 if (device_name)
1347 bb_show_usage();
1348 else
1349 device_name = argv[0];
1350 } else
1351 while (*++argv[0])
1352 switch (argv[0][0]) {
1353 case 'l':
1354 list = 1;
1355 break;
1356 case 'a':
1357 automatic = 1;
1358 repair = 1;
1359 break;
1360 case 'r':
1361 automatic = 0;
1362 repair = 1;
1363 break;
1364 case 'v':
1365 verbose = 1;
1366 break;
1367 case 's':
1368 show = 1;
1369 break;
1370 case 'm':
1371 warn_mode = 1;
1372 break;
1373 case 'f':
1374 force = 1;
1375 break;
1376 default:
1377 bb_show_usage();
1378 }
1379 }
1380 if (!device_name)
1381 bb_show_usage();
1382 check_mount(); /* trying to check a mounted filesystem? */
1383 if (repair && !automatic) {
1384 if (!isatty(0) || !isatty(1))
1385 die("need terminal for interactive repairs");
1386 }
1387 IN = open(device_name, repair ? O_RDWR : O_RDONLY);
1388 if (IN < 0){
1389 fprintf(stderr,"unable to open device '%s'.\n",device_name);
1390 leave(8);
1391 }
1392 for (count = 0; count < 3; count++)
1393 sync();
1394 read_superblock();
1395
1396 /*
1397 * Determine whether or not we should continue with the checking.
1398 * This is based on the status of the filesystem valid and error
1399 * flags and whether or not the -f switch was specified on the
1400 * command line.
1401 */
1402 printf("%s, %s\n", bb_applet_name, program_version);
1403 if (!(Super.s_state & MINIX_ERROR_FS) &&
1404 (Super.s_state & MINIX_VALID_FS) && !force) {
1405 if (repair)
1406 printf("%s is clean, no check.\n", device_name);
1407 return retcode;
1408 } else if (force)
1409 printf("Forcing filesystem check on %s.\n", device_name);
1410 else if (repair)
1411 printf("Filesystem on %s is dirty, needs checking.\n",
1412 device_name);
1413
1414 read_tables();
1415
1416 if (repair && !automatic) {
1417 tcgetattr(0, &termios);
1418 tmp = termios;
1419 tmp.c_lflag &= ~(ICANON | ECHO);
1420 tcsetattr(0, TCSANOW, &tmp);
1421 termios_set = 1;
1422 }
1423#ifdef CONFIG_FEATURE_MINIX2
1424 if (version2) {
1425 check_root2();
1426 check2();
1427 } else
1428#endif
1429 {
1430 check_root();
1431 check();
1432 }
1433 if (verbose) {
1434 int i, free_cnt;
1435
1436 for (i = 1, free_cnt = 0; i <= INODES; i++)
1437 if (!inode_in_use(i))
1438 free_cnt++;
1439 printf("\n%6ld inodes used (%ld%%)\n", (INODES - free_cnt),
1440 100 * (INODES - free_cnt) / INODES);
1441 for (i = FIRSTZONE, free_cnt = 0; i < ZONES; i++)
1442 if (!zone_in_use(i))
1443 free_cnt++;
1444 printf("%6ld zones used (%ld%%)\n", (ZONES - free_cnt),
1445 100 * (ZONES - free_cnt) / ZONES);
1446 printf("\n%6d regular files\n"
1447 "%6d directories\n"
1448 "%6d character device files\n"
1449 "%6d block device files\n"
1450 "%6d links\n"
1451 "%6d symbolic links\n"
1452 "------\n"
1453 "%6d files\n",
1454 regular, directory, chardev, blockdev,
1455 links - 2 * directory + 1, symlinks,
1456 total - 2 * directory + 1);
1457 }
1458 if (changed) {
1459 write_tables();
1460 printf("----------------------------\n"
1461 "FILE SYSTEM HAS BEEN CHANGED\n"
1462 "----------------------------\n");
1463 for (count = 0; count < 3; count++)
1464 sync();
1465 } else if (repair)
1466 write_super_block();
1467
1468 if (repair && !automatic)
1469 tcsetattr(0, TCSANOW, &termios);
1470
1471 if (changed)
1472 retcode += 3;
1473 if (errors_uncorrected)
1474 retcode += 4;
1475 return retcode;
1476}
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 @@
1/*
2 * getopt.c - Enhanced implementation of BSD getopt(1)
3 * Copyright (c) 1997, 1998, 1999, 2000 Frodo Looijaard <frodol@dds.nl>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20/*
21 * Version 1.0-b4: Tue Sep 23 1997. First public release.
22 * Version 1.0: Wed Nov 19 1997.
23 * Bumped up the version number to 1.0
24 * Fixed minor typo (CSH instead of TCSH)
25 * Version 1.0.1: Tue Jun 3 1998
26 * Fixed sizeof instead of strlen bug
27 * Bumped up the version number to 1.0.1
28 * Version 1.0.2: Thu Jun 11 1998 (not present)
29 * Fixed gcc-2.8.1 warnings
30 * Fixed --version/-V option (not present)
31 * Version 1.0.5: Tue Jun 22 1999
32 * Make -u option work (not present)
33 * Version 1.0.6: Tue Jun 27 2000
34 * No important changes
35 * Version 1.1.0: Tue Jun 30 2000
36 * Added NLS support (partly written by Arkadiusz Mi<B6>kiewicz
37 * <misiek@misiek.eu.org>)
38 * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org>
39 * Removed --version/-V and --help/-h in
40 * Removed parse_error(), using bb_error_msg() from Busybox instead
41 * Replaced our_malloc with xmalloc and our_realloc with xrealloc
42 *
43 */
44
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <unistd.h>
49#include <ctype.h>
50#include <getopt.h>
51
52#include "busybox.h"
53
54/* NON_OPT is the code that is returned when a non-option is found in '+'
55 mode */
56static const int NON_OPT = 1;
57/* LONG_OPT is the code that is returned when a long option is found. */
58static const int LONG_OPT = 2;
59
60/* The shells recognized. */
61typedef enum {BASH,TCSH} shell_t;
62
63
64/* Some global variables that tells us how to parse. */
65static shell_t shell=BASH; /* The shell we generate output for. */
66static int quiet_errors=0; /* 0 is not quiet. */
67static int quiet_output=0; /* 0 is not quiet. */
68static int quote=1; /* 1 is do quote. */
69static int alternative=0; /* 0 is getopt_long, 1 is getopt_long_only */
70
71/* Function prototypes */
72static const char *normalize(const char *arg);
73static int generate_output(char * argv[],int argc,const char *optstr,
74 const struct option *longopts);
75static void add_long_options(char *options);
76static void add_longopt(const char *name,int has_arg);
77static void set_shell(const char *new_shell);
78
79
80/*
81 * This function 'normalizes' a single argument: it puts single quotes around
82 * it and escapes other special characters. If quote is false, it just
83 * returns its argument.
84 * Bash only needs special treatment for single quotes; tcsh also recognizes
85 * exclamation marks within single quotes, and nukes whitespace.
86 * This function returns a pointer to a buffer that is overwritten by
87 * each call.
88 */
89const char *normalize(const char *arg)
90{
91 static char *BUFFER=NULL;
92 const char *argptr=arg;
93 char *bufptr;
94
95 free(BUFFER);
96
97 if (!quote) { /* Just copy arg */
98 BUFFER=bb_xstrdup(arg);
99 return BUFFER;
100 }
101
102 /* Each character in arg may take up to four characters in the result:
103 For a quote we need a closing quote, a backslash, a quote and an
104 opening quote! We need also the global opening and closing quote,
105 and one extra character for '\0'. */
106 BUFFER=xmalloc(strlen(arg)*4+3);
107
108 bufptr=BUFFER;
109 *bufptr++='\'';
110
111 while (*argptr) {
112 if (*argptr == '\'') {
113 /* Quote: replace it with: '\'' */
114 *bufptr++='\'';
115 *bufptr++='\\';
116 *bufptr++='\'';
117 *bufptr++='\'';
118 } else if (shell==TCSH && *argptr=='!') {
119 /* Exclamation mark: replace it with: \! */
120 *bufptr++='\'';
121 *bufptr++='\\';
122 *bufptr++='!';
123 *bufptr++='\'';
124 } else if (shell==TCSH && *argptr=='\n') {
125 /* Newline: replace it with: \n */
126 *bufptr++='\\';
127 *bufptr++='n';
128 } else if (shell==TCSH && isspace(*argptr)) {
129 /* Non-newline whitespace: replace it with \<ws> */
130 *bufptr++='\'';
131 *bufptr++='\\';
132 *bufptr++=*argptr;
133 *bufptr++='\'';
134 } else
135 /* Just copy */
136 *bufptr++=*argptr;
137 argptr++;
138 }
139 *bufptr++='\'';
140 *bufptr++='\0';
141 return BUFFER;
142}
143
144/*
145 * Generate the output. argv[0] is the program name (used for reporting errors).
146 * argv[1..] contains the options to be parsed. argc must be the number of
147 * elements in argv (ie. 1 if there are no options, only the program name),
148 * optstr must contain the short options, and longopts the long options.
149 * Other settings are found in global variables.
150 */
151int generate_output(char * argv[],int argc,const char *optstr,
152 const struct option *longopts)
153{
154 int exit_code = 0; /* We assume everything will be OK */
155 int opt;
156 int longindex;
157 const char *charptr;
158
159 if (quiet_errors) /* No error reporting from getopt(3) */
160 opterr=0;
161 optind=0; /* Reset getopt(3) */
162
163 while ((opt = (alternative?
164 getopt_long_only(argc,argv,optstr,longopts,&longindex):
165 getopt_long(argc,argv,optstr,longopts,&longindex)))
166 != EOF)
167 if (opt == '?' || opt == ':' )
168 exit_code = 1;
169 else if (!quiet_output) {
170 if (opt == LONG_OPT) {
171 printf(" --%s",longopts[longindex].name);
172 if (longopts[longindex].has_arg)
173 printf(" %s",
174 normalize(optarg?optarg:""));
175 } else if (opt == NON_OPT)
176 printf(" %s",normalize(optarg));
177 else {
178 printf(" -%c",opt);
179 charptr = strchr(optstr,opt);
180 if (charptr != NULL && *++charptr == ':')
181 printf(" %s",
182 normalize(optarg?optarg:""));
183 }
184 }
185
186 if (! quiet_output) {
187 printf(" --");
188 while (optind < argc)
189 printf(" %s",normalize(argv[optind++]));
190 printf("\n");
191 }
192 return exit_code;
193}
194
195static struct option *long_options=NULL;
196static int long_options_length=0; /* Length of array */
197static int long_options_nr=0; /* Nr of used elements in array */
198static const int LONG_OPTIONS_INCR = 10;
199#define init_longopt() add_longopt(NULL,0)
200
201/* Register a long option. The contents of name is copied. */
202void add_longopt(const char *name,int has_arg)
203{
204 if (!name) { /* init */
205 free(long_options);
206 long_options=NULL;
207 long_options_length=0;
208 long_options_nr=0;
209 }
210
211 if (long_options_nr == long_options_length) {
212 long_options_length += LONG_OPTIONS_INCR;
213 long_options=xrealloc(long_options,
214 sizeof(struct option) *
215 long_options_length);
216 }
217
218 long_options[long_options_nr].name=NULL;
219 long_options[long_options_nr].has_arg=0;
220 long_options[long_options_nr].flag=NULL;
221 long_options[long_options_nr].val=0;
222
223 if (long_options_nr) { /* Not for init! */
224 long_options[long_options_nr-1].has_arg=has_arg;
225 long_options[long_options_nr-1].flag=NULL;
226 long_options[long_options_nr-1].val=LONG_OPT;
227 long_options[long_options_nr-1].name=bb_xstrdup(name);
228 }
229 long_options_nr++;
230}
231
232
233/*
234 * Register several long options. options is a string of long options,
235 * separated by commas or whitespace.
236 * This nukes options!
237 */
238void add_long_options(char *options)
239{
240 int arg_opt, tlen;
241 char *tokptr=strtok(options,", \t\n");
242 while (tokptr) {
243 arg_opt=no_argument;
244 tlen=strlen(tokptr);
245 if (tlen > 0) {
246 if (tokptr[tlen-1] == ':') {
247 if (tlen > 1 && tokptr[tlen-2] == ':') {
248 tokptr[tlen-2]='\0';
249 tlen -= 2;
250 arg_opt=optional_argument;
251 } else {
252 tokptr[tlen-1]='\0';
253 tlen -= 1;
254 arg_opt=required_argument;
255 }
256 if (tlen == 0)
257 bb_error_msg("empty long option after -l or --long argument");
258 }
259 add_longopt(tokptr,arg_opt);
260 }
261 tokptr=strtok(NULL,", \t\n");
262 }
263}
264
265void set_shell(const char *new_shell)
266{
267 if (!strcmp(new_shell,"bash"))
268 shell=BASH;
269 else if (!strcmp(new_shell,"tcsh"))
270 shell=TCSH;
271 else if (!strcmp(new_shell,"sh"))
272 shell=BASH;
273 else if (!strcmp(new_shell,"csh"))
274 shell=TCSH;
275 else
276 bb_error_msg("unknown shell after -s or --shell argument");
277}
278
279
280/* Exit codes:
281 * 0) No errors, successful operation.
282 * 1) getopt(3) returned an error.
283 * 2) A problem with parameter parsing for getopt(1).
284 * 3) Internal error, out of memory
285 * 4) Returned for -T
286 */
287
288static struct option longopts[]=
289{
290 {"options",required_argument,NULL,'o'},
291 {"longoptions",required_argument,NULL,'l'},
292 {"quiet",no_argument,NULL,'q'},
293 {"quiet-output",no_argument,NULL,'Q'},
294 {"shell",required_argument,NULL,'s'},
295 {"test",no_argument,NULL,'T'},
296 {"unquoted",no_argument,NULL,'u'},
297 {"alternative",no_argument,NULL,'a'},
298 {"name",required_argument,NULL,'n'},
299 {NULL,0,NULL,0}
300};
301
302/* Stop scanning as soon as a non-option argument is found! */
303static const char *shortopts="+ao:l:n:qQs:Tu";
304
305
306int getopt_main(int argc, char *argv[])
307{
308 const char *optstr = NULL;
309 const char *name = NULL;
310 int opt;
311 int compatible=0;
312
313 init_longopt();
314
315 if (getenv("GETOPT_COMPATIBLE"))
316 compatible=1;
317
318 if (argc == 1) {
319 if (compatible) {
320 /* For some reason, the original getopt gave no error
321 when there were no arguments. */
322 printf(" --\n");
323 return 0;
324 } else
325 bb_error_msg_and_die("missing optstring argument");
326 }
327
328 if (argv[1][0] != '-' || compatible) {
329 quote=0;
330 optstr=xmalloc(strlen(argv[1])+1);
331 strcpy(optstr,argv[1]+strspn(argv[1],"-+"));
332 argv[1]=argv[0];
333 return (generate_output(argv+1,argc-1,optstr,long_options));
334 }
335
336 while ((opt=getopt_long(argc,argv,shortopts,longopts,NULL)) != EOF)
337 switch (opt) {
338 case 'a':
339 alternative=1;
340 break;
341 case 'o':
342 free(optstr);
343 optstr = optarg;
344 break;
345 case 'l':
346 add_long_options(optarg);
347 break;
348 case 'n':
349 free(name);
350 name = optarg;
351 break;
352 case 'q':
353 quiet_errors=1;
354 break;
355 case 'Q':
356 quiet_output=1;
357 break;
358 case 's':
359 set_shell(optarg);
360 break;
361 case 'T':
362 return 4;
363 case 'u':
364 quote=0;
365 break;
366 default:
367 bb_show_usage();
368 }
369
370 if (!optstr) {
371 if (optind >= argc)
372 bb_error_msg_and_die("missing optstring argument");
373 else {
374 optstr=bb_xstrdup(argv[optind]);
375 optind++;
376 }
377 }
378 if (name)
379 argv[optind-1]=name;
380 else
381 argv[optind-1]=argv[0];
382 return (generate_output(argv+optind-1,argc-optind+1,optstr,long_options));
383}
384
385/*
386 Local Variables:
387 c-file-style: "linux"
388 c-basic-offset: 4
389 tab-width: 4
390 End:
391*/
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 @@
1/*
2 * hexdump implementation for busybox
3 * Based on code from util-linux v 2.11l
4 *
5 * Copyright (c) 1989
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * Original copyright notice is retained at the end of this file.
23 */
24
25#include <getopt.h>
26#include <stdlib.h>
27#include <string.h>
28#include "busybox.h"
29#include "dump.h"
30
31static void bb_dump_addfile(char *name)
32{
33 register char *p;
34 FILE *fp;
35 char *buf;
36
37 fp = bb_xfopen(name, "r");
38
39 while ((buf = bb_get_chomped_line_from_file(fp)) != NULL) {
40 p = (char *) bb_skip_whitespace(buf);
41
42 if (*p && (*p != '#')) {
43 bb_dump_add(p);
44 }
45 free(buf);
46 }
47 fclose(fp);
48}
49
50static const char * const add_strings[] = {
51 "\"%07.7_ax \" 16/1 \"%03o \" \"\\n\"", /* b */
52 "\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\"", /* c */
53 "\"%07.7_ax \" 8/2 \" %05u \" \"\\n\"", /* d */
54 "\"%07.7_ax \" 8/2 \" %06o \" \"\\n\"", /* o */
55 "\"%07.7_ax \" 8/2 \" %04x \" \"\\n\"", /* x */
56};
57
58static const char add_first[] = "\"%07.7_Ax\n\"";
59
60static const char hexdump_opts[] = "bcdoxe:f:n:s:v";
61
62static const struct suffix_mult suffixes[] = {
63 {"b", 512 },
64 {"k", 1024 },
65 {"m", 1024*1024 },
66 {NULL, 0 }
67};
68
69int hexdump_main(int argc, char **argv)
70{
71// register FS *tfs;
72 const char *p;
73 int ch;
74
75 bb_dump_vflag = FIRST;
76 bb_dump_length = -1;
77
78 while ((ch = getopt(argc, argv, hexdump_opts)) > 0) {
79 if ((p = strchr(hexdump_opts, ch)) != NULL) {
80 if ((p - hexdump_opts) < 5) {
81 bb_dump_add(add_first);
82 bb_dump_add(add_strings[(int)(p - hexdump_opts)]);
83 } else {
84 /* Sae a little bit of space below by omitting the 'else's. */
85 if (ch == 'e') {
86 bb_dump_add(optarg);
87 } /* else */
88 if (ch == 'f') {
89 bb_dump_addfile(optarg);
90 } /* else */
91 if (ch == 'n') {
92 bb_dump_length = bb_xgetularg10_bnd(optarg, 0, INT_MAX);
93 } /* else */
94 if (ch == 's') {
95 bb_dump_skip = bb_xgetularg_bnd_sfx(optarg, 10, 0, LONG_MAX, suffixes);
96 } /* else */
97 if (ch == 'v') {
98 bb_dump_vflag = ALL;
99 }
100 }
101 } else {
102 bb_show_usage();
103 }
104 }
105
106 if (!bb_dump_fshead) {
107 bb_dump_add(add_first);
108 bb_dump_add("\"%07.7_ax \" 8/2 \"%04x \" \"\\n\"");
109 }
110
111 argv += optind;
112
113 return(bb_dump_dump(argv));
114}
115/*
116 * Copyright (c) 1989 The Regents of the University of California.
117 * All rights reserved.
118 *
119 * Redistribution and use in source and binary forms, with or without
120 * modification, are permitted provided that the following conditions
121 * are met:
122 * 1. Redistributions of source code must retain the above copyright
123 * notice, this list of conditions and the following disclaimer.
124 * 2. Redistributions in binary form must reproduce the above copyright
125 * notice, this list of conditions and the following disclaimer in the
126 * documentation and/or other materials provided with the distribution.
127 * 3. Neither the name of the University nor the names of its contributors
128 * may be used to endorse or promote products derived from this software
129 * without specific prior written permission.
130 *
131 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
132 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
133 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
134 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
135 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
136 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
137 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
138 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
139 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
140 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
141 * SUCH DAMAGE.
142 */
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini hwclock implementation for busybox
4 *
5 * Copyright (C) 2002 Robert Griebl <griebl@gmx.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21*/
22
23
24#include <sys/ioctl.h>
25#include <sys/time.h>
26#include <sys/utsname.h>
27#include <ctype.h>
28#include <fcntl.h>
29#include <getopt.h>
30#include <stdlib.h>
31#include <string.h>
32#include <syslog.h>
33#include <time.h>
34#include <unistd.h>
35#include "busybox.h"
36
37/* Copied from linux/rtc.h to eliminate the kernel dependency */
38struct linux_rtc_time {
39 int tm_sec;
40 int tm_min;
41 int tm_hour;
42 int tm_mday;
43 int tm_mon;
44 int tm_year;
45 int tm_wday;
46 int tm_yday;
47 int tm_isdst;
48};
49
50#define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time */
51#define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time */
52
53#ifdef CONFIG_FEATURE_HWCLOCK_LONGOPTIONS
54# ifndef _GNU_SOURCE
55# define _GNU_SOURCE
56# endif
57#endif
58
59static time_t read_rtc(int utc)
60{
61 int rtc;
62 struct tm tm;
63 char *oldtz = 0;
64 time_t t = 0;
65
66 if (( rtc = open ( "/dev/rtc", O_RDONLY )) < 0 ) {
67 if (( rtc = open ( "/dev/misc/rtc", O_RDONLY )) < 0 )
68 bb_perror_msg_and_die ( "Could not access RTC" );
69 }
70 memset ( &tm, 0, sizeof( struct tm ));
71 if ( ioctl ( rtc, RTC_RD_TIME, &tm ) < 0 )
72 bb_perror_msg_and_die ( "Could not read time from RTC" );
73 tm. tm_isdst = -1; // not known
74
75 close ( rtc );
76
77 if ( utc ) {
78 oldtz = getenv ( "TZ" );
79 setenv ( "TZ", "UTC 0", 1 );
80 tzset ( );
81 }
82
83 t = mktime ( &tm );
84
85 if ( utc ) {
86 if ( oldtz )
87 setenv ( "TZ", oldtz, 1 );
88 else
89 unsetenv ( "TZ" );
90 tzset ( );
91 }
92 return t;
93}
94
95static void write_rtc(time_t t, int utc)
96{
97 int rtc;
98 struct tm tm;
99
100 if (( rtc = open ( "/dev/rtc", O_WRONLY )) < 0 ) {
101 if (( rtc = open ( "/dev/misc/rtc", O_WRONLY )) < 0 )
102 bb_perror_msg_and_die ( "Could not access RTC" );
103 }
104
105 tm = *( utc ? gmtime ( &t ) : localtime ( &t ));
106 tm. tm_isdst = 0;
107
108 if ( ioctl ( rtc, RTC_SET_TIME, &tm ) < 0 )
109 bb_perror_msg_and_die ( "Could not set the RTC time" );
110
111 close ( rtc );
112}
113
114static int show_clock(int utc)
115{
116 struct tm *ptm;
117 time_t t;
118 char buffer [64];
119
120 t = read_rtc ( utc );
121 ptm = localtime ( &t ); /* Sets 'tzname[]' */
122
123 safe_strncpy ( buffer, ctime ( &t ), sizeof( buffer ));
124 if ( buffer [0] )
125 buffer [bb_strlen ( buffer ) - 1] = 0;
126
127 //printf ( "%s %.6f seconds %s\n", buffer, 0.0, utc ? "" : ( ptm-> tm_isdst ? tzname [1] : tzname [0] ));
128 printf ( "%s %.6f seconds\n", buffer, 0.0 );
129
130 return 0;
131}
132
133static int to_sys_clock(int utc)
134{
135 struct timeval tv = { 0, 0 };
136 const struct timezone tz = { timezone/60 - 60*daylight, 0 };
137
138 tv. tv_sec = read_rtc ( utc );
139
140 if ( settimeofday ( &tv, &tz ))
141 bb_perror_msg_and_die ( "settimeofday() failed" );
142
143 return 0;
144}
145
146static int from_sys_clock(int utc)
147{
148 struct timeval tv = { 0, 0 };
149 struct timezone tz = { 0, 0 };
150
151 if ( gettimeofday ( &tv, &tz ))
152 bb_perror_msg_and_die ( "gettimeofday() failed" );
153
154 write_rtc ( tv. tv_sec, utc );
155 return 0;
156}
157
158
159static int check_utc(void)
160{
161 int utc = 0;
162 FILE *f = fopen ( "/var/lib/hwclock/adjtime", "r" );
163
164 if ( f ) {
165 char buffer [128];
166
167 while ( fgets ( buffer, sizeof( buffer ), f )) {
168 int len = bb_strlen ( buffer );
169
170 while ( len && isspace ( buffer [len - 1] ))
171 len--;
172
173 buffer [len] = 0;
174
175 if ( strncmp ( buffer, "UTC", 3 ) == 0 ) {
176 utc = 1;
177 break;
178 }
179 }
180 fclose ( f );
181 }
182 return utc;
183}
184
185#define HWCLOCK_OPT_LOCALTIME 1
186#define HWCLOCK_OPT_UTC 2
187#define HWCLOCK_OPT_SHOW 4
188#define HWCLOCK_OPT_HCTOSYS 8
189#define HWCLOCK_OPT_SYSTOHC 16
190
191extern int hwclock_main ( int argc, char **argv )
192{
193 unsigned long opt;
194 int utc;
195
196#ifdef CONFIG_FEATURE_HWCLOCK_LONGOPTIONS
197static const struct option hwclock_long_options[] = {
198 { "localtime", 0, 0, 'l' },
199 { "utc", 0, 0, 'u' },
200 { "show", 0, 0, 'r' },
201 { "hctosys", 0, 0, 's' },
202 { "systohc", 0, 0, 'w' },
203 { 0, 0, 0, 0 }
204 };
205 bb_applet_long_options = hwclock_long_options;
206#endif
207
208 bb_opt_complementaly = "r~ws:w~rs:s~wr:l~u:u~l";
209 opt = bb_getopt_ulflags(argc, argv, "lursw");
210 /* Check only one mode was given */
211 if(opt & 0x80000000UL) {
212 bb_show_usage();
213 }
214
215 /* If -u or -l wasn't given check if we are using utc */
216 if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
217 utc = opt & HWCLOCK_OPT_UTC;
218 else
219 utc = check_utc();
220
221 if (opt & HWCLOCK_OPT_HCTOSYS) {
222 return to_sys_clock ( utc );
223 }
224 else if (opt & HWCLOCK_OPT_SYSTOHC) {
225 return from_sys_clock ( utc );
226 } else {
227 /* default HWCLOCK_OPT_SHOW */
228 return show_clock ( utc );
229 }
230}
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 @@
1/*
2 * Mini losetup implementation for busybox
3 *
4 * Copyright (C) 2002 Matt Kraai.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include <getopt.h>
23#include <stdlib.h>
24
25#include "busybox.h"
26
27int
28losetup_main (int argc, char **argv)
29{
30 int delete = 0;
31 int offset = 0;
32 int opt;
33
34 while ((opt = getopt (argc, argv, "do:")) != -1)
35 switch (opt)
36 {
37 case 'd':
38 delete = 1;
39 break;
40
41 case 'o':
42 offset = bb_xparse_number (optarg, NULL);
43 break;
44
45 default:
46 bb_show_usage();
47 }
48
49 if ((delete && (offset || optind + 1 != argc))
50 || (!delete && optind + 2 != argc))
51 bb_show_usage();
52
53 opt = 0;
54 if (delete)
55 return del_loop (argv[optind]) ? EXIT_SUCCESS : EXIT_FAILURE;
56 else
57 return set_loop (argv[optind], argv[optind + 1], offset, &opt)
58 ? EXIT_FAILURE : EXIT_SUCCESS;
59}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * mkfs.c - make a linux (minix) file-system.
4 *
5 * (C) 1991 Linus Torvalds. This file may be redistributed as per
6 * the Linux copyright.
7 */
8
9/*
10 * DD.MM.YY
11 *
12 * 24.11.91 - Time began. Used the fsck sources to get started.
13 *
14 * 25.11.91 - Corrected some bugs. Added support for ".badblocks"
15 * The algorithm for ".badblocks" is a bit weird, but
16 * it should work. Oh, well.
17 *
18 * 25.01.92 - Added the -l option for getting the list of bad blocks
19 * out of a named file. (Dave Rivers, rivers@ponds.uucp)
20 *
21 * 28.02.92 - Added %-information when using -c.
22 *
23 * 28.02.93 - Added support for other namelengths than the original
24 * 14 characters so that I can test the new kernel routines..
25 *
26 * 09.10.93 - Make exit status conform to that required by fsutil
27 * (Rik Faith, faith@cs.unc.edu)
28 *
29 * 31.10.93 - Added inode request feature, for backup floppies: use
30 * 32 inodes, for a news partition use more.
31 * (Scott Heavner, sdh@po.cwru.edu)
32 *
33 * 03.01.94 - Added support for file system valid flag.
34 * (Dr. Wettstein, greg%wind.uucp@plains.nodak.edu)
35 *
36 * 30.10.94 - added support for v2 filesystem
37 * (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
38 *
39 * 09.11.94 - Added test to prevent overwrite of mounted fs adapted
40 * from Theodore Ts'o's (tytso@athena.mit.edu) mke2fs
41 * program. (Daniel Quinlan, quinlan@yggdrasil.com)
42 *
43 * 03.20.95 - Clear first 512 bytes of filesystem to make certain that
44 * the filesystem is not misidentified as a MS-DOS FAT filesystem.
45 * (Daniel Quinlan, quinlan@yggdrasil.com)
46 *
47 * 02.07.96 - Added small patch from Russell King to make the program a
48 * good deal more portable (janl@math.uio.no)
49 *
50 * Usage: mkfs [-c | -l filename ] [-v] [-nXX] [-iXX] device [size-in-blocks]
51 *
52 * -c for readability checking (SLOW!)
53 * -l for getting a list of bad blocks from a file.
54 * -n for namelength (currently the kernel only uses 14 or 30)
55 * -i for number of inodes
56 * -v for v2 filesystem
57 *
58 * The device may be a block device or a image of one, but this isn't
59 * enforced (but it's not much fun on a character device :-).
60 *
61 * Modified for BusyBox by Erik Andersen <andersen@debian.org> --
62 * removed getopt based parser and added a hand rolled one.
63 */
64
65#include <stdio.h>
66#include <time.h>
67#include <unistd.h>
68#include <string.h>
69#include <signal.h>
70#include <fcntl.h>
71#include <ctype.h>
72#include <stdlib.h>
73#include <stdint.h>
74#include <termios.h>
75#include <sys/ioctl.h>
76#include <sys/param.h>
77#include <mntent.h>
78#include "busybox.h"
79
80#define MINIX_ROOT_INO 1
81#define MINIX_LINK_MAX 250
82#define MINIX2_LINK_MAX 65530
83
84#define MINIX_I_MAP_SLOTS 8
85#define MINIX_Z_MAP_SLOTS 64
86#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */
87#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */
88#define MINIX2_SUPER_MAGIC 0x2468 /* minix V2 fs */
89#define MINIX2_SUPER_MAGIC2 0x2478 /* minix V2 fs, 30 char names */
90#define MINIX_VALID_FS 0x0001 /* Clean fs. */
91#define MINIX_ERROR_FS 0x0002 /* fs has errors. */
92
93#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode)))
94#define MINIX2_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix2_inode)))
95
96#define MINIX_V1 0x0001 /* original minix fs */
97#define MINIX_V2 0x0002 /* minix V2 fs */
98
99#define INODE_VERSION(inode) inode->i_sb->u.minix_sb.s_version
100
101/*
102 * This is the original minix inode layout on disk.
103 * Note the 8-bit gid and atime and ctime.
104 */
105struct minix_inode {
106 uint16_t i_mode;
107 uint16_t i_uid;
108 uint32_t i_size;
109 uint32_t i_time;
110 uint8_t i_gid;
111 uint8_t i_nlinks;
112 uint16_t i_zone[9];
113};
114
115/*
116 * The new minix inode has all the time entries, as well as
117 * long block numbers and a third indirect block (7+1+1+1
118 * instead of 7+1+1). Also, some previously 8-bit values are
119 * now 16-bit. The inode is now 64 bytes instead of 32.
120 */
121struct minix2_inode {
122 uint16_t i_mode;
123 uint16_t i_nlinks;
124 uint16_t i_uid;
125 uint16_t i_gid;
126 uint32_t i_size;
127 uint32_t i_atime;
128 uint32_t i_mtime;
129 uint32_t i_ctime;
130 uint32_t i_zone[10];
131};
132
133/*
134 * minix super-block data on disk
135 */
136struct minix_super_block {
137 uint16_t s_ninodes;
138 uint16_t s_nzones;
139 uint16_t s_imap_blocks;
140 uint16_t s_zmap_blocks;
141 uint16_t s_firstdatazone;
142 uint16_t s_log_zone_size;
143 uint32_t s_max_size;
144 uint16_t s_magic;
145 uint16_t s_state;
146 uint32_t s_zones;
147};
148
149struct minix_dir_entry {
150 uint16_t inode;
151 char name[0];
152};
153
154#define BLOCK_SIZE_BITS 10
155#define BLOCK_SIZE (1<<BLOCK_SIZE_BITS)
156
157#define NAME_MAX 255 /* # chars in a file name */
158
159#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode)))
160
161#define MINIX_VALID_FS 0x0001 /* Clean fs. */
162#define MINIX_ERROR_FS 0x0002 /* fs has errors. */
163
164#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */
165#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */
166
167#ifndef BLKGETSIZE
168#define BLKGETSIZE _IO(0x12,96) /* return device size */
169#endif
170
171
172#ifndef __linux__
173#define volatile
174#endif
175
176#define MINIX_ROOT_INO 1
177#define MINIX_BAD_INO 2
178
179#define TEST_BUFFER_BLOCKS 16
180#define MAX_GOOD_BLOCKS 512
181
182#define UPPER(size,n) (((size)+((n)-1))/(n))
183#define INODE_SIZE (sizeof(struct minix_inode))
184#ifdef CONFIG_FEATURE_MINIX2
185#define INODE_SIZE2 (sizeof(struct minix2_inode))
186#define INODE_BLOCKS UPPER(INODES, (version2 ? MINIX2_INODES_PER_BLOCK \
187 : MINIX_INODES_PER_BLOCK))
188#else
189#define INODE_BLOCKS UPPER(INODES, (MINIX_INODES_PER_BLOCK))
190#endif
191#define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE)
192
193#define BITS_PER_BLOCK (BLOCK_SIZE<<3)
194
195static char *device_name = NULL;
196static int DEV = -1;
197static uint32_t BLOCKS = 0;
198static int check = 0;
199static int badblocks = 0;
200static int namelen = 30; /* default (changed to 30, per Linus's
201
202 suggestion, Sun Nov 21 08:05:07 1993) */
203static int dirsize = 32;
204static int magic = MINIX_SUPER_MAGIC2;
205static int version2 = 0;
206
207static char root_block[BLOCK_SIZE] = "\0";
208
209static char *inode_buffer = NULL;
210
211#define Inode (((struct minix_inode *) inode_buffer)-1)
212#ifdef CONFIG_FEATURE_MINIX2
213#define Inode2 (((struct minix2_inode *) inode_buffer)-1)
214#endif
215static char super_block_buffer[BLOCK_SIZE];
216static char boot_block_buffer[512];
217
218#define Super (*(struct minix_super_block *)super_block_buffer)
219#define INODES (Super.s_ninodes)
220#ifdef CONFIG_FEATURE_MINIX2
221#define ZONES (version2 ? Super.s_zones : Super.s_nzones)
222#else
223#define ZONES (Super.s_nzones)
224#endif
225#define IMAPS (Super.s_imap_blocks)
226#define ZMAPS (Super.s_zmap_blocks)
227#define FIRSTZONE (Super.s_firstdatazone)
228#define ZONESIZE (Super.s_log_zone_size)
229#define MAXSIZE (Super.s_max_size)
230#define MAGIC (Super.s_magic)
231#define NORM_FIRSTZONE (2+IMAPS+ZMAPS+INODE_BLOCKS)
232
233static char *inode_map;
234static char *zone_map;
235
236static unsigned short good_blocks_table[MAX_GOOD_BLOCKS];
237static int used_good_blocks = 0;
238static unsigned long req_nr_inodes = 0;
239
240static inline int bit(char * a,unsigned int i)
241{
242 return (a[i >> 3] & (1<<(i & 7))) != 0;
243}
244#define inode_in_use(x) (bit(inode_map,(x)))
245#define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1))
246
247#define mark_inode(x) (setbit(inode_map,(x)))
248#define unmark_inode(x) (clrbit(inode_map,(x)))
249
250#define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1))
251#define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1))
252
253/*
254 * Check to make certain that our new filesystem won't be created on
255 * an already mounted partition. Code adapted from mke2fs, Copyright
256 * (C) 1994 Theodore Ts'o. Also licensed under GPL.
257 */
258static inline void check_mount(void)
259{
260 FILE *f;
261 struct mntent *mnt;
262
263 if ((f = setmntent(MOUNTED, "r")) == NULL)
264 return;
265 while ((mnt = getmntent(f)) != NULL)
266 if (strcmp(device_name, mnt->mnt_fsname) == 0)
267 break;
268 endmntent(f);
269 if (!mnt)
270 return;
271
272 bb_error_msg_and_die("%s is mounted; will not make a filesystem here!", device_name);
273}
274
275static long valid_offset(int fd, int offset)
276{
277 char ch;
278
279 if (lseek(fd, offset, 0) < 0)
280 return 0;
281 if (read(fd, &ch, 1) < 1)
282 return 0;
283 return 1;
284}
285
286static inline int count_blocks(int fd)
287{
288 int high, low;
289
290 low = 0;
291 for (high = 1; valid_offset(fd, high); high *= 2)
292 low = high;
293 while (low < high - 1) {
294 const int mid = (low + high) / 2;
295
296 if (valid_offset(fd, mid))
297 low = mid;
298 else
299 high = mid;
300 }
301 valid_offset(fd, 0);
302 return (low + 1);
303}
304
305static inline int get_size(const char *file)
306{
307 int fd;
308 long size;
309
310 if ((fd = open(file, O_RDWR)) < 0)
311 bb_perror_msg_and_die("%s", file);
312 if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
313 close(fd);
314 return (size * 512);
315 }
316
317 size = count_blocks(fd);
318 close(fd);
319 return size;
320}
321
322static inline void write_tables(void)
323{
324 /* Mark the super block valid. */
325 Super.s_state |= MINIX_VALID_FS;
326 Super.s_state &= ~MINIX_ERROR_FS;
327
328 if (lseek(DEV, 0, SEEK_SET))
329 bb_error_msg_and_die("seek to boot block failed in write_tables");
330 if (512 != write(DEV, boot_block_buffer, 512))
331 bb_error_msg_and_die("unable to clear boot sector");
332 if (BLOCK_SIZE != lseek(DEV, BLOCK_SIZE, SEEK_SET))
333 bb_error_msg_and_die("seek failed in write_tables");
334 if (BLOCK_SIZE != write(DEV, super_block_buffer, BLOCK_SIZE))
335 bb_error_msg_and_die("unable to write super-block");
336 if (IMAPS * BLOCK_SIZE != write(DEV, inode_map, IMAPS * BLOCK_SIZE))
337 bb_error_msg_and_die("unable to write inode map");
338 if (ZMAPS * BLOCK_SIZE != write(DEV, zone_map, ZMAPS * BLOCK_SIZE))
339 bb_error_msg_and_die("unable to write zone map");
340 if (INODE_BUFFER_SIZE != write(DEV, inode_buffer, INODE_BUFFER_SIZE))
341 bb_error_msg_and_die("unable to write inodes");
342
343}
344
345static void write_block(int blk, char *buffer)
346{
347 if (blk * BLOCK_SIZE != lseek(DEV, blk * BLOCK_SIZE, SEEK_SET))
348 bb_error_msg_and_die("seek failed in write_block");
349 if (BLOCK_SIZE != write(DEV, buffer, BLOCK_SIZE))
350 bb_error_msg_and_die("write failed in write_block");
351}
352
353static int get_free_block(void)
354{
355 int blk;
356
357 if (used_good_blocks + 1 >= MAX_GOOD_BLOCKS)
358 bb_error_msg_and_die("too many bad blocks");
359 if (used_good_blocks)
360 blk = good_blocks_table[used_good_blocks - 1] + 1;
361 else
362 blk = FIRSTZONE;
363 while (blk < ZONES && zone_in_use(blk))
364 blk++;
365 if (blk >= ZONES)
366 bb_error_msg_and_die("not enough good blocks");
367 good_blocks_table[used_good_blocks] = blk;
368 used_good_blocks++;
369 return blk;
370}
371
372static inline void mark_good_blocks(void)
373{
374 int blk;
375
376 for (blk = 0; blk < used_good_blocks; blk++)
377 mark_zone(good_blocks_table[blk]);
378}
379
380static int next(int zone)
381{
382 if (!zone)
383 zone = FIRSTZONE - 1;
384 while (++zone < ZONES)
385 if (zone_in_use(zone))
386 return zone;
387 return 0;
388}
389
390static inline void make_bad_inode(void)
391{
392 struct minix_inode *inode = &Inode[MINIX_BAD_INO];
393 int i, j, zone;
394 int ind = 0, dind = 0;
395 unsigned short ind_block[BLOCK_SIZE >> 1];
396 unsigned short dind_block[BLOCK_SIZE >> 1];
397
398#define NEXT_BAD (zone = next(zone))
399
400 if (!badblocks)
401 return;
402 mark_inode(MINIX_BAD_INO);
403 inode->i_nlinks = 1;
404 inode->i_time = time(NULL);
405 inode->i_mode = S_IFREG + 0000;
406 inode->i_size = badblocks * BLOCK_SIZE;
407 zone = next(0);
408 for (i = 0; i < 7; i++) {
409 inode->i_zone[i] = zone;
410 if (!NEXT_BAD)
411 goto end_bad;
412 }
413 inode->i_zone[7] = ind = get_free_block();
414 memset(ind_block, 0, BLOCK_SIZE);
415 for (i = 0; i < 512; i++) {
416 ind_block[i] = zone;
417 if (!NEXT_BAD)
418 goto end_bad;
419 }
420 inode->i_zone[8] = dind = get_free_block();
421 memset(dind_block, 0, BLOCK_SIZE);
422 for (i = 0; i < 512; i++) {
423 write_block(ind, (char *) ind_block);
424 dind_block[i] = ind = get_free_block();
425 memset(ind_block, 0, BLOCK_SIZE);
426 for (j = 0; j < 512; j++) {
427 ind_block[j] = zone;
428 if (!NEXT_BAD)
429 goto end_bad;
430 }
431 }
432 bb_error_msg_and_die("too many bad blocks");
433 end_bad:
434 if (ind)
435 write_block(ind, (char *) ind_block);
436 if (dind)
437 write_block(dind, (char *) dind_block);
438}
439
440#ifdef CONFIG_FEATURE_MINIX2
441static inline void make_bad_inode2(void)
442{
443 struct minix2_inode *inode = &Inode2[MINIX_BAD_INO];
444 int i, j, zone;
445 int ind = 0, dind = 0;
446 unsigned long ind_block[BLOCK_SIZE >> 2];
447 unsigned long dind_block[BLOCK_SIZE >> 2];
448
449 if (!badblocks)
450 return;
451 mark_inode(MINIX_BAD_INO);
452 inode->i_nlinks = 1;
453 inode->i_atime = inode->i_mtime = inode->i_ctime = time(NULL);
454 inode->i_mode = S_IFREG + 0000;
455 inode->i_size = badblocks * BLOCK_SIZE;
456 zone = next(0);
457 for (i = 0; i < 7; i++) {
458 inode->i_zone[i] = zone;
459 if (!NEXT_BAD)
460 goto end_bad;
461 }
462 inode->i_zone[7] = ind = get_free_block();
463 memset(ind_block, 0, BLOCK_SIZE);
464 for (i = 0; i < 256; i++) {
465 ind_block[i] = zone;
466 if (!NEXT_BAD)
467 goto end_bad;
468 }
469 inode->i_zone[8] = dind = get_free_block();
470 memset(dind_block, 0, BLOCK_SIZE);
471 for (i = 0; i < 256; i++) {
472 write_block(ind, (char *) ind_block);
473 dind_block[i] = ind = get_free_block();
474 memset(ind_block, 0, BLOCK_SIZE);
475 for (j = 0; j < 256; j++) {
476 ind_block[j] = zone;
477 if (!NEXT_BAD)
478 goto end_bad;
479 }
480 }
481 /* Could make triple indirect block here */
482 bb_error_msg_and_die("too many bad blocks");
483 end_bad:
484 if (ind)
485 write_block(ind, (char *) ind_block);
486 if (dind)
487 write_block(dind, (char *) dind_block);
488}
489#endif
490
491static inline void make_root_inode(void)
492{
493 struct minix_inode *inode = &Inode[MINIX_ROOT_INO];
494
495 mark_inode(MINIX_ROOT_INO);
496 inode->i_zone[0] = get_free_block();
497 inode->i_nlinks = 2;
498 inode->i_time = time(NULL);
499 if (badblocks)
500 inode->i_size = 3 * dirsize;
501 else {
502 root_block[2 * dirsize] = '\0';
503 root_block[2 * dirsize + 1] = '\0';
504 inode->i_size = 2 * dirsize;
505 }
506 inode->i_mode = S_IFDIR + 0755;
507 inode->i_uid = getuid();
508 if (inode->i_uid)
509 inode->i_gid = getgid();
510 write_block(inode->i_zone[0], root_block);
511}
512
513#ifdef CONFIG_FEATURE_MINIX2
514static inline void make_root_inode2(void)
515{
516 struct minix2_inode *inode = &Inode2[MINIX_ROOT_INO];
517
518 mark_inode(MINIX_ROOT_INO);
519 inode->i_zone[0] = get_free_block();
520 inode->i_nlinks = 2;
521 inode->i_atime = inode->i_mtime = inode->i_ctime = time(NULL);
522 if (badblocks)
523 inode->i_size = 3 * dirsize;
524 else {
525 root_block[2 * dirsize] = '\0';
526 root_block[2 * dirsize + 1] = '\0';
527 inode->i_size = 2 * dirsize;
528 }
529 inode->i_mode = S_IFDIR + 0755;
530 inode->i_uid = getuid();
531 if (inode->i_uid)
532 inode->i_gid = getgid();
533 write_block(inode->i_zone[0], root_block);
534}
535#endif
536
537static inline void setup_tables(void)
538{
539 int i;
540 unsigned long inodes;
541
542 memset(super_block_buffer, 0, BLOCK_SIZE);
543 memset(boot_block_buffer, 0, 512);
544 MAGIC = magic;
545 ZONESIZE = 0;
546 MAXSIZE = version2 ? 0x7fffffff : (7 + 512 + 512 * 512) * 1024;
547#ifdef CONFIG_FEATURE_MINIX2
548 if (version2) {
549 Super.s_zones = BLOCKS;
550 } else
551#endif
552 Super.s_nzones = BLOCKS;
553
554/* some magic nrs: 1 inode / 3 blocks */
555 if (req_nr_inodes == 0)
556 inodes = BLOCKS / 3;
557 else
558 inodes = req_nr_inodes;
559 /* Round up inode count to fill block size */
560#ifdef CONFIG_FEATURE_MINIX2
561 if (version2)
562 inodes = ((inodes + MINIX2_INODES_PER_BLOCK - 1) &
563 ~(MINIX2_INODES_PER_BLOCK - 1));
564 else
565#endif
566 inodes = ((inodes + MINIX_INODES_PER_BLOCK - 1) &
567 ~(MINIX_INODES_PER_BLOCK - 1));
568 if (inodes > 65535)
569 inodes = 65535;
570 INODES = inodes;
571 IMAPS = UPPER(INODES + 1, BITS_PER_BLOCK);
572 ZMAPS = 0;
573 i = 0;
574 while (ZMAPS !=
575 UPPER(BLOCKS - (2 + IMAPS + ZMAPS + INODE_BLOCKS) + 1,
576 BITS_PER_BLOCK) && i < 1000) {
577 ZMAPS =
578 UPPER(BLOCKS - (2 + IMAPS + ZMAPS + INODE_BLOCKS) + 1,
579 BITS_PER_BLOCK);
580 i++;
581 }
582 /* Real bad hack but overwise mkfs.minix can be thrown
583 * in infinite loop...
584 * try:
585 * dd if=/dev/zero of=test.fs count=10 bs=1024
586 * /sbin/mkfs.minix -i 200 test.fs
587 * */
588 if (i >= 999) {
589 bb_error_msg_and_die("unable to allocate buffers for maps");
590 }
591 FIRSTZONE = NORM_FIRSTZONE;
592 inode_map = xmalloc(IMAPS * BLOCK_SIZE);
593 zone_map = xmalloc(ZMAPS * BLOCK_SIZE);
594 memset(inode_map, 0xff, IMAPS * BLOCK_SIZE);
595 memset(zone_map, 0xff, ZMAPS * BLOCK_SIZE);
596 for (i = FIRSTZONE; i < ZONES; i++)
597 unmark_zone(i);
598 for (i = MINIX_ROOT_INO; i <= INODES; i++)
599 unmark_inode(i);
600 inode_buffer = xmalloc(INODE_BUFFER_SIZE);
601 memset(inode_buffer, 0, INODE_BUFFER_SIZE);
602 printf("%ld inodes\n", (long)INODES);
603 printf("%ld blocks\n", (long)ZONES);
604 printf("Firstdatazone=%ld (%ld)\n", (long)FIRSTZONE, (long)NORM_FIRSTZONE);
605 printf("Zonesize=%d\n", BLOCK_SIZE << ZONESIZE);
606 printf("Maxsize=%ld\n\n", (long)MAXSIZE);
607}
608
609/*
610 * Perform a test of a block; return the number of
611 * blocks readable/writable.
612 */
613static inline long do_check(char *buffer, int try, unsigned int current_block)
614{
615 long got;
616
617 /* Seek to the correct loc. */
618 if (lseek(DEV, current_block * BLOCK_SIZE, SEEK_SET) !=
619 current_block * BLOCK_SIZE) {
620 bb_error_msg_and_die("seek failed during testing of blocks");
621 }
622
623
624 /* Try the read */
625 got = read(DEV, buffer, try * BLOCK_SIZE);
626 if (got < 0)
627 got = 0;
628 if (got & (BLOCK_SIZE - 1)) {
629 printf("Weird values in do_check: probably bugs\n");
630 }
631 got /= BLOCK_SIZE;
632 return got;
633}
634
635static unsigned int currently_testing = 0;
636
637static void alarm_intr(int alnum)
638{
639 if (currently_testing >= ZONES)
640 return;
641 signal(SIGALRM, alarm_intr);
642 alarm(5);
643 if (!currently_testing)
644 return;
645 printf("%d ...", currently_testing);
646 fflush(stdout);
647}
648
649static void check_blocks(void)
650{
651 int try, got;
652 static char buffer[BLOCK_SIZE * TEST_BUFFER_BLOCKS];
653
654 currently_testing = 0;
655 signal(SIGALRM, alarm_intr);
656 alarm(5);
657 while (currently_testing < ZONES) {
658 if (lseek(DEV, currently_testing * BLOCK_SIZE, SEEK_SET) !=
659 currently_testing * BLOCK_SIZE)
660 bb_error_msg_and_die("seek failed in check_blocks");
661 try = TEST_BUFFER_BLOCKS;
662 if (currently_testing + try > ZONES)
663 try = ZONES - currently_testing;
664 got = do_check(buffer, try, currently_testing);
665 currently_testing += got;
666 if (got == try)
667 continue;
668 if (currently_testing < FIRSTZONE)
669 bb_error_msg_and_die("bad blocks before data-area: cannot make fs");
670 mark_zone(currently_testing);
671 badblocks++;
672 currently_testing++;
673 }
674 if (badblocks > 1)
675 printf("%d bad blocks\n", badblocks);
676 else if (badblocks == 1)
677 printf("one bad block\n");
678}
679
680static void get_list_blocks(char *filename)
681{
682 FILE *listfile;
683 unsigned long blockno;
684
685 listfile = bb_xfopen(filename, "r");
686 while (!feof(listfile)) {
687 fscanf(listfile, "%ld\n", &blockno);
688 mark_zone(blockno);
689 badblocks++;
690 }
691 if (badblocks > 1)
692 printf("%d bad blocks\n", badblocks);
693 else if (badblocks == 1)
694 printf("one bad block\n");
695}
696
697extern int mkfs_minix_main(int argc, char **argv)
698{
699 int i=1;
700 char *tmp;
701 struct stat statbuf;
702 char *listfile = NULL;
703 int stopIt=FALSE;
704
705 if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE)
706 bb_error_msg_and_die("bad inode size");
707#ifdef CONFIG_FEATURE_MINIX2
708 if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE)
709 bb_error_msg_and_die("bad inode size");
710#endif
711
712 /* Parse options */
713 argv++;
714 while (--argc >= 0 && *argv && **argv) {
715 if (**argv == '-') {
716 stopIt=FALSE;
717 while (i > 0 && *++(*argv) && stopIt==FALSE) {
718 switch (**argv) {
719 case 'c':
720 check = 1;
721 break;
722 case 'i':
723 {
724 char *cp=NULL;
725 if (*(*argv+1) != 0) {
726 cp = ++(*argv);
727 } else {
728 if (--argc == 0) {
729 goto goodbye;
730 }
731 cp = *(++argv);
732 }
733 req_nr_inodes = strtoul(cp, &tmp, 0);
734 if (*tmp)
735 bb_show_usage();
736 stopIt=TRUE;
737 break;
738 }
739 case 'l':
740 if (--argc == 0) {
741 goto goodbye;
742 }
743 listfile = *(++argv);
744 break;
745 case 'n':
746 {
747 char *cp=NULL;
748
749 if (*(*argv+1) != 0) {
750 cp = ++(*argv);
751 } else {
752 if (--argc == 0) {
753 goto goodbye;
754 }
755 cp = *(++argv);
756 }
757 i = strtoul(cp, &tmp, 0);
758 if (*tmp)
759 bb_show_usage();
760 if (i == 14)
761 magic = MINIX_SUPER_MAGIC;
762 else if (i == 30)
763 magic = MINIX_SUPER_MAGIC2;
764 else
765 bb_show_usage();
766 namelen = i;
767 dirsize = i + 2;
768 stopIt=TRUE;
769 break;
770 }
771 case 'v':
772#ifdef CONFIG_FEATURE_MINIX2
773 version2 = 1;
774#else
775 bb_error_msg("%s: not compiled with minix v2 support",
776 device_name);
777 exit(-1);
778#endif
779 break;
780 case '-':
781 case 'h':
782 default:
783goodbye:
784 bb_show_usage();
785 }
786 }
787 } else {
788 if (device_name == NULL)
789 device_name = *argv;
790 else if (BLOCKS == 0)
791 BLOCKS = strtol(*argv, &tmp, 0);
792 else {
793 goto goodbye;
794 }
795 }
796 argv++;
797 }
798
799 if (device_name && !BLOCKS)
800 BLOCKS = get_size(device_name) / 1024;
801 if (!device_name || BLOCKS < 10) {
802 bb_show_usage();
803 }
804#ifdef CONFIG_FEATURE_MINIX2
805 if (version2) {
806 if (namelen == 14)
807 magic = MINIX2_SUPER_MAGIC;
808 else
809 magic = MINIX2_SUPER_MAGIC2;
810 } else
811#endif
812 if (BLOCKS > 65535)
813 BLOCKS = 65535;
814 check_mount(); /* is it already mounted? */
815 tmp = root_block;
816 *(short *) tmp = 1;
817 strcpy(tmp + 2, ".");
818 tmp += dirsize;
819 *(short *) tmp = 1;
820 strcpy(tmp + 2, "..");
821 tmp += dirsize;
822 *(short *) tmp = 2;
823 strcpy(tmp + 2, ".badblocks");
824 DEV = open(device_name, O_RDWR);
825 if (DEV < 0)
826 bb_error_msg_and_die("unable to open %s", device_name);
827 if (fstat(DEV, &statbuf) < 0)
828 bb_error_msg_and_die("unable to stat %s", device_name);
829 if (!S_ISBLK(statbuf.st_mode))
830 check = 0;
831 else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
832 bb_error_msg_and_die("will not try to make filesystem on '%s'", device_name);
833 setup_tables();
834 if (check)
835 check_blocks();
836 else if (listfile)
837 get_list_blocks(listfile);
838#ifdef CONFIG_FEATURE_MINIX2
839 if (version2) {
840 make_root_inode2();
841 make_bad_inode2();
842 } else
843#endif
844 {
845 make_root_inode();
846 make_bad_inode();
847 }
848 mark_good_blocks();
849 write_tables();
850 return( 0);
851
852}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * mkswap.c - set up a linux swap device
4 *
5 * (C) 1991 Linus Torvalds. This file may be redistributed as per
6 * the Linux copyright.
7 */
8
9/*
10 * 20.12.91 - time began. Got VM working yesterday by doing this by hand.
11 *
12 * Usage: mkswap [-c] [-vN] [-f] device [size-in-blocks]
13 *
14 * -c for readability checking. (Use it unless you are SURE!)
15 * -vN for swap areas version N. (Only N=0,1 known today.)
16 * -f for forcing swap creation even if it would smash partition table.
17 *
18 * The device may be a block device or an image of one, but this isn't
19 * enforced (but it's not much fun on a character device :-).
20 *
21 * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the
22 * size-in-blocks parameter optional added Wed Feb 8 10:33:43 1995.
23 *
24 * Version 1 swap area code (for kernel 2.1.117), aeb, 981010.
25 *
26 * Sparc fixes, jj@ultra.linux.cz (Jakub Jelinek), 981201 - mangled by aeb.
27 * V1_MAX_PAGES fixes, jj, 990325.
28 *
29 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
30 * - added Native Language Support
31 *
32 * from util-linux -- adapted for busybox by
33 * Erik Andersen <andersen@codepoet.org>. I ripped out Native Language
34 * Support, made some stuff smaller, and fitted for life in busybox.
35 *
36 */
37
38#include <stdio.h>
39#include <unistd.h>
40#include <string.h>
41#include <fcntl.h>
42#include <stdlib.h>
43#include <sys/ioctl.h> /* for _IO */
44#include <sys/utsname.h>
45#include <asm/page.h> /* for PAGE_SIZE and PAGE_SHIFT */
46 /* we also get PAGE_SIZE via getpagesize() */
47#include "busybox.h"
48
49#ifndef _IO
50/* pre-1.3.45 */
51static const int BLKGETSIZE = 0x1260;
52#else
53/* same on i386, m68k, arm; different on alpha, mips, sparc, ppc */
54#define BLKGETSIZE _IO(0x12,96)
55#endif
56
57static char *device_name = NULL;
58static int DEV = -1;
59static long PAGES = 0;
60static int check = 0;
61static int badpages = 0;
62static int version = -1;
63
64#define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r))
65
66/*
67 * The definition of the union swap_header uses the constant PAGE_SIZE.
68 * Unfortunately, on some architectures this depends on the hardware model,
69 * and can only be found at run time -- we use getpagesize().
70 */
71
72static int pagesize;
73static int *signature_page;
74
75static struct swap_header_v1 {
76 char bootbits[1024]; /* Space for disklabel etc. */
77 unsigned int version;
78 unsigned int last_page;
79 unsigned int nr_badpages;
80 unsigned int padding[125];
81 unsigned int badpages[1];
82} *p;
83
84static inline void init_signature_page(void)
85{
86 pagesize = getpagesize();
87
88#ifdef PAGE_SIZE
89 if (pagesize != PAGE_SIZE)
90 bb_error_msg("Assuming pages of size %d", pagesize);
91#endif
92 signature_page = (int *) xmalloc(pagesize);
93 memset(signature_page, 0, pagesize);
94 p = (struct swap_header_v1 *) signature_page;
95}
96
97static inline void write_signature(char *sig)
98{
99 char *sp = (char *) signature_page;
100
101 strncpy(sp + pagesize - 10, sig, 10);
102}
103
104#define V0_MAX_PAGES (8 * (pagesize - 10))
105/* Before 2.2.0pre9 */
106#define V1_OLD_MAX_PAGES ((0x7fffffff / pagesize) - 1)
107/* Since 2.2.0pre9:
108 error if nr of pages >= SWP_OFFSET(SWP_ENTRY(0,~0UL))
109 with variations on
110 #define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8))
111 #define SWP_OFFSET(entry) ((entry) >> 8)
112 on the various architectures. Below the result - yuk.
113
114 Machine pagesize SWP_ENTRY SWP_OFFSET bound+1 oldbound+2
115 i386 2^12 o<<8 e>>8 1<<24 1<<19
116 mips 2^12 o<<15 e>>15 1<<17 1<<19
117 alpha 2^13 o<<40 e>>40 1<<24 1<<18
118 m68k 2^12 o<<12 e>>12 1<<20 1<<19
119 sparc 2^{12,13} (o&0x3ffff)<<9 (e>>9)&0x3ffff 1<<18 1<<{19,18}
120 sparc64 2^13 o<<13 e>>13 1<<51 1<<18
121 ppc 2^12 o<<8 e>>8 1<<24 1<<19
122 armo 2^{13,14,15} o<<8 e>>8 1<<24 1<<{18,17,16}
123 armv 2^12 o<<9 e>>9 1<<23 1<<19
124
125 assuming that longs have 64 bits on alpha and sparc64 and 32 bits elsewhere.
126
127 The bad part is that we need to know this since the kernel will
128 refuse a swap space if it is too large.
129*/
130/* patch from jj - why does this differ from the above? */
131#if defined(__alpha__)
132#define V1_MAX_PAGES ((1 << 24) - 1)
133#elif defined(__mips__)
134#define V1_MAX_PAGES ((1 << 17) - 1)
135#elif defined(__sparc_v9__)
136#define V1_MAX_PAGES ((3 << 29) - 1)
137#elif defined(__sparc__)
138#define V1_MAX_PAGES (pagesize == 8192 ? ((3 << 29) - 1) : ((1 << 18) - 1))
139#else
140#define V1_MAX_PAGES V1_OLD_MAX_PAGES
141#endif
142/* man page now says:
143The maximum useful size of a swap area now depends on the architecture.
144It is roughly 2GB on i386, PPC, m68k, ARM, 1GB on sparc, 512MB on mips,
145128GB on alpha and 3TB on sparc64.
146*/
147
148#define MAX_BADPAGES ((pagesize-1024-128*sizeof(int)-10)/sizeof(int))
149
150static inline void bit_set(unsigned int *addr, unsigned int nr)
151{
152 unsigned int r, m;
153
154 addr += nr / (8 * sizeof(int));
155
156 r = *addr;
157 m = 1 << (nr & (8 * sizeof(int) - 1));
158
159 *addr = r | m;
160}
161
162static int bit_test_and_clear(unsigned int *addr, unsigned int nr)
163{
164 unsigned int r, m;
165
166 addr += nr / (8 * sizeof(int));
167
168 r = *addr;
169 m = 1 << (nr & (8 * sizeof(int) - 1));
170
171 *addr = r & ~m;
172 return (r & m) != 0;
173}
174
175
176static void page_ok(int page)
177{
178 if (version == 0)
179 bit_set(signature_page, page);
180}
181
182static inline void page_bad(int page)
183{
184 if (version == 0)
185 bit_test_and_clear(signature_page, page);
186 else {
187 if (badpages == MAX_BADPAGES)
188 bb_error_msg_and_die("too many bad pages");
189 p->badpages[badpages] = page;
190 }
191 badpages++;
192}
193
194static void check_blocks(void)
195{
196 unsigned int current_page;
197 int do_seek = 1;
198 char *buffer;
199
200 buffer = xmalloc(pagesize);
201 current_page = 0;
202 while (current_page < PAGES) {
203 if (!check) {
204 page_ok(current_page++);
205 continue;
206 }
207 if (do_seek && lseek(DEV, current_page * pagesize, SEEK_SET) !=
208 current_page * pagesize)
209 bb_error_msg_and_die("seek failed in check_blocks");
210 if ((do_seek = (pagesize != read(DEV, buffer, pagesize)))) {
211 page_bad(current_page++);
212 continue;
213 }
214 page_ok(current_page++);
215 }
216 if (badpages == 1)
217 printf("one bad page\n");
218 else if (badpages > 1)
219 printf("%d bad pages\n", badpages);
220}
221
222static long valid_offset(int fd, int offset)
223{
224 char ch;
225
226 if (lseek(fd, offset, 0) < 0)
227 return 0;
228 if (read(fd, &ch, 1) < 1)
229 return 0;
230 return 1;
231}
232
233static int find_size(int fd)
234{
235 unsigned int high, low;
236
237 low = 0;
238 for (high = 1; high > 0 && valid_offset(fd, high); high *= 2)
239 low = high;
240 while (low < high - 1) {
241 const int mid = (low + high) / 2;
242
243 if (valid_offset(fd, mid))
244 low = mid;
245 else
246 high = mid;
247 }
248 return (low + 1);
249}
250
251/* return size in pages, to avoid integer overflow */
252static long get_size(const char *file)
253{
254 int fd;
255 long size;
256
257 if ((fd = open(file, O_RDONLY)) < 0)
258 bb_perror_msg_and_die("%s", file);
259 if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
260 int sectors_per_page = pagesize / 512;
261
262 size /= sectors_per_page;
263 } else {
264 size = find_size(fd) / pagesize;
265 }
266 close(fd);
267 return size;
268}
269
270int mkswap_main(int argc, char **argv)
271{
272 char *tmp;
273 struct stat statbuf;
274 int sz;
275 int maxpages;
276 int goodpages;
277 int offset;
278 int force = 0;
279
280 init_signature_page(); /* get pagesize */
281
282 while (argc-- > 1) {
283 argv++;
284 if (argv[0][0] != '-') {
285 if (device_name) {
286 int blocks_per_page = pagesize / 1024;
287
288 PAGES = strtol(argv[0], &tmp, 0) / blocks_per_page;
289 if (*tmp)
290 bb_show_usage();
291 } else
292 device_name = argv[0];
293 } else {
294 switch (argv[0][1]) {
295 case 'c':
296 check = 1;
297 break;
298 case 'f':
299 force = 1;
300 break;
301 case 'v':
302 version = atoi(argv[0] + 2);
303 break;
304 default:
305 bb_show_usage();
306 }
307 }
308 }
309 if (!device_name) {
310 bb_error_msg("error: Nowhere to set up swap on?");
311 bb_show_usage();
312 }
313 sz = get_size(device_name);
314 if (!PAGES) {
315 PAGES = sz;
316 } else if (PAGES > sz && !force) {
317 bb_error_msg("error: size %ld is larger than device size %d",
318 PAGES * (pagesize / 1024), sz * (pagesize / 1024));
319 return EXIT_FAILURE;
320 }
321
322 if (version == -1) {
323 if (get_kernel_revision() < MAKE_VERSION(2, 1, 117))
324 version = 0;
325 else
326 version = 1;
327 }
328 if (version != 0 && version != 1) {
329 bb_error_msg("error: unknown version %d", version);
330 bb_show_usage();
331 }
332 if (PAGES < 10) {
333 bb_error_msg("error: swap area needs to be at least %ldkB",
334 (long) (10 * pagesize / 1024));
335 bb_show_usage();
336 }
337#if 0
338 maxpages = ((version == 0) ? V0_MAX_PAGES : V1_MAX_PAGES);
339#else
340 if (!version)
341 maxpages = V0_MAX_PAGES;
342 else if (get_kernel_revision() >= MAKE_VERSION(2, 2, 1))
343 maxpages = V1_MAX_PAGES;
344 else {
345 maxpages = V1_OLD_MAX_PAGES;
346 if (maxpages > V1_MAX_PAGES)
347 maxpages = V1_MAX_PAGES;
348 }
349#endif
350 if (PAGES > maxpages) {
351 PAGES = maxpages;
352 bb_error_msg("warning: truncating swap area to %ldkB",
353 PAGES * pagesize / 1024);
354 }
355
356 DEV = open(device_name, O_RDWR);
357 if (DEV < 0 || fstat(DEV, &statbuf) < 0)
358 bb_perror_msg_and_die("%s", device_name);
359 if (!S_ISBLK(statbuf.st_mode))
360 check = 0;
361 else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
362 bb_error_msg_and_die("Will not try to make swapdevice on '%s'", device_name);
363
364#ifdef __sparc__
365 if (!force && version == 0) {
366 /* Don't overwrite partition table unless forced */
367 unsigned char *buffer = (unsigned char *) signature_page;
368 unsigned short *q, sum;
369
370 if (read(DEV, buffer, 512) != 512)
371 bb_error_msg_and_die("fatal: first page unreadable");
372 if (buffer[508] == 0xDA && buffer[509] == 0xBE) {
373 q = (unsigned short *) (buffer + 510);
374 for (sum = 0; q >= (unsigned short *) buffer;)
375 sum ^= *q--;
376 if (!sum) {
377 bb_error_msg("Device '%s' contains a valid Sun disklabel.\n"
378"This probably means creating v0 swap would destroy your partition table\n"
379"No swap created. If you really want to create swap v0 on that device, use\n"
380"the -f option to force it.", device_name);
381 return EXIT_FAILURE;
382 }
383 }
384 }
385#endif
386
387 if (version == 0 || check)
388 check_blocks();
389 if (version == 0 && !bit_test_and_clear(signature_page, 0))
390 bb_error_msg_and_die("fatal: first page unreadable");
391 if (version == 1) {
392 p->version = version;
393 p->last_page = PAGES - 1;
394 p->nr_badpages = badpages;
395 }
396
397 goodpages = PAGES - badpages - 1;
398 if (goodpages <= 0)
399 bb_error_msg_and_die("Unable to set up swap-space: unreadable");
400 printf("Setting up swapspace version %d, size = %ld bytes\n",
401 version, (long) (goodpages * pagesize));
402 write_signature((version == 0) ? "SWAP-SPACE" : "SWAPSPACE2");
403
404 offset = ((version == 0) ? 0 : 1024);
405 if (lseek(DEV, offset, SEEK_SET) != offset)
406 bb_error_msg_and_die("unable to rewind swap-device");
407 if (write(DEV, (char *) signature_page + offset, pagesize - offset)
408 != pagesize - offset)
409 bb_error_msg_and_die("unable to write signature page");
410
411 /*
412 * A subsequent swapon() will fail if the signature
413 * is not actually on disk. (This is a kernel bug.)
414 */
415 if (fsync(DEV))
416 bb_error_msg_and_die("fsync failed");
417 return EXIT_SUCCESS;
418}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini more implementation for busybox
4 *
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 *
8 * Latest version blended together by Erik Andersen <andersen@codepoet.org>,
9 * based on the original more implementation by Bruce, and code from the
10 * Debian boot-floppies team.
11 *
12 * Termios corrects by Vladimir Oleynik <dzo@simtreas.ru>
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 *
28 */
29
30#include <stdio.h>
31#include <fcntl.h>
32#include <signal.h>
33#include <stdlib.h>
34#include <unistd.h>
35#include <sys/ioctl.h>
36#include "busybox.h"
37
38
39#ifdef CONFIG_FEATURE_USE_TERMIOS
40static int cin_fileno;
41#include <termios.h>
42#define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
43#define getTermSettings(fd,argp) tcgetattr(fd, argp);
44
45static struct termios initial_settings, new_settings;
46
47static void set_tty_to_initial_mode(void)
48{
49 setTermSettings(cin_fileno, &initial_settings);
50}
51
52static void gotsig(int sig)
53{
54 putchar('\n');
55 exit(EXIT_FAILURE);
56}
57#endif /* CONFIG_FEATURE_USE_TERMIOS */
58
59
60extern int more_main(int argc, char **argv)
61{
62 int c, lines, input = 0;
63 int please_display_more_prompt = 0;
64 struct stat st;
65 FILE *file;
66 FILE *cin;
67 int len, page_height;
68 int terminal_width;
69 int terminal_height;
70#ifndef CONFIG_FEATURE_USE_TERMIOS
71 int cin_fileno;
72#endif
73
74 argc--;
75 argv++;
76
77
78 /* not use inputing from terminal if usage: more > outfile */
79 if(isatty(STDOUT_FILENO)) {
80 cin = fopen(CURRENT_TTY, "r");
81 if (!cin)
82 cin = bb_xfopen(CONSOLE_DEV, "r");
83 cin_fileno = fileno(cin);
84 please_display_more_prompt = 2;
85#ifdef CONFIG_FEATURE_USE_TERMIOS
86 getTermSettings(cin_fileno, &initial_settings);
87 new_settings = initial_settings;
88 new_settings.c_lflag &= ~ICANON;
89 new_settings.c_lflag &= ~ECHO;
90 new_settings.c_cc[VMIN] = 1;
91 new_settings.c_cc[VTIME] = 0;
92 setTermSettings(cin_fileno, &new_settings);
93 atexit(set_tty_to_initial_mode);
94 (void) signal(SIGINT, gotsig);
95 (void) signal(SIGQUIT, gotsig);
96 (void) signal(SIGTERM, gotsig);
97#endif
98 } else {
99 cin = stdin;
100 }
101
102 do {
103 if (argc == 0) {
104 file = stdin;
105 } else
106 file = bb_wfopen(*argv, "r");
107 if(file==0)
108 goto loop;
109
110 st.st_size = 0;
111 fstat(fileno(file), &st);
112
113 please_display_more_prompt &= ~1;
114
115 get_terminal_width_height(cin_fileno, &terminal_width, &terminal_height);
116 if (terminal_height > 4)
117 terminal_height -= 2;
118 if (terminal_width > 0)
119 terminal_width -= 1;
120
121 len=0;
122 lines = 0;
123 page_height = terminal_height;
124 while ((c = getc(file)) != EOF) {
125
126 if ((please_display_more_prompt & 3) == 3) {
127 len = printf("--More-- ");
128 if (file != stdin && st.st_size > 0) {
129#if _FILE_OFFSET_BITS == 64
130 len += printf("(%d%% of %lld bytes)",
131 (int) (100 * ((double) ftell(file) /
132 (double) st.st_size)), (long long)st.st_size);
133#else
134 len += printf("(%d%% of %ld bytes)",
135 (int) (100 * ((double) ftell(file) /
136 (double) st.st_size)), (long)st.st_size);
137#endif
138 }
139
140 fflush(stdout);
141
142 /*
143 * We've just displayed the "--More--" prompt, so now we need
144 * to get input from the user.
145 */
146 input = getc(cin);
147#ifndef CONFIG_FEATURE_USE_TERMIOS
148 printf("\033[A"); /* up cursor */
149#endif
150 /* Erase the "More" message */
151 putc('\r', stdout);
152 while (--len >= 0)
153 putc(' ', stdout);
154 putc('\r', stdout);
155 len=0;
156 lines = 0;
157 page_height = terminal_height;
158 please_display_more_prompt &= ~1;
159
160 if (input == 'q')
161 goto end;
162 }
163
164 /*
165 * There are two input streams to worry about here:
166 *
167 * c : the character we are reading from the file being "mored"
168 * input : a character received from the keyboard
169 *
170 * If we hit a newline in the _file_ stream, we want to test and
171 * see if any characters have been hit in the _input_ stream. This
172 * allows the user to quit while in the middle of a file.
173 */
174 if (c == '\n') {
175 /* increment by just one line if we are at
176 * the end of this line */
177 if (input == '\n')
178 please_display_more_prompt |= 1;
179 /* Adjust the terminal height for any overlap, so that
180 * no lines get lost off the top. */
181 if (len >= terminal_width) {
182 int quot, rem;
183 quot = len / terminal_width;
184 rem = len - (quot * terminal_width);
185 if (quot) {
186 if (rem)
187 page_height-=quot;
188 else
189 page_height-=(quot-1);
190 }
191 }
192 if (++lines >= page_height) {
193 please_display_more_prompt |= 1;
194 }
195 len=0;
196 }
197 /*
198 * If we just read a newline from the file being 'mored' and any
199 * key other than a return is hit, scroll by one page
200 */
201 putc(c, stdout);
202 len++;
203 }
204 fclose(file);
205 fflush(stdout);
206loop:
207 argv++;
208 } while (--argc > 0);
209 end:
210 return 0;
211}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini mount implementation for busybox
4 *
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * 3/21/1999 Charles P. Wright <cpwright@cpwright.com>
23 * searches through fstab when -a is passed
24 * will try mounting stuff with all fses when passed -t auto
25 *
26 * 1999-04-17 Dave Cinege...Rewrote -t auto. Fixed ro mtab.
27 *
28 * 1999-10-07 Erik Andersen <andersen@codepoet.org>.
29 * Rewrite of a lot of code. Removed mtab usage (I plan on
30 * putting it back as a compile-time option some time),
31 * major adjustments to option parsing, and some serious
32 * dieting all around.
33 *
34 * 1999-11-06 mtab support is back - andersee
35 *
36 * 2000-01-12 Ben Collins <bcollins@debian.org>, Borrowed utils-linux's
37 * mount to add loop support.
38 *
39 * 2000-04-30 Dave Cinege <dcinege@psychosis.com>
40 * Rewrote fstab while loop and lower mount section. Can now do
41 * single mounts from fstab. Can override fstab options for single
42 * mount. Common mount_one call for single mounts and 'all'. Fixed
43 * mtab updating and stale entries. Removed 'remount' default.
44 *
45 */
46
47#include <limits.h>
48#include <stdlib.h>
49#include <unistd.h>
50#include <errno.h>
51#include <string.h>
52#include <stdio.h>
53#include <mntent.h>
54#include <ctype.h>
55#include "busybox.h"
56
57#ifdef CONFIG_NFSMOUNT
58#if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
59#error "You need to build uClibc with UCLIBC_HAS_RPC for busybox mount with NFS support to compile."
60#endif
61#endif
62
63enum {
64 MS_MGC_VAL = 0xc0ed0000, /* Magic number indicatng "new" flags */
65 MS_RDONLY = 1, /* Mount read-only */
66 MS_NOSUID = 2, /* Ignore suid and sgid bits */
67 MS_NODEV = 4, /* Disallow access to device special files */
68 MS_NOEXEC = 8, /* Disallow program execution */
69 MS_SYNCHRONOUS = 16, /* Writes are synced at once */
70 MS_REMOUNT = 32, /* Alter flags of a mounted FS */
71 MS_MANDLOCK = 64, /* Allow mandatory locks on an FS */
72 S_QUOTA = 128, /* Quota initialized for file/directory/symlink */
73 S_APPEND = 256, /* Append-only file */
74 S_IMMUTABLE = 512, /* Immutable file */
75 MS_NOATIME = 1024, /* Do not update access times. */
76 MS_NODIRATIME = 2048, /* Do not update directory access times */
77 MS_BIND = 4096, /* Use the new linux 2.4.x "mount --bind" feature */
78 MS_MOVE = 8192, /* Use the new linux 2.4.x "mount --move" feature */
79};
80
81
82#if defined CONFIG_FEATURE_MOUNT_LOOP
83#include <fcntl.h>
84#include <sys/ioctl.h>
85static int use_loop = FALSE;
86#endif
87
88extern int mount(__const char *__special_file, __const char *__dir,
89 __const char *__fstype, unsigned long int __rwflag,
90 __const void *__data);
91extern int umount(__const char *__special_file);
92extern int umount2(__const char *__special_file, int __flags);
93
94extern int sysfs(int option, unsigned int fs_index, char *buf);
95
96struct mount_options {
97 const char *name;
98 unsigned long and;
99 unsigned long or;
100};
101
102static const struct mount_options mount_options[] = {
103 {"async", ~MS_SYNCHRONOUS, 0},
104 {"atime", ~0, ~MS_NOATIME},
105 {"defaults", ~0, 0},
106 {"noauto", ~0, 0},
107 {"dev", ~MS_NODEV, 0},
108 {"diratime", ~0, ~MS_NODIRATIME},
109 {"exec", ~MS_NOEXEC, 0},
110 {"noatime", ~0, MS_NOATIME},
111 {"nodev", ~0, MS_NODEV},
112 {"nodiratime", ~0, MS_NODIRATIME},
113 {"noexec", ~0, MS_NOEXEC},
114 {"nosuid", ~0, MS_NOSUID},
115 {"remount", ~0, MS_REMOUNT},
116 {"ro", ~0, MS_RDONLY},
117 {"rw", ~MS_RDONLY, 0},
118 {"suid", ~MS_NOSUID, 0},
119 {"sync", ~0, MS_SYNCHRONOUS},
120 {"bind", ~0, MS_BIND},
121 {"move", ~0, MS_MOVE},
122 {0, 0, 0}
123};
124
125static int
126do_mount(char *specialfile, char *dir, char *filesystemtype, long flags,
127 void *string_flags, int useMtab, int fakeIt, char *mtab_opts,
128 int mount_all)
129{
130 int status = 0;
131
132#if defined CONFIG_FEATURE_MOUNT_LOOP
133 char *lofile = NULL;
134#endif
135
136 if (!fakeIt) {
137#if defined CONFIG_FEATURE_MOUNT_LOOP
138 if (use_loop == TRUE) {
139 int loro = flags & MS_RDONLY;
140
141 lofile = specialfile;
142
143 specialfile = find_unused_loop_device();
144 if (specialfile == NULL) {
145 bb_error_msg_and_die("Could not find a spare loop device");
146 }
147 if (set_loop(specialfile, lofile, 0, &loro)) {
148 bb_error_msg_and_die("Could not setup loop device");
149 }
150 if (!(flags & MS_RDONLY) && loro) { /* loop is ro, but wanted rw */
151 bb_error_msg("WARNING: loop device is read-only");
152 flags |= MS_RDONLY;
153 }
154 }
155#endif
156 status = mount(specialfile, dir, filesystemtype, flags, string_flags);
157 if (status < 0 && errno == EROFS) {
158 bb_error_msg("%s is write-protected, mounting read-only",
159 specialfile);
160 status = mount(specialfile, dir, filesystemtype, flags |=
161 MS_RDONLY, string_flags);
162 }
163 /* Don't whine about already mounted filesystems when mounting all. */
164 if (status < 0 && errno == EBUSY && mount_all) {
165 return TRUE;
166 }
167 }
168
169
170 /* If the mount was sucessful, do anything needed, then return TRUE */
171 if (status == 0 || fakeIt == TRUE) {
172
173#if defined CONFIG_FEATURE_MTAB_SUPPORT
174 if (useMtab) {
175 erase_mtab(specialfile); /* Clean any stale entries */
176 write_mtab(specialfile, dir, filesystemtype, flags, mtab_opts);
177 }
178#endif
179 return (TRUE);
180 }
181
182 /* Bummer. mount failed. Clean up */
183#if defined CONFIG_FEATURE_MOUNT_LOOP
184 if (lofile != NULL) {
185 del_loop(specialfile);
186 }
187#endif
188
189 if (errno == EPERM) {
190 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
191 }
192
193 return (FALSE);
194}
195
196
197static void paste_str(char **s1, const char *s2)
198{
199 *s1 = xrealloc(*s1, strlen(*s1) + strlen(s2) + 1);
200 strcat(*s1, s2);
201}
202
203/* Seperate standard mount options from the nonstandard string options */
204static void parse_mount_options(char *options, int *flags, char **strflags)
205{
206 while (options) {
207 int gotone = FALSE;
208 char *comma = strchr(options, ',');
209 const struct mount_options *f = mount_options;
210
211 if (comma) {
212 *comma = '\0';
213 }
214
215 while (f->name != 0) {
216 if (strcasecmp(f->name, options) == 0) {
217
218 *flags &= f->and;
219 *flags |= f->or;
220 gotone = TRUE;
221 break;
222 }
223 f++;
224 }
225#if defined CONFIG_FEATURE_MOUNT_LOOP
226 if (!strcasecmp("loop", options)) { /* loop device support */
227 use_loop = TRUE;
228 gotone = TRUE;
229 }
230#endif
231 if (!gotone) {
232 if (**strflags) {
233 /* have previous parsed options */
234 paste_str(strflags, ",");
235 }
236 paste_str(strflags, options);
237 }
238 if (comma) {
239 *comma = ',';
240 options = ++comma;
241 } else {
242 break;
243 }
244 }
245}
246
247static int mount_one(char *blockDevice, char *directory, char *filesystemType,
248 unsigned long flags, char *string_flags, int useMtab,
249 int fakeIt, char *mtab_opts, int whineOnErrors,
250 int mount_all)
251{
252 int status = 0;
253 if (strcmp(filesystemType, "auto") == 0) {
254 char buf[255];
255 FILE *f;
256 int read_proc = 0;
257
258 f = fopen("/etc/filesystems", "r");
259
260 if (f) {
261 while (fgets(buf, sizeof(buf), f)) {
262 if (*buf == '*') {
263 read_proc = 1;
264 } else if (*buf == '#') {
265 continue;
266 } else {
267 filesystemType = buf;
268
269 /* Add NULL termination to each line */
270 while (*filesystemType && !isspace(*filesystemType)) {
271 filesystemType++;
272 }
273 *filesystemType = '\0';
274
275 filesystemType = buf;
276
277 if (bb_strlen(filesystemType)) {
278 status = do_mount(blockDevice, directory, filesystemType,
279 flags | MS_MGC_VAL, string_flags,
280 useMtab, fakeIt, mtab_opts, mount_all);
281 if (status) {
282 break;
283 }
284 }
285
286 }
287 }
288 fclose(f);
289 } else {
290 read_proc = 1;
291 }
292
293 if (read_proc && !status) {
294
295 f = bb_xfopen("/proc/filesystems", "r");
296
297 while (fgets(buf, sizeof(buf), f) != NULL) {
298 filesystemType = buf;
299 if (*filesystemType == '\t') { /* Not a nodev filesystem */
300
301 /* Add NULL termination to each line */
302 while (*filesystemType && *filesystemType != '\n') {
303 filesystemType++;
304 }
305 *filesystemType = '\0';
306
307 filesystemType = buf;
308 filesystemType++; /* hop past tab */
309
310 status = do_mount(blockDevice, directory, filesystemType,
311 flags | MS_MGC_VAL, string_flags, useMtab,
312 fakeIt, mtab_opts, mount_all);
313 if (status) {
314 break;
315 }
316 }
317 }
318 fclose(f);
319 }
320 } else {
321 status = do_mount(blockDevice, directory, filesystemType,
322 flags | MS_MGC_VAL, string_flags, useMtab, fakeIt,
323 mtab_opts, mount_all);
324 }
325
326 if (!status) {
327 if (whineOnErrors) {
328 bb_perror_msg("Mounting %s on %s failed", blockDevice, directory);
329 }
330 return (FALSE);
331 }
332 return (TRUE);
333}
334
335static void show_mounts(char *onlytype)
336{
337 FILE *mountTable = setmntent(bb_path_mtab_file, "r");
338
339 if (mountTable) {
340 struct mntent *m;
341
342 while ((m = getmntent(mountTable)) != 0) {
343 char *blockDevice = m->mnt_fsname;
344
345 if (strcmp(blockDevice, "rootfs") == 0) {
346 continue;
347 } else if (strcmp(blockDevice, "/dev/root") == 0) {
348 blockDevice = find_real_root_device_name();
349 }
350 if (!onlytype || (strcmp(m->mnt_type, onlytype) == 0)) {
351 printf("%s on %s type %s (%s)\n", blockDevice, m->mnt_dir,
352 m->mnt_type, m->mnt_opts);
353 }
354#ifdef CONFIG_FEATURE_CLEAN_UP
355 if (blockDevice != m->mnt_fsname) {
356 free(blockDevice);
357 }
358#endif
359 }
360 endmntent(mountTable);
361 } else {
362 bb_perror_msg_and_die(bb_path_mtab_file);
363 }
364 exit(EXIT_SUCCESS);
365}
366
367extern int mount_main(int argc, char **argv)
368{
369 struct stat statbuf;
370 char *string_flags = bb_xstrdup("");
371 char *extra_opts;
372 int flags = 0;
373 char *filesystemType = "auto";
374 int got_filesystemType = 0;
375 char *device = xmalloc(PATH_MAX);
376 char *directory = xmalloc(PATH_MAX);
377 struct mntent *m = NULL;
378 int all = FALSE;
379 int fakeIt = FALSE;
380 int useMtab = TRUE;
381 int rc = EXIT_FAILURE;
382 FILE *f = 0;
383 int opt;
384
385 /* Parse options */
386 while ((opt = getopt(argc, argv, "o:rt:wafnv")) > 0) {
387 switch (opt) {
388 case 'o':
389 parse_mount_options(optarg, &flags, &string_flags);
390 break;
391 case 'r':
392 flags |= MS_RDONLY;
393 break;
394 case 't':
395 filesystemType = optarg;
396 got_filesystemType = 1;
397 break;
398 case 'w':
399 flags &= ~MS_RDONLY;
400 break;
401 case 'a':
402 all = TRUE;
403 break;
404 case 'f':
405 fakeIt = TRUE;
406 break;
407 case 'n':
408#ifdef CONFIG_FEATURE_MTAB_SUPPORT
409 useMtab = FALSE;
410#endif
411 break;
412 case 'v':
413 break; /* ignore -v */
414 }
415 }
416
417 if (!all && (optind == argc)) {
418 show_mounts(got_filesystemType ? filesystemType : NULL);
419 }
420
421 if (optind < argc) {
422 /* if device is a filename get its real path */
423 if (stat(argv[optind], &statbuf) == 0) {
424 char *tmp = bb_simplify_path(argv[optind]);
425
426 safe_strncpy(device, tmp, PATH_MAX);
427 } else {
428 safe_strncpy(device, argv[optind], PATH_MAX);
429 }
430 }
431
432 if (optind + 1 < argc)
433 directory = bb_simplify_path(argv[optind + 1]);
434
435 if (all || optind + 1 == argc) {
436 f = setmntent("/etc/fstab", "r");
437
438 if (f == NULL)
439 bb_perror_msg_and_die("\nCannot read /etc/fstab");
440
441 while ((m = getmntent(f)) != NULL) {
442 if (!all && (optind + 1 == argc)
443 && ((strcmp(device, m->mnt_fsname) != 0)
444 && (strcmp(device, m->mnt_dir) != 0))) {
445 continue;
446 }
447
448 if (all && ( /* If we're mounting 'all' */
449 (strstr(m->mnt_opts, "noauto")) || /* and the file system isn't noauto, */
450 (strstr(m->mnt_type, "swap")))) /* and isn't swap, then mount it */
451 {
452 continue;
453 }
454
455 if (all || flags == 0) { /* Allow single mount to override fstab flags */
456 flags = 0;
457 string_flags[0] = 0;
458 parse_mount_options(m->mnt_opts, &flags, &string_flags);
459 }
460
461 strcpy(device, m->mnt_fsname);
462 strcpy(directory, m->mnt_dir);
463 filesystemType = bb_xstrdup(m->mnt_type);
464 singlemount:
465 extra_opts = string_flags;
466 rc = EXIT_SUCCESS;
467#ifdef CONFIG_NFSMOUNT
468 if (strchr(device, ':') != NULL) {
469 filesystemType = "nfs";
470 if (nfsmount
471 (device, directory, &flags, &extra_opts, &string_flags,
472 1)) {
473 bb_perror_msg("nfsmount failed");
474 rc = EXIT_FAILURE;
475 }
476 }
477#endif
478 if (!mount_one
479 (device, directory, filesystemType, flags, string_flags,
480 useMtab, fakeIt, extra_opts, TRUE, all)) {
481 rc = EXIT_FAILURE;
482 }
483 if (!all) {
484 break;
485 }
486 }
487 if (f) {
488 endmntent(f);
489 }
490 if (!all && f && m == NULL) {
491 fprintf(stderr, "Can't find %s in /etc/fstab\n", device);
492 }
493 return rc;
494 }
495
496 goto singlemount;
497}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * nfsmount.c -- Linux NFS mount
4 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
17 * numbers to be specified on the command line.
18 *
19 * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
20 * Omit the call to connect() for Linux version 1.3.11 or later.
21 *
22 * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
23 * Implemented the "bg", "fg" and "retry" mount options for NFS.
24 *
25 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
26 * - added Native Language Support
27 *
28 * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
29 * plus NFSv3 stuff.
30 */
31
32/*
33 * nfsmount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
34 */
35
36#include <unistd.h>
37#include <stdio.h>
38#include <string.h>
39#include <errno.h>
40#include <netdb.h>
41#include <sys/socket.h>
42#include <time.h>
43#include <sys/utsname.h>
44#include <netinet/in.h>
45#include <arpa/inet.h>
46#include <stdlib.h>
47#include "busybox.h"
48#undef TRUE
49#undef FALSE
50#include <rpc/rpc.h>
51#include <rpc/pmap_prot.h>
52#include <rpc/pmap_clnt.h>
53#include "nfsmount.h"
54
55
56/*
57 * NFS stats. The good thing with these values is that NFSv3 errors are
58 * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which
59 * no-one uses anyway), so we can happily mix code as long as we make sure
60 * no NFSv3 errors are returned to NFSv2 clients.
61 * Error codes that have a `--' in the v2 column are not part of the
62 * standard, but seem to be widely used nevertheless.
63 */
64enum nfs_stat {
65 NFS_OK = 0, /* v2 v3 */
66 NFSERR_PERM = 1, /* v2 v3 */
67 NFSERR_NOENT = 2, /* v2 v3 */
68 NFSERR_IO = 5, /* v2 v3 */
69 NFSERR_NXIO = 6, /* v2 v3 */
70 NFSERR_EAGAIN = 11, /* v2 v3 */
71 NFSERR_ACCES = 13, /* v2 v3 */
72 NFSERR_EXIST = 17, /* v2 v3 */
73 NFSERR_XDEV = 18, /* v3 */
74 NFSERR_NODEV = 19, /* v2 v3 */
75 NFSERR_NOTDIR = 20, /* v2 v3 */
76 NFSERR_ISDIR = 21, /* v2 v3 */
77 NFSERR_INVAL = 22, /* v2 v3 that Sun forgot */
78 NFSERR_FBIG = 27, /* v2 v3 */
79 NFSERR_NOSPC = 28, /* v2 v3 */
80 NFSERR_ROFS = 30, /* v2 v3 */
81 NFSERR_MLINK = 31, /* v3 */
82 NFSERR_OPNOTSUPP = 45, /* v2 v3 */
83 NFSERR_NAMETOOLONG = 63, /* v2 v3 */
84 NFSERR_NOTEMPTY = 66, /* v2 v3 */
85 NFSERR_DQUOT = 69, /* v2 v3 */
86 NFSERR_STALE = 70, /* v2 v3 */
87 NFSERR_REMOTE = 71, /* v2 v3 */
88 NFSERR_WFLUSH = 99, /* v2 */
89 NFSERR_BADHANDLE = 10001, /* v3 */
90 NFSERR_NOT_SYNC = 10002, /* v3 */
91 NFSERR_BAD_COOKIE = 10003, /* v3 */
92 NFSERR_NOTSUPP = 10004, /* v3 */
93 NFSERR_TOOSMALL = 10005, /* v3 */
94 NFSERR_SERVERFAULT = 10006, /* v3 */
95 NFSERR_BADTYPE = 10007, /* v3 */
96 NFSERR_JUKEBOX = 10008 /* v3 */
97};
98
99#define NFS_PROGRAM 100003
100
101
102
103#ifndef NFS_FHSIZE
104static const int NFS_FHSIZE = 32;
105#endif
106#ifndef NFS_PORT
107static const int NFS_PORT = 2049;
108#endif
109
110/* Disable the nls stuff */
111# undef bindtextdomain
112# define bindtextdomain(Domain, Directory) /* empty */
113# undef textdomain
114# define textdomain(Domain) /* empty */
115# define _(Text) (Text)
116# define N_(Text) (Text)
117
118static const int MS_MGC_VAL = 0xc0ed0000; /* Magic number indicatng "new" flags */
119static const int MS_RDONLY = 1; /* Mount read-only */
120static const int MS_NOSUID = 2; /* Ignore suid and sgid bits */
121static const int MS_NODEV = 4; /* Disallow access to device special files */
122static const int MS_NOEXEC = 8; /* Disallow program execution */
123static const int MS_SYNCHRONOUS = 16; /* Writes are synced at once */
124static const int MS_REMOUNT = 32; /* Alter flags of a mounted FS */
125static const int MS_MANDLOCK = 64; /* Allow mandatory locks on an FS */
126static const int S_QUOTA = 128; /* Quota initialized for file/directory/symlink */
127static const int S_APPEND = 256; /* Append-only file */
128static const int S_IMMUTABLE = 512; /* Immutable file */
129static const int MS_NOATIME = 1024; /* Do not update access times. */
130static const int MS_NODIRATIME = 2048; /* Do not update directory access times */
131
132
133/*
134 * We want to be able to compile mount on old kernels in such a way
135 * that the binary will work well on more recent kernels.
136 * Thus, if necessary we teach nfsmount.c the structure of new fields
137 * that will come later.
138 *
139 * Moreover, the new kernel includes conflict with glibc includes
140 * so it is easiest to ignore the kernel altogether (at compile time).
141 */
142
143/* NOTE: Do not make this into a 'static const int' because the pre-processor
144 * needs to test this value in some #if statements. */
145#define NFS_MOUNT_VERSION 4
146
147struct nfs2_fh {
148 char data[32];
149};
150struct nfs3_fh {
151 unsigned short size;
152 unsigned char data[64];
153};
154
155struct nfs_mount_data {
156 int version; /* 1 */
157 int fd; /* 1 */
158 struct nfs2_fh old_root; /* 1 */
159 int flags; /* 1 */
160 int rsize; /* 1 */
161 int wsize; /* 1 */
162 int timeo; /* 1 */
163 int retrans; /* 1 */
164 int acregmin; /* 1 */
165 int acregmax; /* 1 */
166 int acdirmin; /* 1 */
167 int acdirmax; /* 1 */
168 struct sockaddr_in addr; /* 1 */
169 char hostname[256]; /* 1 */
170 int namlen; /* 2 */
171 unsigned int bsize; /* 3 */
172 struct nfs3_fh root; /* 4 */
173};
174
175/* bits in the flags field */
176
177static const int NFS_MOUNT_SOFT = 0x0001; /* 1 */
178static const int NFS_MOUNT_INTR = 0x0002; /* 1 */
179static const int NFS_MOUNT_SECURE = 0x0004; /* 1 */
180static const int NFS_MOUNT_POSIX = 0x0008; /* 1 */
181static const int NFS_MOUNT_NOCTO = 0x0010; /* 1 */
182static const int NFS_MOUNT_NOAC = 0x0020; /* 1 */
183static const int NFS_MOUNT_TCP = 0x0040; /* 2 */
184static const int NFS_MOUNT_VER3 = 0x0080; /* 3 */
185static const int NFS_MOUNT_KERBEROS = 0x0100; /* 3 */
186static const int NFS_MOUNT_NONLM = 0x0200; /* 3 */
187
188
189#define UTIL_LINUX_VERSION "2.10m"
190#define util_linux_version "util-linux-2.10m"
191
192#define HAVE_inet_aton
193#define HAVE_scsi_h
194#define HAVE_blkpg_h
195#define HAVE_kd_h
196#define HAVE_termcap
197#define HAVE_locale_h
198#define HAVE_libintl_h
199#define ENABLE_NLS
200#define HAVE_langinfo_h
201#define HAVE_progname
202#define HAVE_openpty
203#define HAVE_nanosleep
204#define HAVE_personality
205#define HAVE_tm_gmtoff
206
207static char *nfs_strerror(int status);
208
209#define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r))
210#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
211
212static const int EX_FAIL = 32; /* mount failure */
213static const int EX_BG = 256; /* retry in background (internal only) */
214
215
216/*
217 * nfs_mount_version according to the sources seen at compile time.
218 */
219static int nfs_mount_version;
220
221/*
222 * Unfortunately, the kernel prints annoying console messages
223 * in case of an unexpected nfs mount version (instead of
224 * just returning some error). Therefore we'll have to try
225 * and figure out what version the kernel expects.
226 *
227 * Variables:
228 * KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
229 * NFS_MOUNT_VERSION: these nfsmount sources at compile time
230 * nfs_mount_version: version this source and running kernel can handle
231 */
232static void
233find_kernel_nfs_mount_version(void)
234{
235 static int kernel_version = 0;
236
237 if (kernel_version)
238 return;
239
240 nfs_mount_version = NFS_MOUNT_VERSION; /* default */
241
242 kernel_version = get_kernel_revision();
243 if (kernel_version) {
244 if (kernel_version < MAKE_VERSION(2,1,32))
245 nfs_mount_version = 1;
246 else if (kernel_version < MAKE_VERSION(2,2,18) ||
247 (kernel_version >= MAKE_VERSION(2,3,0) &&
248 kernel_version < MAKE_VERSION(2,3,99)))
249 nfs_mount_version = 3;
250 else
251 nfs_mount_version = 4; /* since 2.3.99pre4 */
252 }
253 if (nfs_mount_version > NFS_MOUNT_VERSION)
254 nfs_mount_version = NFS_MOUNT_VERSION;
255}
256
257static struct pmap *
258get_mountport(struct sockaddr_in *server_addr,
259 long unsigned prog,
260 long unsigned version,
261 long unsigned proto,
262 long unsigned port)
263{
264struct pmaplist *pmap;
265static struct pmap p = {0, 0, 0, 0};
266
267server_addr->sin_port = PMAPPORT;
268pmap = pmap_getmaps(server_addr);
269
270if (version > MAX_NFSPROT)
271 version = MAX_NFSPROT;
272if (!prog)
273 prog = MOUNTPROG;
274p.pm_prog = prog;
275p.pm_vers = version;
276p.pm_prot = proto;
277p.pm_port = port;
278
279while (pmap) {
280 if (pmap->pml_map.pm_prog != prog)
281 goto next;
282 if (!version && p.pm_vers > pmap->pml_map.pm_vers)
283 goto next;
284 if (version > 2 && pmap->pml_map.pm_vers != version)
285 goto next;
286 if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
287 goto next;
288 if (pmap->pml_map.pm_vers > MAX_NFSPROT ||
289 (proto && p.pm_prot && pmap->pml_map.pm_prot != proto) ||
290 (port && pmap->pml_map.pm_port != port))
291 goto next;
292 memcpy(&p, &pmap->pml_map, sizeof(p));
293next:
294 pmap = pmap->pml_next;
295}
296if (!p.pm_vers)
297 p.pm_vers = MOUNTVERS;
298if (!p.pm_port)
299 p.pm_port = MOUNTPORT;
300if (!p.pm_prot)
301 p.pm_prot = IPPROTO_TCP;
302return &p;
303}
304
305int nfsmount(const char *spec, const char *node, int *flags,
306 char **extra_opts, char **mount_opts, int running_bg)
307{
308 static char *prev_bg_host;
309 char hostdir[1024];
310 CLIENT *mclient;
311 char *hostname;
312 char *pathname;
313 char *old_opts;
314 char *mounthost=NULL;
315 char new_opts[1024];
316 struct timeval total_timeout;
317 enum clnt_stat clnt_stat;
318 struct nfs_mount_data data;
319 char *opt, *opteq;
320 int val;
321 struct hostent *hp;
322 struct sockaddr_in server_addr;
323 struct sockaddr_in mount_server_addr;
324 struct pmap* pm_mnt;
325 int msock, fsock;
326 struct timeval retry_timeout;
327 union {
328 struct fhstatus nfsv2;
329 struct mountres3 nfsv3;
330 } status;
331 struct stat statbuf;
332 char *s;
333 int port;
334 int mountport;
335 int proto;
336 int bg;
337 int soft;
338 int intr;
339 int posix;
340 int nocto;
341 int noac;
342 int nolock;
343 int retry;
344 int tcp;
345 int mountprog;
346 int mountvers;
347 int nfsprog;
348 int nfsvers;
349 int retval;
350 time_t t;
351 time_t prevt;
352 time_t timeout;
353
354 find_kernel_nfs_mount_version();
355
356 retval = EX_FAIL;
357 msock = fsock = -1;
358 mclient = NULL;
359 if (strlen(spec) >= sizeof(hostdir)) {
360 bb_error_msg("excessively long host:dir argument");
361 goto fail;
362 }
363 strcpy(hostdir, spec);
364 if ((s = strchr(hostdir, ':'))) {
365 hostname = hostdir;
366 pathname = s + 1;
367 *s = '\0';
368 /* Ignore all but first hostname in replicated mounts
369 until they can be fully supported. (mack@sgi.com) */
370 if ((s = strchr(hostdir, ','))) {
371 *s = '\0';
372 bb_error_msg("warning: multiple hostnames not supported");
373 }
374 } else {
375 bb_error_msg("directory to mount not in host:dir format");
376 goto fail;
377 }
378
379 server_addr.sin_family = AF_INET;
380#ifdef HAVE_inet_aton
381 if (!inet_aton(hostname, &server_addr.sin_addr))
382#endif
383 {
384 if ((hp = gethostbyname(hostname)) == NULL) {
385 bb_herror_msg("%s", hostname);
386 goto fail;
387 } else {
388 if (hp->h_length > sizeof(struct in_addr)) {
389 bb_error_msg("got bad hp->h_length");
390 hp->h_length = sizeof(struct in_addr);
391 }
392 memcpy(&server_addr.sin_addr,
393 hp->h_addr, hp->h_length);
394 }
395 }
396
397 memcpy (&mount_server_addr, &server_addr, sizeof (mount_server_addr));
398
399 /* add IP address to mtab options for use when unmounting */
400
401 s = inet_ntoa(server_addr.sin_addr);
402 old_opts = *extra_opts;
403 if (!old_opts)
404 old_opts = "";
405 if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
406 bb_error_msg("excessively long option argument");
407 goto fail;
408 }
409 sprintf(new_opts, "%s%saddr=%s",
410 old_opts, *old_opts ? "," : "", s);
411 *extra_opts = bb_xstrdup(new_opts);
412
413 /* Set default options.
414 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
415 * let the kernel decide.
416 * timeo is filled in after we know whether it'll be TCP or UDP. */
417 memset(&data, 0, sizeof(data));
418 data.retrans = 3;
419 data.acregmin = 3;
420 data.acregmax = 60;
421 data.acdirmin = 30;
422 data.acdirmax = 60;
423#if NFS_MOUNT_VERSION >= 2
424 data.namlen = NAME_MAX;
425#endif
426
427 bg = 0;
428 soft = 0;
429 intr = 0;
430 posix = 0;
431 nocto = 0;
432 nolock = 0;
433 noac = 0;
434 retry = 10000; /* 10000 minutes ~ 1 week */
435 tcp = 0;
436
437 mountprog = MOUNTPROG;
438 mountvers = 0;
439 port = 0;
440 mountport = 0;
441 nfsprog = NFS_PROGRAM;
442 nfsvers = 0;
443
444 /* parse options */
445
446 for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
447 if ((opteq = strchr(opt, '='))) {
448 val = atoi(opteq + 1);
449 *opteq = '\0';
450 if (!strcmp(opt, "rsize"))
451 data.rsize = val;
452 else if (!strcmp(opt, "wsize"))
453 data.wsize = val;
454 else if (!strcmp(opt, "timeo"))
455 data.timeo = val;
456 else if (!strcmp(opt, "retrans"))
457 data.retrans = val;
458 else if (!strcmp(opt, "acregmin"))
459 data.acregmin = val;
460 else if (!strcmp(opt, "acregmax"))
461 data.acregmax = val;
462 else if (!strcmp(opt, "acdirmin"))
463 data.acdirmin = val;
464 else if (!strcmp(opt, "acdirmax"))
465 data.acdirmax = val;
466 else if (!strcmp(opt, "actimeo")) {
467 data.acregmin = val;
468 data.acregmax = val;
469 data.acdirmin = val;
470 data.acdirmax = val;
471 }
472 else if (!strcmp(opt, "retry"))
473 retry = val;
474 else if (!strcmp(opt, "port"))
475 port = val;
476 else if (!strcmp(opt, "mountport"))
477 mountport = val;
478 else if (!strcmp(opt, "mounthost"))
479 mounthost=bb_xstrndup(opteq+1,
480 strcspn(opteq+1," \t\n\r,"));
481 else if (!strcmp(opt, "mountprog"))
482 mountprog = val;
483 else if (!strcmp(opt, "mountvers"))
484 mountvers = val;
485 else if (!strcmp(opt, "nfsprog"))
486 nfsprog = val;
487 else if (!strcmp(opt, "nfsvers") ||
488 !strcmp(opt, "vers"))
489 nfsvers = val;
490 else if (!strcmp(opt, "proto")) {
491 if (!strncmp(opteq+1, "tcp", 3))
492 tcp = 1;
493 else if (!strncmp(opteq+1, "udp", 3))
494 tcp = 0;
495 else
496 printf(_("Warning: Unrecognized proto= option.\n"));
497 } else if (!strcmp(opt, "namlen")) {
498#if NFS_MOUNT_VERSION >= 2
499 if (nfs_mount_version >= 2)
500 data.namlen = val;
501 else
502#endif
503 printf(_("Warning: Option namlen is not supported.\n"));
504 } else if (!strcmp(opt, "addr"))
505 /* ignore */;
506 else {
507 printf(_("unknown nfs mount parameter: "
508 "%s=%d\n"), opt, val);
509 goto fail;
510 }
511 }
512 else {
513 val = 1;
514 if (!strncmp(opt, "no", 2)) {
515 val = 0;
516 opt += 2;
517 }
518 if (!strcmp(opt, "bg"))
519 bg = val;
520 else if (!strcmp(opt, "fg"))
521 bg = !val;
522 else if (!strcmp(opt, "soft"))
523 soft = val;
524 else if (!strcmp(opt, "hard"))
525 soft = !val;
526 else if (!strcmp(opt, "intr"))
527 intr = val;
528 else if (!strcmp(opt, "posix"))
529 posix = val;
530 else if (!strcmp(opt, "cto"))
531 nocto = !val;
532 else if (!strcmp(opt, "ac"))
533 noac = !val;
534 else if (!strcmp(opt, "tcp"))
535 tcp = val;
536 else if (!strcmp(opt, "udp"))
537 tcp = !val;
538 else if (!strcmp(opt, "lock")) {
539 if (nfs_mount_version >= 3)
540 nolock = !val;
541 else
542 printf(_("Warning: option nolock is not supported.\n"));
543 } else {
544 printf(_("unknown nfs mount option: "
545 "%s%s\n"), val ? "" : "no", opt);
546 goto fail;
547 }
548 }
549 }
550 proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
551
552 data.flags = (soft ? NFS_MOUNT_SOFT : 0)
553 | (intr ? NFS_MOUNT_INTR : 0)
554 | (posix ? NFS_MOUNT_POSIX : 0)
555 | (nocto ? NFS_MOUNT_NOCTO : 0)
556 | (noac ? NFS_MOUNT_NOAC : 0);
557#if NFS_MOUNT_VERSION >= 2
558 if (nfs_mount_version >= 2)
559 data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
560#endif
561#if NFS_MOUNT_VERSION >= 3
562 if (nfs_mount_version >= 3)
563 data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
564#endif
565 if (nfsvers > MAX_NFSPROT) {
566 bb_error_msg("NFSv%d not supported!", nfsvers);
567 return 0;
568 }
569 if (mountvers > MAX_NFSPROT) {
570 bb_error_msg("NFSv%d not supported!", nfsvers);
571 return 0;
572 }
573 if (nfsvers && !mountvers)
574 mountvers = (nfsvers < 3) ? 1 : nfsvers;
575 if (nfsvers && nfsvers < mountvers) {
576 mountvers = nfsvers;
577 }
578
579 /* Adjust options if none specified */
580 if (!data.timeo)
581 data.timeo = tcp ? 70 : 7;
582
583#ifdef NFS_MOUNT_DEBUG
584 printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
585 data.rsize, data.wsize, data.timeo, data.retrans);
586 printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
587 data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
588 printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
589 port, bg, retry, data.flags);
590 printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n",
591 mountprog, mountvers, nfsprog, nfsvers);
592 printf("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n",
593 (data.flags & NFS_MOUNT_SOFT) != 0,
594 (data.flags & NFS_MOUNT_INTR) != 0,
595 (data.flags & NFS_MOUNT_POSIX) != 0,
596 (data.flags & NFS_MOUNT_NOCTO) != 0,
597 (data.flags & NFS_MOUNT_NOAC) != 0);
598#if NFS_MOUNT_VERSION >= 2
599 printf("tcp = %d\n",
600 (data.flags & NFS_MOUNT_TCP) != 0);
601#endif
602#endif
603
604 data.version = nfs_mount_version;
605
606 if (*flags & MS_REMOUNT)
607 goto copy_data_and_return;
608
609 /*
610 * If the previous mount operation on the same host was
611 * backgrounded, and the "bg" for this mount is also set,
612 * give up immediately, to avoid the initial timeout.
613 */
614 if (bg && !running_bg &&
615 prev_bg_host && strcmp(hostname, prev_bg_host) == 0) {
616 if (retry > 0)
617 retval = EX_BG;
618 return retval;
619 }
620
621 /* create mount deamon client */
622 /* See if the nfs host = mount host. */
623 if (mounthost) {
624 if (mounthost[0] >= '0' && mounthost[0] <= '9') {
625 mount_server_addr.sin_family = AF_INET;
626 mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
627 } else {
628 if ((hp = gethostbyname(mounthost)) == NULL) {
629 bb_herror_msg("%s", mounthost);
630 goto fail;
631 } else {
632 if (hp->h_length > sizeof(struct in_addr)) {
633 bb_error_msg("got bad hp->h_length?");
634 hp->h_length = sizeof(struct in_addr);
635 }
636 mount_server_addr.sin_family = AF_INET;
637 memcpy(&mount_server_addr.sin_addr,
638 hp->h_addr, hp->h_length);
639 }
640 }
641 }
642
643 /*
644 * The following loop implements the mount retries. On the first
645 * call, "running_bg" is 0. When the mount times out, and the
646 * "bg" option is set, the exit status EX_BG will be returned.
647 * For a backgrounded mount, there will be a second call by the
648 * child process with "running_bg" set to 1.
649 *
650 * The case where the mount point is not present and the "bg"
651 * option is set, is treated as a timeout. This is done to
652 * support nested mounts.
653 *
654 * The "retry" count specified by the user is the number of
655 * minutes to retry before giving up.
656 *
657 * Only the first error message will be displayed.
658 */
659 retry_timeout.tv_sec = 3;
660 retry_timeout.tv_usec = 0;
661 total_timeout.tv_sec = 20;
662 total_timeout.tv_usec = 0;
663 timeout = time(NULL) + 60 * retry;
664 prevt = 0;
665 t = 30;
666 val = 1;
667 for (;;) {
668 if (bg && stat(node, &statbuf) == -1) {
669 if (running_bg) {
670 sleep(val); /* 1, 2, 4, 8, 16, 30, ... */
671 val *= 2;
672 if (val > 30)
673 val = 30;
674 }
675 } else {
676 /* be careful not to use too many CPU cycles */
677 if (t - prevt < 30)
678 sleep(30);
679
680 pm_mnt = get_mountport(&mount_server_addr,
681 mountprog,
682 mountvers,
683 proto,
684 mountport);
685
686 /* contact the mount daemon via TCP */
687 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
688 msock = RPC_ANYSOCK;
689
690 switch (pm_mnt->pm_prot) {
691 case IPPROTO_UDP:
692 mclient = clntudp_create(&mount_server_addr,
693 pm_mnt->pm_prog,
694 pm_mnt->pm_vers,
695 retry_timeout,
696 &msock);
697 if (mclient)
698 break;
699 mount_server_addr.sin_port = htons(pm_mnt->pm_port);
700 msock = RPC_ANYSOCK;
701 case IPPROTO_TCP:
702 mclient = clnttcp_create(&mount_server_addr,
703 pm_mnt->pm_prog,
704 pm_mnt->pm_vers,
705 &msock, 0, 0);
706 break;
707 default:
708 mclient = 0;
709 }
710 if (mclient) {
711 /* try to mount hostname:pathname */
712 mclient->cl_auth = authunix_create_default();
713
714 /* make pointers in xdr_mountres3 NULL so
715 * that xdr_array allocates memory for us
716 */
717 memset(&status, 0, sizeof(status));
718
719 if (pm_mnt->pm_vers == 3)
720 clnt_stat = clnt_call(mclient, MOUNTPROC3_MNT,
721 (xdrproc_t) xdr_dirpath,
722 (caddr_t) &pathname,
723 (xdrproc_t) xdr_mountres3,
724 (caddr_t) &status,
725 total_timeout);
726 else
727 clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
728 (xdrproc_t) xdr_dirpath,
729 (caddr_t) &pathname,
730 (xdrproc_t) xdr_fhstatus,
731 (caddr_t) &status,
732 total_timeout);
733
734 if (clnt_stat == RPC_SUCCESS)
735 break; /* we're done */
736 if (errno != ECONNREFUSED) {
737 clnt_perror(mclient, "mount");
738 goto fail; /* don't retry */
739 }
740 if (!running_bg && prevt == 0)
741 clnt_perror(mclient, "mount");
742 auth_destroy(mclient->cl_auth);
743 clnt_destroy(mclient);
744 mclient = 0;
745 close(msock);
746 } else {
747 if (!running_bg && prevt == 0)
748 clnt_pcreateerror("mount");
749 }
750 prevt = t;
751 }
752 if (!bg)
753 goto fail;
754 if (!running_bg) {
755 prev_bg_host = bb_xstrdup(hostname);
756 if (retry > 0)
757 retval = EX_BG;
758 goto fail;
759 }
760 t = time(NULL);
761 if (t >= timeout)
762 goto fail;
763 }
764 nfsvers = (pm_mnt->pm_vers < 2) ? 2 : pm_mnt->pm_vers;
765
766 if (nfsvers == 2) {
767 if (status.nfsv2.fhs_status != 0) {
768 bb_error_msg("%s:%s failed, reason given by server: %s",
769 hostname, pathname,
770 nfs_strerror(status.nfsv2.fhs_status));
771 goto fail;
772 }
773 memcpy(data.root.data,
774 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
775 NFS_FHSIZE);
776#if NFS_MOUNT_VERSION >= 4
777 data.root.size = NFS_FHSIZE;
778 memcpy(data.old_root.data,
779 (char *) status.nfsv2.fhstatus_u.fhs_fhandle,
780 NFS_FHSIZE);
781#endif
782 } else {
783#if NFS_MOUNT_VERSION >= 4
784 fhandle3 *my_fhandle;
785 if (status.nfsv3.fhs_status != 0) {
786 bb_error_msg("%s:%s failed, reason given by server: %s",
787 hostname, pathname,
788 nfs_strerror(status.nfsv3.fhs_status));
789 goto fail;
790 }
791 my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
792 memset(data.old_root.data, 0, NFS_FHSIZE);
793 memset(&data.root, 0, sizeof(data.root));
794 data.root.size = my_fhandle->fhandle3_len;
795 memcpy(data.root.data,
796 (char *) my_fhandle->fhandle3_val,
797 my_fhandle->fhandle3_len);
798
799 data.flags |= NFS_MOUNT_VER3;
800#endif
801 }
802
803 /* create nfs socket for kernel */
804
805 if (tcp) {
806 if (nfs_mount_version < 3) {
807 printf(_("NFS over TCP is not supported.\n"));
808 goto fail;
809 }
810 fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
811 } else
812 fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
813 if (fsock < 0) {
814 perror(_("nfs socket"));
815 goto fail;
816 }
817 if (bindresvport(fsock, 0) < 0) {
818 perror(_("nfs bindresvport"));
819 goto fail;
820 }
821 if (port == 0) {
822 server_addr.sin_port = PMAPPORT;
823 port = pmap_getport(&server_addr, nfsprog, nfsvers,
824 tcp ? IPPROTO_TCP : IPPROTO_UDP);
825 if (port == 0)
826 port = NFS_PORT;
827#ifdef NFS_MOUNT_DEBUG
828 else
829 printf(_("used portmapper to find NFS port\n"));
830#endif
831 }
832#ifdef NFS_MOUNT_DEBUG
833 printf(_("using port %d for nfs deamon\n"), port);
834#endif
835 server_addr.sin_port = htons(port);
836 /*
837 * connect() the socket for kernels 1.3.10 and below only,
838 * to avoid problems with multihomed hosts.
839 * --Swen
840 */
841 if (get_kernel_revision() <= 66314
842 && connect(fsock, (struct sockaddr *) &server_addr,
843 sizeof (server_addr)) < 0) {
844 perror(_("nfs connect"));
845 goto fail;
846 }
847
848 /* prepare data structure for kernel */
849
850 data.fd = fsock;
851 memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
852 strncpy(data.hostname, hostname, sizeof(data.hostname));
853
854 /* clean up */
855
856 auth_destroy(mclient->cl_auth);
857 clnt_destroy(mclient);
858 close(msock);
859copy_data_and_return:
860 *mount_opts = xrealloc(*mount_opts, sizeof(data));
861 memcpy(*mount_opts, &data, sizeof(data));
862 return 0;
863
864 /* abort */
865
866fail:
867 if (msock != -1) {
868 if (mclient) {
869 auth_destroy(mclient->cl_auth);
870 clnt_destroy(mclient);
871 }
872 close(msock);
873 }
874 if (fsock != -1)
875 close(fsock);
876 return retval;
877}
878
879/*
880 * We need to translate between nfs status return values and
881 * the local errno values which may not be the same.
882 *
883 * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
884 * "after #include <errno.h> the symbol errno is reserved for any use,
885 * it cannot even be used as a struct tag or field name".
886 */
887
888#ifndef EDQUOT
889#define EDQUOT ENOSPC
890#endif
891
892static struct {
893 enum nfs_stat stat;
894 int errnum;
895} nfs_errtbl[] = {
896 { NFS_OK, 0 },
897 { NFSERR_PERM, EPERM },
898 { NFSERR_NOENT, ENOENT },
899 { NFSERR_IO, EIO },
900 { NFSERR_NXIO, ENXIO },
901 { NFSERR_ACCES, EACCES },
902 { NFSERR_EXIST, EEXIST },
903 { NFSERR_NODEV, ENODEV },
904 { NFSERR_NOTDIR, ENOTDIR },
905 { NFSERR_ISDIR, EISDIR },
906#ifdef NFSERR_INVAL
907 { NFSERR_INVAL, EINVAL }, /* that Sun forgot */
908#endif
909 { NFSERR_FBIG, EFBIG },
910 { NFSERR_NOSPC, ENOSPC },
911 { NFSERR_ROFS, EROFS },
912 { NFSERR_NAMETOOLONG, ENAMETOOLONG },
913 { NFSERR_NOTEMPTY, ENOTEMPTY },
914 { NFSERR_DQUOT, EDQUOT },
915 { NFSERR_STALE, ESTALE },
916#ifdef EWFLUSH
917 { NFSERR_WFLUSH, EWFLUSH },
918#endif
919 /* Throw in some NFSv3 values for even more fun (HP returns these) */
920 { 71, EREMOTE },
921
922 { -1, EIO }
923};
924
925static char *nfs_strerror(int status)
926{
927 int i;
928 static char buf[256];
929
930 for (i = 0; nfs_errtbl[i].stat != -1; i++) {
931 if (nfs_errtbl[i].stat == status)
932 return strerror(nfs_errtbl[i].errnum);
933 }
934 sprintf(buf, _("unknown nfs status return value: %d"), status);
935 return buf;
936}
937
938static bool_t
939xdr_fhandle (XDR *xdrs, fhandle objp)
940{
941 //register int32_t *buf;
942
943 if (!xdr_opaque (xdrs, objp, FHSIZE))
944 return FALSE;
945 return TRUE;
946}
947
948bool_t
949xdr_fhstatus (XDR *xdrs, fhstatus *objp)
950{
951 //register int32_t *buf;
952
953 if (!xdr_u_int (xdrs, &objp->fhs_status))
954 return FALSE;
955 switch (objp->fhs_status) {
956 case 0:
957 if (!xdr_fhandle (xdrs, objp->fhstatus_u.fhs_fhandle))
958 return FALSE;
959 break;
960 default:
961 break;
962 }
963 return TRUE;
964}
965
966bool_t
967xdr_dirpath (XDR *xdrs, dirpath *objp)
968{
969 //register int32_t *buf;
970
971 if (!xdr_string (xdrs, objp, MNTPATHLEN))
972 return FALSE;
973 return TRUE;
974}
975
976bool_t
977xdr_fhandle3 (XDR *xdrs, fhandle3 *objp)
978{
979 //register int32_t *buf;
980
981 if (!xdr_bytes (xdrs, (char **)&objp->fhandle3_val, (unsigned int *) &objp->fhandle3_len, FHSIZE3))
982 return FALSE;
983 return TRUE;
984}
985
986bool_t
987xdr_mountres3_ok (XDR *xdrs, mountres3_ok *objp)
988{
989 //register int32_t *buf;
990
991 if (!xdr_fhandle3 (xdrs, &objp->fhandle))
992 return FALSE;
993 if (!xdr_array (xdrs, (char **)&objp->auth_flavours.auth_flavours_val, (unsigned int *) &objp->auth_flavours.auth_flavours_len, ~0,
994 sizeof (int), (xdrproc_t) xdr_int))
995 return FALSE;
996 return TRUE;
997}
998
999bool_t
1000xdr_mountstat3 (XDR *xdrs, mountstat3 *objp)
1001{
1002 //register int32_t *buf;
1003
1004 if (!xdr_enum (xdrs, (enum_t *) objp))
1005 return FALSE;
1006 return TRUE;
1007}
1008
1009bool_t
1010xdr_mountres3 (XDR *xdrs, mountres3 *objp)
1011{
1012 //register int32_t *buf;
1013
1014 if (!xdr_mountstat3 (xdrs, &objp->fhs_status))
1015 return FALSE;
1016 switch (objp->fhs_status) {
1017 case MNT_OK:
1018 if (!xdr_mountres3_ok (xdrs, &objp->mountres3_u.mountinfo))
1019 return FALSE;
1020 break;
1021 default:
1022 break;
1023 }
1024 return TRUE;
1025}
1026
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * This file was originally generated using rpcgen.
4 * But now we edit it by hand as needed to make it
5 * shut up...
6 */
7
8#ifndef _NFSMOUNT_H_RPCGEN
9#define _NFSMOUNT_H_RPCGEN
10
11#include <rpc/rpc.h>
12
13
14#ifdef __cplusplus
15extern "C" {
16#endif
17
18/*
19 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
20 * unrestricted use provided that this legend is included on all tape
21 * media and as a part of the software program in whole or part. Users
22 * may copy or modify Sun RPC without charge, but are not authorized
23 * to license or distribute it to anyone else except as part of a product or
24 * program developed by the user or with the express written consent of
25 * Sun Microsystems, Inc.
26 *
27 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
28 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
30 *
31 * Sun RPC is provided with no support and without any obligation on the
32 * part of Sun Microsystems, Inc. to assist in its use, correction,
33 * modification or enhancement.
34 *
35 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
36 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
37 * OR ANY PART THEREOF.
38 *
39 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
40 * or profits or other special, indirect and consequential damages, even if
41 * Sun has been advised of the possibility of such damages.
42 *
43 * Sun Microsystems, Inc.
44 * 2550 Garcia Avenue
45 * Mountain View, California 94043
46 */
47/*
48 * Copyright (c) 1985, 1990 by Sun Microsystems, Inc.
49 */
50
51/* from @(#)mount.x 1.3 91/03/11 TIRPC 1.0 */
52#ifndef _rpcsvc_mount_h
53#define _rpcsvc_mount_h
54#include <asm/types.h>
55#define MOUNTPORT 635
56#define MNTPATHLEN 1024
57#define MNTNAMLEN 255
58#define FHSIZE 32
59#define FHSIZE3 64
60
61typedef char fhandle[FHSIZE];
62
63typedef struct {
64 unsigned int fhandle3_len;
65 char *fhandle3_val;
66} fhandle3;
67
68enum mountstat3 {
69 MNT_OK = 0,
70 MNT3ERR_PERM = 1,
71 MNT3ERR_NOENT = 2,
72 MNT3ERR_IO = 5,
73 MNT3ERR_ACCES = 13,
74 MNT3ERR_NOTDIR = 20,
75 MNT3ERR_INVAL = 22,
76 MNT3ERR_NAMETOOLONG = 63,
77 MNT3ERR_NOTSUPP = 10004,
78 MNT3ERR_SERVERFAULT = 10006,
79};
80typedef enum mountstat3 mountstat3;
81
82struct fhstatus {
83 unsigned int fhs_status;
84 union {
85 fhandle fhs_fhandle;
86 } fhstatus_u;
87};
88typedef struct fhstatus fhstatus;
89
90struct mountres3_ok {
91 fhandle3 fhandle;
92 struct {
93 unsigned int auth_flavours_len;
94 int *auth_flavours_val;
95 } auth_flavours;
96};
97typedef struct mountres3_ok mountres3_ok;
98
99struct mountres3 {
100 mountstat3 fhs_status;
101 union {
102 mountres3_ok mountinfo;
103 } mountres3_u;
104};
105typedef struct mountres3 mountres3;
106
107typedef char *dirpath;
108
109typedef char *name;
110
111typedef struct mountbody *mountlist;
112
113struct mountbody {
114 name ml_hostname;
115 dirpath ml_directory;
116 mountlist ml_next;
117};
118typedef struct mountbody mountbody;
119
120typedef struct groupnode *groups;
121
122struct groupnode {
123 name gr_name;
124 groups gr_next;
125};
126typedef struct groupnode groupnode;
127
128typedef struct exportnode *exports;
129
130struct exportnode {
131 dirpath ex_dir;
132 groups ex_groups;
133 exports ex_next;
134};
135typedef struct exportnode exportnode;
136
137struct ppathcnf {
138 int pc_link_max;
139 short pc_max_canon;
140 short pc_max_input;
141 short pc_name_max;
142 short pc_path_max;
143 short pc_pipe_buf;
144 u_char pc_vdisable;
145 char pc_xxx;
146 short pc_mask[2];
147};
148typedef struct ppathcnf ppathcnf;
149#endif /*!_rpcsvc_mount_h*/
150
151#define MOUNTPROG 100005
152#define MOUNTVERS 1
153
154#define MOUNTPROC_NULL 0
155extern void * mountproc_null_1(void *, CLIENT *);
156extern void * mountproc_null_1_svc(void *, struct svc_req *);
157#define MOUNTPROC_MNT 1
158extern fhstatus * mountproc_mnt_1(dirpath *, CLIENT *);
159extern fhstatus * mountproc_mnt_1_svc(dirpath *, struct svc_req *);
160#define MOUNTPROC_DUMP 2
161extern mountlist * mountproc_dump_1(void *, CLIENT *);
162extern mountlist * mountproc_dump_1_svc(void *, struct svc_req *);
163#define MOUNTPROC_UMNT 3
164extern void * mountproc_umnt_1(dirpath *, CLIENT *);
165extern void * mountproc_umnt_1_svc(dirpath *, struct svc_req *);
166#define MOUNTPROC_UMNTALL 4
167extern void * mountproc_umntall_1(void *, CLIENT *);
168extern void * mountproc_umntall_1_svc(void *, struct svc_req *);
169#define MOUNTPROC_EXPORT 5
170extern exports * mountproc_export_1(void *, CLIENT *);
171extern exports * mountproc_export_1_svc(void *, struct svc_req *);
172#define MOUNTPROC_EXPORTALL 6
173extern exports * mountproc_exportall_1(void *, CLIENT *);
174extern exports * mountproc_exportall_1_svc(void *, struct svc_req *);
175extern int mountprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
176
177#define MOUNTVERS_POSIX 2
178
179extern void * mountproc_null_2(void *, CLIENT *);
180extern void * mountproc_null_2_svc(void *, struct svc_req *);
181extern fhstatus * mountproc_mnt_2(dirpath *, CLIENT *);
182extern fhstatus * mountproc_mnt_2_svc(dirpath *, struct svc_req *);
183extern mountlist * mountproc_dump_2(void *, CLIENT *);
184extern mountlist * mountproc_dump_2_svc(void *, struct svc_req *);
185extern void * mountproc_umnt_2(dirpath *, CLIENT *);
186extern void * mountproc_umnt_2_svc(dirpath *, struct svc_req *);
187extern void * mountproc_umntall_2(void *, CLIENT *);
188extern void * mountproc_umntall_2_svc(void *, struct svc_req *);
189extern exports * mountproc_export_2(void *, CLIENT *);
190extern exports * mountproc_export_2_svc(void *, struct svc_req *);
191extern exports * mountproc_exportall_2(void *, CLIENT *);
192extern exports * mountproc_exportall_2_svc(void *, struct svc_req *);
193#define MOUNTPROC_PATHCONF 7
194extern ppathcnf * mountproc_pathconf_2(dirpath *, CLIENT *);
195extern ppathcnf * mountproc_pathconf_2_svc(dirpath *, struct svc_req *);
196extern int mountprog_2_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
197
198#define MOUNT_V3 3
199
200#define MOUNTPROC3_NULL 0
201extern void * mountproc3_null_3(void *, CLIENT *);
202extern void * mountproc3_null_3_svc(void *, struct svc_req *);
203#define MOUNTPROC3_MNT 1
204extern mountres3 * mountproc3_mnt_3(dirpath *, CLIENT *);
205extern mountres3 * mountproc3_mnt_3_svc(dirpath *, struct svc_req *);
206#define MOUNTPROC3_DUMP 2
207extern mountlist * mountproc3_dump_3(void *, CLIENT *);
208extern mountlist * mountproc3_dump_3_svc(void *, struct svc_req *);
209#define MOUNTPROC3_UMNT 3
210extern void * mountproc3_umnt_3(dirpath *, CLIENT *);
211extern void * mountproc3_umnt_3_svc(dirpath *, struct svc_req *);
212#define MOUNTPROC3_UMNTALL 4
213extern void * mountproc3_umntall_3(void *, CLIENT *);
214extern void * mountproc3_umntall_3_svc(void *, struct svc_req *);
215#define MOUNTPROC3_EXPORT 5
216extern exports * mountproc3_export_3(void *, CLIENT *);
217extern exports * mountproc3_export_3_svc(void *, struct svc_req *);
218extern int mountprog_3_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
219
220/* the xdr functions */
221
222static bool_t xdr_fhandle (XDR *, fhandle);
223extern bool_t xdr_fhandle3 (XDR *, fhandle3*);
224extern bool_t xdr_mountstat3 (XDR *, mountstat3*);
225extern bool_t xdr_fhstatus (XDR *, fhstatus*);
226extern bool_t xdr_mountres3_ok (XDR *, mountres3_ok*);
227extern bool_t xdr_mountres3 (XDR *, mountres3*);
228extern bool_t xdr_dirpath (XDR *, dirpath*);
229extern bool_t xdr_name (XDR *, name*);
230extern bool_t xdr_mountlist (XDR *, mountlist*);
231extern bool_t xdr_mountbody (XDR *, mountbody*);
232extern bool_t xdr_groups (XDR *, groups*);
233extern bool_t xdr_groupnode (XDR *, groupnode*);
234extern bool_t xdr_exports (XDR *, exports*);
235extern bool_t xdr_exportnode (XDR *, exportnode*);
236extern bool_t xdr_ppathcnf (XDR *, ppathcnf*);
237
238#ifdef __cplusplus
239}
240#endif
241
242#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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * pivot_root.c - Change root file system. Based on util-linux 2.10s
4 *
5 * busyboxed by Evin Robertson
6 * pivot_root syscall stubbed by Erik Andersen, so it will compile
7 * regardless of the kernel being used.
8 */
9#include <stdlib.h>
10#include <stdio.h>
11#include <errno.h>
12#include "busybox.h"
13
14extern int pivot_root(const char * new_root,const char * put_old);
15
16int pivot_root_main(int argc, char **argv)
17{
18 if (argc != 3)
19 bb_show_usage();
20
21 if (pivot_root(argv[1],argv[2]) < 0)
22 bb_perror_msg_and_die("pivot_root");
23
24 return EXIT_SUCCESS;
25
26}
27
28
29/*
30Local Variables:
31c-file-style: "linux"
32c-basic-offset: 4
33tab-width: 4
34End:
35*/
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * The Rdate command will ask a time server for the RFC 868 time
4 * and optionally set the system time.
5 *
6 * by Sterling Huxley <sterling@europa.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22*/
23
24#include <sys/time.h>
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <netinet/in.h>
28#include <netdb.h>
29#include <stdio.h>
30#include <getopt.h>
31#include <string.h>
32#include <time.h>
33#include <stdlib.h>
34#include <unistd.h>
35#include <signal.h>
36
37#include "busybox.h"
38
39
40static const int RFC_868_BIAS = 2208988800UL;
41
42static void socket_timeout(int sig)
43{
44 bb_error_msg_and_die("timeout connecting to time server");
45}
46
47static time_t askremotedate(const char *host)
48{
49 unsigned long int nett, localt;
50 struct sockaddr_in s_in;
51 int fd;
52
53 bb_lookup_host(&s_in, host);
54 s_in.sin_port = bb_lookup_port("time", "tcp", 37);
55
56 /* Add a timeout for dead or non accessable servers */
57 alarm(10);
58 signal(SIGALRM, socket_timeout);
59
60 fd = xconnect(&s_in);
61
62 if (safe_read(fd, (void *)&nett, 4) != 4) /* read time from server */
63 bb_error_msg_and_die("%s did not send the complete time", host);
64
65 close(fd);
66
67 /* convert from network byte order to local byte order.
68 * RFC 868 time is the number of seconds
69 * since 00:00 (midnight) 1 January 1900 GMT
70 * the RFC 868 time 2,208,988,800 corresponds to 00:00 1 Jan 1970 GMT
71 * Subtract the RFC 868 time to get Linux epoch
72 */
73 localt= ntohl(nett) - RFC_868_BIAS;
74
75 return(localt);
76}
77
78int rdate_main(int argc, char **argv)
79{
80 time_t remote_time;
81 int opt;
82 int setdate = 1;
83 int printdate = 1;
84
85 /* Interpret command line args */
86 while ((opt = getopt(argc, argv, "sp")) > 0) {
87 switch (opt) {
88 case 's':
89 printdate = 0;
90 setdate = 1;
91 break;
92 case 'p':
93 printdate = 1;
94 setdate = 0;
95 break;
96 default:
97 bb_show_usage();
98 }
99 }
100
101 if (optind == argc)
102 bb_show_usage();
103
104 remote_time = askremotedate(argv[optind]);
105
106 if (setdate) {
107 time_t current_time;
108
109 time(&current_time);
110 if (current_time == remote_time)
111 bb_error_msg("Current time matches remote time.");
112 else
113 if (stime(&remote_time) < 0)
114 bb_perror_msg_and_die("Could not set time of day");
115 }
116
117 if (printdate)
118 printf("%s", ctime(&remote_time));
119
120 return EXIT_SUCCESS;
121}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini swapon/swapoff implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <stdio.h>
24#include <mntent.h>
25#include <dirent.h>
26#include <errno.h>
27#include <string.h>
28#include <stdlib.h>
29#include <sys/mount.h>
30#include <sys/swap.h>
31
32#include "busybox.h"
33
34static int whichApp; /* default SWAPON_APP */
35
36static const int SWAPON_APP = 0;
37static const int SWAPOFF_APP = 1;
38
39
40static int swap_enable_disable(const char *device)
41{
42 int status;
43 struct stat st;
44
45 if (stat(device, &st) < 0) {
46 bb_perror_msg_and_die("cannot stat %s", device);
47 }
48
49 /* test for holes */
50 if (S_ISREG(st.st_mode)) {
51 if (st.st_blocks * 512 < st.st_size) {
52 bb_error_msg_and_die("swap file has holes");
53 }
54 }
55
56 if (whichApp == SWAPON_APP)
57 status = swapon(device, 0);
58 else
59 status = swapoff(device);
60
61 if (status != 0) {
62 bb_perror_msg("%s", device);
63 return EXIT_FAILURE;
64 }
65 return EXIT_SUCCESS;
66}
67
68static int do_em_all(void)
69{
70 struct mntent *m;
71 FILE *f = setmntent("/etc/fstab", "r");
72 int err = 0;
73
74 if (f == NULL)
75 bb_perror_msg_and_die("/etc/fstab");
76 while ((m = getmntent(f)) != NULL) {
77 if (strcmp(m->mnt_type, MNTTYPE_SWAP)==0) {
78 if(swap_enable_disable(m->mnt_fsname) == EXIT_FAILURE)
79 err++;
80 }
81 }
82 endmntent(f);
83 return err;
84}
85
86
87extern int swap_on_off_main(int argc, char **argv)
88{
89 if (bb_applet_name[5] == 'f') { /* "swapoff" */
90 whichApp = SWAPOFF_APP;
91 }
92
93 if (argc != 2) {
94 goto usage_and_exit;
95 }
96 argc--;
97 argv++;
98
99 /* Parse any options */
100 while (**argv == '-') {
101 while (*++(*argv))
102 switch (**argv) {
103 case 'a':
104 {
105 struct stat statBuf;
106
107 if (stat("/etc/fstab", &statBuf) < 0)
108 bb_error_msg_and_die("/etc/fstab file missing");
109 }
110 return do_em_all();
111 break;
112 default:
113 goto usage_and_exit;
114 }
115 }
116 return swap_enable_disable(*argv);
117
118 usage_and_exit:
119 bb_show_usage();
120}
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 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini umount implementation for busybox
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23#include <limits.h>
24#include <stdio.h>
25#include <mntent.h>
26#include <errno.h>
27#include <string.h>
28#include <stdlib.h>
29#include "busybox.h"
30
31/* Teach libc5 about realpath -- it includes it but the
32 * prototype is missing... */
33#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 1)
34extern char *realpath(const char *path, char *resolved_path);
35#endif
36
37static const int MNT_FORCE = 1;
38static const int MS_MGC_VAL = 0xc0ed0000; /* Magic number indicatng "new" flags */
39static const int MS_REMOUNT = 32; /* Alter flags of a mounted FS. */
40static const int MS_RDONLY = 1; /* Mount read-only. */
41
42extern int mount (__const char *__special_file, __const char *__dir,
43 __const char *__fstype, unsigned long int __rwflag,
44 __const void *__data);
45extern int umount (__const char *__special_file);
46extern int umount2 (__const char *__special_file, int __flags);
47
48struct _mtab_entry_t {
49 char *device;
50 char *mountpt;
51 struct _mtab_entry_t *next;
52};
53
54static struct _mtab_entry_t *mtab_cache = NULL;
55
56
57
58#if defined CONFIG_FEATURE_MOUNT_FORCE
59static int doForce = FALSE;
60#endif
61#if defined CONFIG_FEATURE_MOUNT_LOOP
62static int freeLoop = TRUE;
63#endif
64#if defined CONFIG_FEATURE_MTAB_SUPPORT
65static int useMtab = TRUE;
66#endif
67static int umountAll = FALSE;
68static int doRemount = FALSE;
69
70
71
72/* These functions are here because the getmntent functions do not appear
73 * to be re-entrant, which leads to all sorts of problems when we try to
74 * use them recursively - randolph
75 *
76 * TODO: Perhaps switch to using Glibc's getmntent_r
77 * -Erik
78 */
79static void mtab_read(void)
80{
81 struct _mtab_entry_t *entry = NULL;
82 struct mntent *e;
83 FILE *fp;
84
85 if (mtab_cache != NULL)
86 return;
87
88 if ((fp = setmntent(bb_path_mtab_file, "r")) == NULL) {
89 bb_error_msg("Cannot open %s", bb_path_mtab_file);
90 return;
91 }
92 while ((e = getmntent(fp))) {
93 entry = xmalloc(sizeof(struct _mtab_entry_t));
94 entry->device = strdup(e->mnt_fsname);
95 entry->mountpt = strdup(e->mnt_dir);
96 entry->next = mtab_cache;
97 mtab_cache = entry;
98 }
99 endmntent(fp);
100}
101
102static char *mtab_getinfo(const char *match, const char which)
103{
104 struct _mtab_entry_t *cur = mtab_cache;
105
106 while (cur) {
107 if (strcmp(cur->mountpt, match) == 0 ||
108 strcmp(cur->device, match) == 0) {
109 if (which == MTAB_GETMOUNTPT) {
110 return cur->mountpt;
111 } else {
112#if !defined CONFIG_FEATURE_MTAB_SUPPORT
113 if (strcmp(cur->device, "rootfs") == 0) {
114 continue;
115 } else if (strcmp(cur->device, "/dev/root") == 0) {
116 /* Adjusts device to be the real root device,
117 * or leaves device alone if it can't find it */
118 cur->device = find_real_root_device_name();
119 }
120#endif
121 return cur->device;
122 }
123 }
124 cur = cur->next;
125 }
126 return NULL;
127}
128
129static char *mtab_next(void **iter)
130{
131 char *mp;
132
133 if (iter == NULL || *iter == NULL)
134 return NULL;
135 mp = ((struct _mtab_entry_t *) (*iter))->mountpt;
136 *iter = (void *) ((struct _mtab_entry_t *) (*iter))->next;
137 return mp;
138}
139
140static char *mtab_first(void **iter)
141{
142 struct _mtab_entry_t *mtab_iter;
143
144 if (!iter)
145 return NULL;
146 mtab_iter = mtab_cache;
147 *iter = (void *) mtab_iter;
148 return mtab_next(iter);
149}
150
151/* Don't bother to clean up, since exit() does that
152 * automagically, so we can save a few bytes */
153#ifdef CONFIG_FEATURE_CLEAN_UP
154static void mtab_free(void)
155{
156 struct _mtab_entry_t *this, *next;
157
158 this = mtab_cache;
159 while (this) {
160 next = this->next;
161 free(this->device);
162 free(this->mountpt);
163 free(this);
164 this = next;
165 }
166}
167#endif
168
169static int do_umount(const char *name)
170{
171 int status;
172 char *blockDevice = mtab_getinfo(name, MTAB_GETDEVICE);
173
174 if (blockDevice && strcmp(blockDevice, name) == 0)
175 name = mtab_getinfo(blockDevice, MTAB_GETMOUNTPT);
176
177 status = umount(name);
178
179#if defined CONFIG_FEATURE_MOUNT_LOOP
180 if (freeLoop && blockDevice != NULL && !strncmp("/dev/loop", blockDevice, 9))
181 /* this was a loop device, delete it */
182 del_loop(blockDevice);
183#endif
184#if defined CONFIG_FEATURE_MOUNT_FORCE
185 if (status != 0 && doForce) {
186 status = umount2(blockDevice, MNT_FORCE);
187 if (status != 0) {
188 bb_error_msg_and_die("forced umount of %s failed!", blockDevice);
189 }
190 }
191#endif
192 if (status != 0 && doRemount && errno == EBUSY) {
193 status = mount(blockDevice, name, NULL,
194 MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
195 if (status == 0) {
196 bb_error_msg("%s busy - remounted read-only", blockDevice);
197 } else {
198 bb_error_msg("Cannot remount %s read-only", blockDevice);
199 }
200 }
201 if (status == 0) {
202#if defined CONFIG_FEATURE_MTAB_SUPPORT
203 if (useMtab)
204 erase_mtab(name);
205#endif
206 return (TRUE);
207 }
208 return (FALSE);
209}
210
211static int umount_all(void)
212{
213 int status = TRUE;
214 char *mountpt;
215 void *iter;
216
217 for (mountpt = mtab_first(&iter); mountpt; mountpt = mtab_next(&iter)) {
218 /* Never umount /proc on a umount -a */
219 if (strstr(mountpt, "proc")!= NULL)
220 continue;
221 if (!do_umount(mountpt)) {
222 /* Don't bother retrying the umount on busy devices */
223 if (errno == EBUSY) {
224 bb_perror_msg("%s", mountpt);
225 status = FALSE;
226 continue;
227 }
228 if (!do_umount(mountpt)) {
229 printf("Couldn't umount %s on %s: %s\n",
230 mountpt, mtab_getinfo(mountpt, MTAB_GETDEVICE),
231 strerror(errno));
232 status = FALSE;
233 }
234 }
235 }
236 return (status);
237}
238
239extern int umount_main(int argc, char **argv)
240{
241 char path[PATH_MAX], result = 0;
242
243 if (argc < 2) {
244 bb_show_usage();
245 }
246#ifdef CONFIG_FEATURE_CLEAN_UP
247 atexit(mtab_free);
248#endif
249
250 /* Parse any options */
251 while (--argc > 0 && **(++argv) == '-') {
252 while (*++(*argv))
253 switch (**argv) {
254 case 'a':
255 umountAll = TRUE;
256 break;
257#if defined CONFIG_FEATURE_MOUNT_LOOP
258 case 'l':
259 freeLoop = FALSE;
260 break;
261#endif
262#ifdef CONFIG_FEATURE_MTAB_SUPPORT
263 case 'n':
264 useMtab = FALSE;
265 break;
266#endif
267#ifdef CONFIG_FEATURE_MOUNT_FORCE
268 case 'f':
269 doForce = TRUE;
270 break;
271#endif
272 case 'r':
273 doRemount = TRUE;
274 break;
275 case 'v':
276 break; /* ignore -v */
277 default:
278 bb_show_usage();
279 }
280 }
281
282 mtab_read();
283 if (umountAll) {
284 if (umount_all())
285 return EXIT_SUCCESS;
286 else
287 return EXIT_FAILURE;
288 }
289
290 do {
291 if (realpath(*argv, path) != NULL)
292 if (do_umount(path))
293 continue;
294 bb_perror_msg("%s", path);
295 result++;
296 } while (--argc > 0 && ++argv);
297 return result;
298}